diff options
author | Alex Clayton <alex.clayton@isode.com> | 2016-02-19 15:10:29 (GMT) |
---|---|---|
committer | Alex Clayton <alex.clayton@isode.com> | 2016-02-29 11:09:07 (GMT) |
commit | 2de569d23468c94fdcf1adc336a580b053423fd7 (patch) | |
tree | f34fff273c65be6006c9de34c6903ad8db553118 /src | |
parent | 3bfe54c141dd3fa20e391312a0a84c75731e2b2a (diff) | |
download | stroke-2de569d23468c94fdcf1adc336a580b053423fd7.zip stroke-2de569d23468c94fdcf1adc336a580b053423fd7.tar.bz2 |
Add sort method for ServiceQuery and add Tests
Add the sortResult static method to the DomainNameServiceQuery class. This
required adding a few equivalances for C++ std library methods to the class. And
add a test for the new method too.
Test-information:
All unit tests pass ok.
Change-Id: Idee0888f7ea140d35a971414fc2fd3cbcdfc337f
Diffstat (limited to 'src')
-rw-r--r-- | src/com/isode/stroke/base/JavaRandomGenerator.java | 31 | ||||
-rw-r--r-- | src/com/isode/stroke/base/RandomGenerator.java | 23 | ||||
-rw-r--r-- | src/com/isode/stroke/network/DomainNameServiceQuery.java | 250 |
3 files changed, 300 insertions, 4 deletions
diff --git a/src/com/isode/stroke/base/JavaRandomGenerator.java b/src/com/isode/stroke/base/JavaRandomGenerator.java new file mode 100644 index 0000000..09d389d --- /dev/null +++ b/src/com/isode/stroke/base/JavaRandomGenerator.java @@ -0,0 +1,31 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.base; + +import java.util.Random; + +/** + * A {@link RandomGenerator} that generates integers with a uniform + * distribution (using the java {@link Random} class). + */ +public final class JavaRandomGenerator implements RandomGenerator { + + /** + * {@link Random} to use to generate the numbers + */ + private final Random rng = new Random(); + + @Override + public int generateRandomInteger(int max) { + // Random.nextInt(bound) is exclusive of bound + return rng.nextInt(max+1); + } + +} diff --git a/src/com/isode/stroke/base/RandomGenerator.java b/src/com/isode/stroke/base/RandomGenerator.java new file mode 100644 index 0000000..970bb98 --- /dev/null +++ b/src/com/isode/stroke/base/RandomGenerator.java @@ -0,0 +1,23 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.base; + +public interface RandomGenerator { + + /** + * Generates a random integer between 0 and 'max', + * 'max' inclusive. + * @param max The maximum possible value + * to generate (inclusive) + * @return A random integer between 0 and 'max' + */ + public int generateRandomInteger(int max); + +} diff --git a/src/com/isode/stroke/network/DomainNameServiceQuery.java b/src/com/isode/stroke/network/DomainNameServiceQuery.java index 05e4d32..6fb73cb 100644 --- a/src/com/isode/stroke/network/DomainNameServiceQuery.java +++ b/src/com/isode/stroke/network/DomainNameServiceQuery.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Isode Limited, London, England. + * Copyright (c) 2010-2016, Isode Limited, London, England. * All rights reserved. */ /* @@ -8,8 +8,15 @@ */ package com.isode.stroke.network; +import com.ibm.icu.util.BytesTrie.Iterator; +import com.isode.stroke.base.RandomGenerator; import com.isode.stroke.signals.Signal1; + +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; public abstract class DomainNameServiceQuery { @@ -34,13 +41,248 @@ public abstract class DomainNameServiceQuery { public final int weight; }; - public class ResultPriorityComparator { + /** + * Compares {@link DomainNameServiceQuery.Result} based on their + * priority only. + * + */ + public final static class ResultPriorityComparator implements Comparator<Result> { - public boolean compare(DomainNameServiceQuery.Result a, DomainNameServiceQuery.Result b) { - return a.priority < b.priority; + public int compare(DomainNameServiceQuery.Result a, DomainNameServiceQuery.Result b) { + if (a.priority < b.priority) { + return -1; + } + if (a.priority > b.priority) { + return 1; + } + return 0; } }; public abstract void run(); public final Signal1<Collection<Result>> onResult = new Signal1<Collection<Result>>(); + + public static void sortResults(List<Result> queries,RandomGenerator generator) { + ResultPriorityComparator comparator = new ResultPriorityComparator(); + Collections.sort(queries,comparator); + int i = 0; + while (i < queries.size()) { + Result current = queries.get(i); + int nextI = upperBound(queries, current, comparator); + if ((nextI - i) > 1) { + List<Result> subList = queries.subList(i, nextI); + List<Integer> weights = transform(subList, new UnaryOperation<Result, Integer>() { + + @Override + public Integer perform(Result item) { + // easy hack to account for '0' weights getting at least some weight + return Integer.valueOf(item.weight+1); + } + + }); + for (int j = 0; j < (weights.size()-1); j++) { + List<Integer> cumulativeWeights = + partialSum(weights.subList(j, weights.size()), Integer.valueOf(0), + new BinaryOperation<Integer, Integer, Integer>() { + + @Override + public Integer perform(Integer int1, + Integer int2) { + int results = int1.intValue() + int2.intValue(); + return Integer.valueOf(results); + } + + }); + int lastWeight = cumulativeWeights.get(cumulativeWeights.size() - 1).intValue(); + int randomNumber = generator.generateRandomInteger(lastWeight); + int selectedIndex = lowerBound(cumulativeWeights, Integer.valueOf(randomNumber)); + Collections.swap(subList, j, j+selectedIndex); + Collections.swap(weights, j, j+selectedIndex); + } + + } + i = nextI; + } + } + + /** + * Behaves similarly to the c++ function std::upper_bound. + * Given a range that is sorted with respect to the given + * {@link Comparator}, returns the index of the first element in the range greater + * (under the {@link Comparator}) then the given value. + * @param <T> The type of the value. + * @param range A sorted (with respect to {@code comp}) list of + * T. Should not be {@code null}. + * @param value A value to search the list for. Should not be {@code null}. + * @param comp The comparator to use for the search, the {@code range} should be + * sorted with respect to this. Should not be {@code null}. + * @return The index of the first element in {@code range} that is greater (under + * {@code comp}) then {@code value}, or {@code range.size()} if there is no such value. + */ + public static <T> int upperBound(List<? extends T> range,T value,Comparator<? super T> comp) { + for (int i = 0; i < range.size(); i++) { + T current = range.get(i); + if (comp.compare(current, value) > 0) { + return i; + } + } + return range.size(); + } + + /** + * Behaves similarly to the c++ function std::upper_bound. + * Given a range that is sorted with respect to the natural order of its elements + * , returns the index of the first element in the range greater then the given value. + * @param <T> The type of the value. + * @param range A sorted list of + * T. Should not be {@code null}. + * @param value A value to search the list for. Should not be {@code null}. + * @return The index of the first element in {@code range} that is greate then {@code value}, + * or {@code range.size()} if there is no such value. + */ + public static <T extends Comparable<T>> int upperBound(List<? extends T> range,T value) { + Comparator<T> comparator = new Comparator<T>() { + + @Override + public int compare(T a, T b) { + return a.compareTo(b); + } + }; + return upperBound(range, value, comparator); + } + + /** + * Behaves similarly to the c++ function std::lower_bound. + * Given a range that is sorted with respect to the given + * {@link Comparator}, returns the index of the first element in the range that is not + * less then (i.e it is greater then or equal to) a given value. + * @param <T> The type of the value. + * @param range A sorted (with respect to {@code comp}) list of + * T. Should not be {@code null}. + * @param value A value to search the list for. Should not be {@code null}. + * @param comp The comparator to use for the search, the {@code range} should be + * sorted with respect to this. Should not be {@code null}. + * @return The index of the first element in {@code range} that is not less then + * (i.e it is greater then or equal to) then {@code value}, or {@code range.size()} + * if there is no such value. + */ + public static <T> int lowerBound(List<? extends T> range,T value,Comparator<? super T> comp) { + for (int i = 0; i < range.size(); i++) { + T current = range.get(i); + if (comp.compare(current, value) < 0) { + continue; + } + return i; + } + return range.size(); + } + + /** + * Behaves similarly to the c++ function std::lower_bound. + * Given a range that is sorted with respect to the natural order of its elements + * , returns the index of the first element in the range that is not less then + * (i.e it is greater then or equal to) a given value. + * @param <T> The type of the value. + * @param range A sorted list of + * T. Should not be {@code null}. + * @param value A value to search the list for. Should not be {@code null}. + * @return The index of the first element in {@code range} that is not less then + * (i.e it is greater then or equal to) {@code value}, + * or {@code range.size()} if there is no such value. + */ + public static <T extends Comparable<T>> int lowerBound(List<? extends T> range,T value) { + Comparator<T> comparator = new Comparator<T>() { + + @Override + public int compare(T a, T b) { + return a.compareTo(b); + } + }; + return lowerBound(range, value, comparator); + } + + /** + * Operation that produces an item of type T from + * an item of type S + * @param <S> Type of input to the operation + * @param <T> Type of output of the operation + */ + public interface UnaryOperation<S,T> { + + /** + * Produces an item of type T from an item of type S + * @param item An item of type S. + * @return An item of type T + */ + public T perform(S item); + + } + + /** + * Operation that produces an item of type T from + * an items of type R and an item of type S. + * + * @param <R> Type of the first input to the operation. + * @param <S> Type of the second input to the operation. + * @param <T> Type of the output of the operation. + */ + public interface BinaryOperation<R,S,T> { + + /** + * Produces an item of type from an Item of type R + * and an Item of type S + * @param input1 An item of type R + * @param input2 An item of type S + * @return An item of type T + */ + public T perform(R input1,S input2); + + } + + /** + * Behaves similar to the C++ function std::transform + * Transform a list of items of type T into a list of item of + * type S using the given {@link UnaryOperation} + * @param <S> Type of the input objects + * @param <T> Type of the output objects. + * @param input The input objects to apply the {@link UnaryOperation} + * to. + * @param operation The {@link UnaryOperation} to apply to the input + * to get the output. + * @return The results of applying the {@link UnaryOperation} to + * the input in the order they are returned by the input's {@link Iterator}. + */ + public static <S,T> List<T> transform(Collection<? extends S> input, + UnaryOperation<? super S,? extends T> operation) { + List<T> output = new ArrayList<T>(input.size()); + for (S item : input) { + T product = operation.perform(item); + output.add(product); + } + return output; + } + + /** + * Produces the partial sum of the elements in a {@link Collections} using the + * given {@link BinaryOperation}. + * @param <T> The type of objects to calculate the sum for + * @param input The items to produce the partial sums for. Should not be {@code null}. + * @param initialValue The initial value for the sum to add the first element. + * @param sumOp The {@link BinaryOperation} to produce the sum must take two items + * of type T to produce another item of type T. Should not be {@code null}. + * @return A {@link List} of T consisting of the partial sums of the input. + * Will not be {@code null} + */ + public static <T> List<T> partialSum(Collection<? extends T> input, + T initialValue,BinaryOperation<? super T,? super T,? extends T> sumOp) { + List<T> partialSums = new ArrayList<T>(input.size()); + T previousSum = initialValue; + for (T currentInput : input) { + T newSum = sumOp.perform(previousSum, currentInput); + partialSums.add(newSum); + previousSum = newSum; + } + return partialSums; + } + } |