diff --git a/src/main/java/gov/usgs/earthquake/nshmp/calc/Disaggregation.java b/src/main/java/gov/usgs/earthquake/nshmp/calc/Disaggregation.java index 7fba75e14e5ab2952f96b1a43f99b34b2e7856f3..72999c4ae6264dcc3fda2fa192be22dfa7e6728c 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/calc/Disaggregation.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/calc/Disaggregation.java @@ -226,7 +226,7 @@ public final class Disaggregation { /* Hazard curves are already in log-x space. */ static final Interpolator IML_INTERPOLATER = Interpolator.builder() .logy() - .decreasingX() + .decreasingY() .build(); /* Hazard curves are already in log-x space. */ diff --git a/src/main/java/gov/usgs/earthquake/nshmp/data/ArrayXySequence.java b/src/main/java/gov/usgs/earthquake/nshmp/data/ArrayXySequence.java index 82f2301e4b6bedc79a792c30364e702b1c87b1d6..5f5a1b06e88784800e3a3fcc7171bbe6f2f45d7b 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/data/ArrayXySequence.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/data/ArrayXySequence.java @@ -27,8 +27,6 @@ class ArrayXySequence implements XySequence { ArrayXySequence(XySequence sequence, boolean clear) { /* - * TODO is clear ever used?? - * * This constructor provides the option to 'clear' (or zero-out) the * y-values when copying. In practice, it is only ever used when creating * mutable instances. diff --git a/src/main/java/gov/usgs/earthquake/nshmp/data/DoubleData.java b/src/main/java/gov/usgs/earthquake/nshmp/data/DoubleData.java index b218bbb02985b67a4dc44c057ce76d315ff7b5aa..8ae5ee35b6d34c0b813855a6a8258630a35ca894 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/data/DoubleData.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/data/DoubleData.java @@ -1,20 +1,12 @@ package gov.usgs.earthquake.nshmp.data; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static gov.usgs.earthquake.nshmp.Text.NEWLINE; import java.math.BigDecimal; import java.util.Arrays; import java.util.Collection; import java.util.List; -import java.util.Map; -import java.util.function.DoubleUnaryOperator; -import com.google.common.base.Strings; -import com.google.common.collect.ContiguousSet; -import com.google.common.collect.DiscreteDomain; -import com.google.common.collect.Lists; import com.google.common.collect.Range; import com.google.common.math.DoubleMath; import com.google.common.primitives.Doubles; @@ -222,27 +214,6 @@ public final class DoubleData { return data1; } - /** - * Adds the entries of {@code map2} to {@code map1} in place. If a key from - * {@code map2} exists in {@code map1}, then the value for that key is added - * to the corresponding value in {@code map1}. If no such key exists in map 1, - * then the key and value from map2 are transferred as is. Note that this - * method is <i>not</i> synchronized. - * - * @param map1 - * @param map2 - * @return a reference to {@code map1} - */ - @Deprecated - public static <T> Map<T, Double> add(Map<T, Double> map1, Map<T, Double> map2) { - for (T key : map2.keySet()) { - Double v2 = map2.get(key); - Double v1 = (map1.containsKey(key)) ? map1.get(key) + v2 : v2; - map1.put(key, v1); - } - return map1; - } - /** * Subtract the values of {@code data2} from {@code data1} in place. To * subtract a term from every value of a dataset, use @@ -424,7 +395,6 @@ public final class DoubleData { * @return a reference to the supplied {@code data} * @see Math#log(double) */ - @Deprecated public static List<Double> ln(List<Double> data) { for (int i = 0; i < data.size(); i++) { data.set(i, Math.log(data.get(i))); @@ -474,6 +444,13 @@ public final class DoubleData { return data; } + /** + * Sets the elements {@code data} to {@code 1 - value} in place. Assumes this + * is a probability distribution limited to the domain [0..1]. + * + * @param data to operate on + * @return a reference to the supplied {@code data} + */ public static double[] complement(double... data) { for (int i = 0; i < data.length; i++) { data[i] = 1 - data[i]; @@ -599,104 +576,9 @@ public final class DoubleData { return data; } - /** - * Transform {@code data} by a {@code DoubleUnaryOperator} in place. - * - * @param function to apply to {@code data} - * @param data to operate on - * @return a reference to the supplied {@code data} - */ - public static double[] transform(DoubleUnaryOperator function, double... data) { - return transform(Range.closedOpen(0, data.length), function, data); - } - - /** - * Transform {@code data} by a {@code DoubleUnaryOperator} in place. - * - * @param function to apply - * @param data to operate on - * @return a reference to the supplied {@code data} - */ - @Deprecated - public static List<Double> transform(DoubleUnaryOperator function, List<Double> data) { - checkNotNull(function); - for (int i = 0; i < data.size(); i++) { - data.set(i, function.applyAsDouble(data.get(i))); - } - return data; - } - - // TODO why does this exist - // seems heavy; tansformRange?? - // - /** - * Transform {@code data} in a given {@code Range} by a - * {@code DoubleUnaryOperator} in place. - * - * @param range to apply the {@code function} - * @param function to apply to the {@code data} - * @param data to operate on - * @return a reference to the supplied {@code data} - */ - @Deprecated - public static double[] transform( - Range<Integer> range, - DoubleUnaryOperator function, - double... data) { - checkNotNull(function); - checkArgument(!range.isEmpty()); - - ContiguousSet<Integer> rangeSet = ContiguousSet - .create(range, DiscreteDomain.integers()); - - checkArgument(rangeSet.first() >= 0, "Invalid range: %s", range.toString()); - - for (int index : rangeSet) { - if (index >= data.length) break; - data[index] = function.applyAsDouble(data[index]); - } - - return data; - } - - /** - * Transform {@code data} in a given range, [{@code minIndex}, - * {@code maxIndex}), by a {@code DoubleUnaryOperator} in place. - * - * @param lower inclusive index - * @param upper exclusive index - * @param function to apply to the {@code data} - * @param data to operate on - * @return a reference to the supplied {@code data} - */ - @Deprecated - public static double[] transform( - int lower, - int upper, - DoubleUnaryOperator function, double... data) { - return transform(Range.closedOpen(lower, upper), function, data); - } - private static final String NORM_DATA_ERROR = "Normalize: Data outside range [0..+Inf)"; private static final String NORM_SUM_ERROR = "Normalize: Sum outside range (0..+Inf)"; - /** - * Normalize the elements of {@code data} in place such that they sum to 1. - * - * @param data to normalize - * @return a reference to the supplied {@code data} - * @throws IllegalArgumentException if {@code data} is empty or no varargs are - * supplied, contains values outside the range {@code [0..+Inf)}, or - * sums to a value outside the range {@code (0..+Inf)} - */ - public static List<Double> normalize(List<Double> data) { - checkArgument(arePositiveAndRealOrZero(data), NORM_DATA_ERROR); - double sum = sum(data); - checkArgument(isPositiveAndReal(sum), NORM_SUM_ERROR); - double scale = 1.0 / sum; - return multiply(scale, data); - } - /** * Normalize the elements of {@code data} in place such that they sum to 1. * @@ -818,47 +700,6 @@ public final class DoubleData { return value > 0.0 && value < Double.POSITIVE_INFINITY; } - /** - * Determine whether the elements of {@code data} are all positive, real - * numbers. - * - * @param data to validate - * @return {@code true} if all data are in the range {@code (0..+Inf)}; {@code - * false} otherwise - * @throws IllegalArgumentException if {@code data} is empty or no varargs are - * supplied - */ - @Deprecated - static boolean arePositiveAndReal(double... data) { - checkSize(1, data); - for (double d : data) { - if (!isPositiveAndReal(d)) { - return false; - } - } - return true; - } - - /** - * Determine whether the elements of {@code data} are all positive, real - * numbers. - * - * @param data to validate - * @return {@code true} if all data are in the range {@code (0..+Inf)}; {@code - * false} otherwise - * @throws IllegalArgumentException if {@code data} is empty - */ - @Deprecated - static boolean arePositiveAndReal(Collection<Double> data) { - checkSize(1, data); - for (double d : data) { - if (!isPositiveAndReal(d)) { - return false; - } - } - return true; - } - /** * Determine whether {@code value} is a positive, real number in the range * {@code [0..+Inf)}. @@ -889,25 +730,6 @@ public final class DoubleData { return true; } - /** - * Determine whether the elements of {@code data} are all positive, real - * numbers, or 0. - * - * @param data to validate - * @return {@code true} if all data are in the range {@code [0..+Inf)}; {@code - * false} otherwise - * @throws IllegalArgumentException if {@code data} is empty - */ - static boolean arePositiveAndRealOrZero(Collection<Double> data) { - checkSize(1, data); - for (double d : data) { - if (!isPositiveAndRealOrZero(d)) { - return false; - } - } - return true; - } - /** * Determine whether the elements of {@code data} are all equal to 0. * @@ -926,24 +748,6 @@ public final class DoubleData { return true; } - /** - * Determine whether all the elements of {@code data} are equal to 0. - * - * @param data to validate - * @return {@code true} if all values = 0; {@code false} otherwise - * @throws IllegalArgumentException if {@code data} is empty - */ - @Deprecated - static boolean areZeroValued(Collection<Double> data) { - checkSize(1, data); - for (double d : data) { - if (d != 0.0) { - return false; - } - } - return true; - } - /** * Determine whether the elements of {@code data} increase or decrease * monotonically.The {@code strict} flag indicates if identical adjacent @@ -990,7 +794,6 @@ public final class DoubleData { * @param delta discretization delta * @return the supplied {@code delta} for use inline */ - @Deprecated public static double checkDelta(double min, double max, double delta) { checkFinite("Sequence minimum", min); checkFinite("Sequence maximum", max); @@ -1001,6 +804,15 @@ public final class DoubleData { return delta; } + /** + * Ensure {@code value} is a positive, real number in the range + * {@code (0..+Inf)}. + * + * @param value to check + * @return the validated value + * @throws IllegalArgumentException if {@code value} is outside the range + * {@code (0..+Inf)} + */ public static double checkIsPositiveAndReal(double value) { checkArgument(isPositiveAndReal(value)); return value; @@ -1021,43 +833,6 @@ public final class DoubleData { return value; } - /** - * Ensure the elements of {@code data} are finite. - * - * @param data to validate - * @return a reference to the supplied {@code data} - * @throws IllegalArgumentException if {@code data} is empty, or any elements - * of {@code data} are outside the range {@code (-Inf..+Inf)} - * @see Doubles#isFinite(double) - */ - @Deprecated - public static Collection<Double> checkFinite(Collection<Double> data) { - checkSize(1, data); - for (double d : data) { - checkFinite(DATA_ERROR, d); - } - return data; - } - - /** - * Ensure the elements of {@code data} are finite. - * - * @param data to validate - * @return a reference to the supplied {@code data} - * @throws IllegalArgumentException if {@code data} is empty or no varargs are - * supplied, or any elements of {@code data} are outside the range - * {@code (-Inf..+Inf)} - * @see Doubles#isFinite(double) - */ - @Deprecated - public static double[] checkFinite(double... data) { - checkSize(1, data); - for (double d : data) { - checkFinite(DATA_ERROR, d); - } - return data; - } - /** * Ensure {@code value} falls within the specified {@link Range}. * @@ -1073,23 +848,6 @@ public final class DoubleData { return value; } - /** - * Ensure the elements of {@code data} fall within the specified {@link Range} - * . - * - * @param range of allowable values - * @param data to validate - * @return a reference to the supplied {@code data} - * @see #checkInRange(Range, String, double) for exception notes - */ - @Deprecated - public static Collection<Double> checkInRange(Range<Double> range, Collection<Double> data) { - for (double d : data) { - checkInRange(range, DATA_ERROR, d); - } - return data; - } - /** * Ensure the elements of {@code data} fall within the specified {@link Range} * . @@ -1106,16 +864,6 @@ public final class DoubleData { return data; } - /** - * Ensure {@code data.size() ≥ min}. - * - * @return a reference to the supplied {@code data} - */ - public static Collection<Double> checkSize(int min, Collection<Double> data) { - checkSize(min, data.size()); - return data; - } - /** * Ensure {@code data.length ≥ min}. * @@ -1258,7 +1006,6 @@ public final class DoubleData { * @param weights to validate * @return a reference to the supplied {@code weights} */ - @Deprecated public static double[] checkWeights(double[] weights) { return checkWeights(weights, true); } @@ -1295,260 +1042,4 @@ public final class DoubleData { return out; } - /** - * Format a two-dimensional data array for printing. - * - * @param data to format - * @return a string representation of the supplied {@code data} - */ - @Deprecated - public static String toString(double[][] data) { - return toString(data, 1); - } - - /* To support indenting of multidimensional arrays */ - @Deprecated - private static String toString(double[][] data, int indent) { - StringBuilder sb = new StringBuilder("["); - for (int i = 0; i < data.length; i++) { - if (i > 0) { - sb.append(",").append(NEWLINE); - sb.append(Strings.repeat(" ", indent)); - } - sb.append(Arrays.toString(data[i])); - } - sb.append("]"); - return sb.toString(); - } - - /** - * Format a three-dimensional data array for printing - * - * @param data to format - * @return a string representation of the supplied {@code data} - */ - @Deprecated - public static String toString(double[][][] data) { - return toString(data, 1); - } - - /* To support indenting of multidimensional arrays */ - @Deprecated - private static String toString(double[][][] data, int indent) { - StringBuilder sb = new StringBuilder("["); - for (int i = 0; i < data.length; i++) { - if (i > 0) { - sb.append(",").append(NEWLINE); - sb.append(Strings.repeat(" ", indent)); - } - sb.append(toString(data[i], indent + 1)); - } - sb.append("]"); - return sb.toString(); - } - - /* - * - * - * - * - * - * - * - * - * - * - * Everything below needs review - */ - - /** - * Creates a sequence of evenly spaced values starting at {@code min} and - * ending at {@code max}, cleaned of any double precision math offsets. If - * {@code (max - min) / step} is not equivalent to an integer, within - * {@code scale} tolerance, the last step in the sequence will be - * {@code <step}. - * - * @param min sequence value - * @param max sequence value - * @param step sequence spacing - * @param ascending if {@code true}, descending if {@code false} - * @param scale the number of decimal places to preserve - * @return a monotonically increasing or decreasing sequence of values - * @throws IllegalArgumentException if {@code min >= max}, {@code step <= 0} , - * or any arguments are {@code Double.NaN}, - * {@code Double.POSITIVE_INFINITY}, or - * {@code Double.NEGATIVE_INFINITY} - */ - @Deprecated - public static double[] buildCleanSequence( - double min, - double max, - double step, - boolean ascending, - int scale) { - double[] seq = buildSequence(min, max, step, ascending); - return round(scale, seq); - } - - /** - * Creates a sequence of evenly spaced values starting at {@code min} and - * ending at {@code max}. If {@code (max - min) / step} is not integer valued, - * the last step in the sequence will be {@code < step}. - * - * @param min sequence value - * @param max sequence value - * @param step sequence spacing - * @param ascending if {@code true}, descending if {@code false} - * @return a monotonically increasing or decreasing sequence of values - * @throws IllegalArgumentException if {@code min >= max}, {@code step <= 0} , - * or any arguments are {@code Double.NaN}, - * {@code Double.POSITIVE_INFINITY}, or - * {@code Double.NEGATIVE_INFINITY} - */ - @Deprecated - public static double[] buildSequence(double min, double max, double step, boolean ascending) { - // if passed in arguments are NaN, +Inf, or -Inf, and step <= 0, - // then capacity [c] will end up 0 because (int) NaN = 0, or outside the - // range 1:10000 - checkArgument(min <= max, "min-max reversed"); - if (min == max) { - return new double[] { min }; - } - int c = (int) ((max - min) / step); - checkArgument(c > 0 && c < MAX_SEQ_LEN, "sequence size"); - if (ascending) { - return buildSequence(min, max, step, c + 2); - } - double[] descSeq = buildSequence(-max, -min, step, c + 2); - return flip(descSeq); - - // TODO - // double[] mags = DataUtils.buildSequence(5.05, 7.85, 0.1, true); - // System.out.println(Arrays.toString(mags)); - // produces crummy values 2.449999999999999999 etc... - } - - private static final int MAX_SEQ_LEN = 10001; - private static final double SEQ_MAX_VAL_TOL = 0.000001; // 1e-6 - - @Deprecated - private static double[] buildSequence(double min, double max, double step, int capacity) { - List<Double> seq = Lists.newArrayListWithCapacity(capacity); - for (double val = min; val < max; val += step) { - seq.add(val); - } - // do not add max if current max is equal to max wihthin tolerance - if (!DoubleMath.fuzzyEquals(seq.get(seq.size() - 1), max, SEQ_MAX_VAL_TOL)) { - seq.add(max); - } - return Doubles.toArray(seq); - } - - // TODO clean - public static void main(String[] args) { - double[] test = buildSequence(5.05, 7.83, 0.1, true); - System.out.println(Arrays.toString(test)); - test = buildCleanSequence(5.05, 7.85, 0.1, true, 4); - System.out.println(Arrays.toString(test)); - // test = buildSequence(6.55625, 7.34385, 0.1125); - // System.out.println(Arrays.toString(test)); - // test = buildCenteredSequence(6.5, 7.4, 0.1125); - // System.out.println(Arrays.toString(test)); - } - - /** - * Combine the supplied {@code sequences}. The y-values returned are the set - * of all supplied y-values. The x-values returned are the sum of the supplied - * x-values. When summing, x-values for points outside the original domain of - * a sequence are set to 0, while those inside the original domain are sampled - * via linear interpolation. - * - * - * @param sequences to combine - * @return a combined sequence - */ - // @Deprecated TODO review wrt MFD combining - // public static XySequence combine(Iterable<XySequence> sequences) { - // - // // TODO I think we want to have interpolating and non-interpolating - // // flavors. Interpolating for visual presentation, non-interpolating - // // for re-use as MFD - // - // // create master x-value sequence - // Builder<Double> builder = ImmutableSortedSet.naturalOrder(); - // for (XySequence sequence : sequences) { - // builder.addAll(sequence.xValues()); - // } - // double[] xMaster = Doubles.toArray(builder.build()); - // - // // resample and combine sequences - // XySequence combined = XySequence.create(xMaster, null); - // for (XySequence sequence : sequences) { - // // TODO need to disable extrapolation in Interpolation - // if (true) { - // throw new UnsupportedOperationException(); - // } - // XySequence resampled = XySequence.resampleTo(sequence, xMaster); - // combined.add(resampled); - // } - // - // return combined; - // } - - // TODO clean - // /** - // * Validates the domain of a {@code double} data set. Method verifies - // * that data values all fall between {@code min} and {@code max} range - // * (inclusive). Empty arrays are ignored. If {@code min} is - // * {@code Double.NaN}, no lower limit is imposed; the same holds true - // * for {@code max}. {@code Double.NaN} values in {@code array} - // * will validate. - // * - // * @param min minimum range value - // * @param max maximum range value - // * @param array to validate - // * @throws IllegalArgumentException if {@code min > max} - // * @throws IllegalArgumentException if any {@code array} value is out of - // * range - // * @deprecated Ranges should be used instead with NaNs throwing an - // exception - // */ - // @Deprecated - // public final static void validate(double min, double max, double... - // array) { - // checkNotNull(array, "array"); - // for (int i = 0; i < array.length; i++) { - // validate(min, max, array[i]); - // } - // } - // - // /** - // * Verifies that a {@code double} data value falls within a specified - // * minimum and maximum range (inclusive). If {@code min} is - // * {@code Double.NaN}, no lower limit is imposed; the same holds true - // * for {@code max}. A value of {@code Double.NaN} will always - // * validate. - // * - // * @param min minimum range value - // * @param max minimum range value - // * @param value to check - // * @throws IllegalArgumentException if {@code min > max} - // * @throws IllegalArgumentException if value is out of range - // * @deprecated Ranges should be used instead with NaNs throwing an - // exception - // */ - // @Deprecated - // public final static void validate(double min, double max, double value) { - // boolean valNaN = isNaN(value); - // boolean minNaN = isNaN(min); - // boolean maxNaN = isNaN(max); - // boolean both = minNaN && maxNaN; - // boolean neither = !(minNaN || maxNaN); - // if (neither) checkArgument(min <= max, "min-max reversed"); - // boolean expression = valNaN || both ? true : minNaN - // ? value <= max : maxNaN ? value >= min : value >= min && - // value <= max; - // checkArgument(expression, "value"); - // } - } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/data/Indexing.java b/src/main/java/gov/usgs/earthquake/nshmp/data/Indexing.java index 686b36dfd6eb438b39efc3399a1b9ca523c9731f..f48f451bb5b8620e858a64b813b15aa11f42fcf4 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/data/Indexing.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/data/Indexing.java @@ -1,12 +1,9 @@ package gov.usgs.earthquake.nshmp.data; import static com.google.common.base.Preconditions.checkArgument; -import static gov.usgs.earthquake.nshmp.data.DoubleData.checkSize; import static java.util.Objects.checkIndex; -import java.util.Arrays; import java.util.BitSet; -import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -88,7 +85,7 @@ public final class Indexing { * @throws IllegalArgumentException if {@code data} is empty */ public static List<Integer> sortedIndices(List<Double> data, boolean ascending) { - checkSize(1, data); + checkArgument(data.size() > 1); List<Integer> indices = Ints.asList(indices(data.size())); Collections.sort(indices, new IndexComparator(data, ascending)); return indices; @@ -135,33 +132,13 @@ public final class Indexing { * Return a {@code BitSet} with {@code capacity} and with all bits at * {@code indices} 'set'. * - * @param indices to operate on * @param capacity of returned {@code BitSet} - */ - public static BitSet indicesToBits(List<Integer> indices, int capacity) { - checkArgument(capacity > 0, "BitSet capacity [%s] must be > 0", capacity); - checkElementIndices(indices, capacity); - BitSet bits = new BitSet(capacity); - for (int index : indices) { - bits.set(index); - } - return bits; - } - - private static void checkElementIndices(Collection<Integer> indices, int size) { - for (int index : indices) { - checkIndex(index, size); - } - } - - /** - * Return a {@code BitSet} with {@code capacity} and with all bits at - * {@code indices} 'set'. - * * @param indices to operate on - * @param capacity of returned {@code BitSet} + * @throws IllegalArgumentException if {@code capacity < 1} + * @throws IndexOutOfBoundsException if any {@code indices} are outside the + * range specified by {@code capacity} */ - public static BitSet indicesToBits(int[] indices, int capacity) { + public static BitSet indicesToBits(int capacity, int[] indices) { checkArgument(capacity > 0, "BitSet capacity [%s] must be > 0", capacity); checkElementIndices(indices, capacity); BitSet bits = new BitSet(capacity); @@ -335,23 +312,6 @@ public final class Indexing { return new int[] { index0, index1, index2 }; } - /* - * NOTE this was lifted from the interpolate class and could parhaps benefit - * from checking the size of 'data' and then doing linear instead of binary - * search. - * - * This is a clamping index search algorithm; it will always return an index - * in the range [0, data.length - 2]; it is always used to get some value at - * index and index+1 - */ - public static int dataIndex(double[] data, double value) { - int i = Arrays.binarySearch(data, value); - // adjust index for low value (-1) and in-sequence insertion pt - i = (i == -1) ? 0 : (i < 0) ? -i - 2 : i; - // adjust hi index to next to last index - return (i >= data.length - 1) ? --i : i; - } - /** * Ensure {@code value} falls within the specified {@link Range}. * @@ -359,7 +319,7 @@ public final class Indexing { * @param label for value if check fails * @param value to validate * @return the supplied {@code value} - * @throws IllegalArgumentException + * @throws IllegalArgumentException if value is out of specified range */ public static int checkInRange(Range<Integer> range, String label, int value) { checkArgument(range.contains(value), "%s [%s] not in range %s", label, value, range); diff --git a/src/main/java/gov/usgs/earthquake/nshmp/data/Interpolator.java b/src/main/java/gov/usgs/earthquake/nshmp/data/Interpolator.java index c4fd0b06aab74c29011e7230c0469d7debe288b9..c60637400d0b5a3ff4370d87a42f4919cf5f8ef3 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/data/Interpolator.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/data/Interpolator.java @@ -9,19 +9,21 @@ import java.util.List; /** * Utility class to perform linear and log interpolations. The methods of this - * class are designed to be fast and, as such, perform very little argument - * checking for monotonicity and the like. + * class are designed to be fast and perform very little argument checking for + * monotonicity and the like. Results are undefined for x- and y-value arguments + * where {@code size < 2}. * * <p>Making some assumptions, interpolation is fairly straightforward. Most of * the methods implemented here are designed to support interpolation (or - * derivation) of y-values keyed to monotonically increasing x-values. x-value - * interpolation is somewhat thornier. Assumptions and behaviors: + * derivation) of y-values keyed to monotonically increasing x-values. X-value + * interpolation requires knowing if y-values are increasing or decreasing. + * Assumptions and behaviors: * * <ul><li>No error checking for null, empty, single-valued arrays; or arrays of * different lengths is performed. Buyer beware.</li> * * <li>X-value arrays are always assumed to be strictly monotonically ascending - * (no repeated values)</li> + * with no repeated values.</li> * * <li>Internally, binary search is used for y-value interpolation; linear * search is used for x-value interpolation.</li> @@ -29,7 +31,7 @@ import java.util.List; * <li>Y-value interpolation will always extrapolate off the ends a sequence; * this may change, or be configurable, in the future.</li> * - * <li>X-value interpolation is predicated on x-values representing some form of + * <li>X-value interpolation is predicated on y-values representing some form of * cumulative distribution function, either increasing or decreasing * (complementary), and must be specified as such. X-values are assumed to be * increasing by default.</li> @@ -46,20 +48,10 @@ import java.util.List; * all interpolation operations in this class. These two methods are point-order * agnostic. * - * TODO example; explain array swapping techniques for x-interpolation - * * @author U.S. Geological Survey */ public abstract class Interpolator { - /* - * Developer notes: - * - * ------------------------------------------------------------------------- - * Perhaps add extrapolation constraint (on/off) for y value interpolation - * ------------------------------------------------------------------------- - */ - private Interpolator() {} /** @@ -107,11 +99,11 @@ public abstract class Interpolator { * Return an interpolated x-value corresponding to the supplied y-value in the * supplied xy-sequence. * - * @param xys an xy-sequence + * @param xy an xy-sequence * @param y value at which to find x * @return an interpolated x-value */ - public abstract double findX(XySequence xys, double y); + public abstract double findX(XySequence xy, double y); /** * Return an interpolated or extrapolated y-value corresponding to the @@ -157,11 +149,11 @@ public abstract class Interpolator { * Return an interpolated or extrapolated y-value corresponding to the * supplied x-value in the supplied xy-sequence. * - * @param xys an xy-sequence + * @param xy an xy-sequence * @param x value at which to find y * @return an interpolated y-value */ - public abstract double findY(XySequence xys, double x); + public abstract double findY(XySequence xy, double x); /** * Return interpolated or extrapolated y-values using the supplied x- and @@ -189,26 +181,31 @@ public abstract class Interpolator { * Return interpolated or extrapolated y-values using the supplied x- and * y-value arrays. * - * @param xys an xy-sequence + * @param xy an xy-sequence * @param x values at which to find y-values * @return interpolated y-values */ - public abstract double[] findY(XySequence xys, double[] x); + public abstract double[] findY(XySequence xy, double[] x); + /** Create a new builder instance. */ public static Builder builder() { return new Builder(); } + /** + * An interpolator builder. + */ public static final class Builder { private Builder() {} boolean logx = false; boolean logy = false; - boolean xIncreasing = true; + boolean yIncreasing = true; /** - * Indicate that interpolation should be performed in y-value log space. + * Indicate that interpolation should be performed in {@code log(x)} value + * space. */ public Builder logx() { this.logx = true; @@ -216,7 +213,8 @@ public abstract class Interpolator { } /** - * Indicate that interpolation should be performed in y-value log space. + * Indicate that interpolation should be performed in {@code log(y)} value + * space. */ public Builder logy() { this.logy = true; @@ -224,12 +222,13 @@ public abstract class Interpolator { } /** - * Indicate if the x-values to be interpolated are decreasing. In the - * absence of calling this method, x-value are assumed to monotonically - * increasing. This setting has no effect on y-value interpolation. + * Indicate if the y-values to be interpolated decrease monotonically. In + * the absence of calling this method, both x- and y-values are assumed to + * monotonically increase. This setting has no effect on y-value + * interpolation. */ - public Builder decreasingX() { - this.xIncreasing = false; + public Builder decreasingY() { + this.yIncreasing = false; return this; } @@ -237,18 +236,17 @@ public abstract class Interpolator { * Return a newly created {@code Interpolator}. */ public Interpolator build() { - return new RegularInterpolator(logx, logy, xIncreasing); + return new RegularInterpolator(logx, logy, yIncreasing); } - } private static final class RegularInterpolator extends Interpolator { private final InterpolateFn yFunction; private final InterpolateFn xFunction; - private final boolean xIncreasing; + private final boolean yIncreasing; - private RegularInterpolator(boolean logx, boolean logy, boolean xIncreasing) { + private RegularInterpolator(boolean logx, boolean logy, boolean yIncreasing) { if (logx && logy) { xFunction = new XFn_LogX_LogY(); yFunction = new YFn_LogX_LogY(); @@ -262,12 +260,12 @@ public abstract class Interpolator { xFunction = new XFn(); yFunction = new YFn(); } - this.xIncreasing = xIncreasing; + this.yIncreasing = yIncreasing; } @Override public double findX(double[] xs, double[] ys, double y) { - int i = linearIndex(ys, y, xIncreasing); + int i = linearIndex(ys, y, yIncreasing); if (i == -1) { return 0; } @@ -276,7 +274,7 @@ public abstract class Interpolator { @Override public double findX(List<Double> xs, List<Double> ys, double y) { - int i = linearIndex(ys, y, xIncreasing); + int i = linearIndex(ys, y, yIncreasing); if (i == -1) { return 0; } @@ -284,10 +282,10 @@ public abstract class Interpolator { } @Override - public double findX(XySequence xys, double y) { + public double findX(XySequence xy, double y) { // safe covariant cast - ArrayXySequence ixys = (ArrayXySequence) xys; - return findX(ixys.xs, ixys.ys, y); + ArrayXySequence ixy = (ArrayXySequence) xy; + return findX(ixy.xs, ixy.ys, y); } @Override @@ -303,10 +301,10 @@ public abstract class Interpolator { } @Override - public double findY(XySequence xys, double x) { + public double findY(XySequence xy, double x) { // safe covariant cast - ArrayXySequence ixys = (ArrayXySequence) xys; - return findY(ixys.xs, ixys.ys, x); + ArrayXySequence ixy = (ArrayXySequence) xy; + return findY(ixy.xs, ixy.ys, x); } @Override @@ -328,10 +326,10 @@ public abstract class Interpolator { } @Override - public double[] findY(XySequence xys, double[] x) { + public double[] findY(XySequence xy, double[] x) { // safe covariant cast - ArrayXySequence ixys = (ArrayXySequence) xys; - return findY(ixys.xs, ixys.ys, x); + ArrayXySequence ixy = (ArrayXySequence) xy; + return findY(ixy.xs, ixy.ys, x); } } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/data/IntervalArray.java b/src/main/java/gov/usgs/earthquake/nshmp/data/IntervalArray.java index 8f8440f9638902305bb724ea06fb76c3f43be505..28fcda4884bcd84933a739e806a90e356669ebfc 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/data/IntervalArray.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/data/IntervalArray.java @@ -2,26 +2,22 @@ package gov.usgs.earthquake.nshmp.data; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkElementIndex; -import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static gov.usgs.earthquake.nshmp.data.IntervalData.checkDataState; import static gov.usgs.earthquake.nshmp.data.IntervalData.indexOf; import static gov.usgs.earthquake.nshmp.data.IntervalData.keys; +import static java.util.stream.Collectors.toList; import java.util.Arrays; -import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; - -import com.google.common.primitives.Doubles; /** * An array of immutable, double-valued data that is arranged according to * increasing and uniformly spaced double-valued keys. Interval arrays are used - * to represent binned data, and so while row keys are bin centers, indexing is - * managed internally using bin edges. This simplifies issues related to - * rounding/precision errors that occur when indexing according to explicit - * double values. + * to represent binned data (or histograms), and so while row keys are bin + * centers, indexing is managed internally using bin edges. This simplifies + * issues related to rounding/precision errors that occur when indexing and + * binning according to explicit double values. * * <p>To create an instance of an {@code IntervalArray}, use a {@link Builder}. * @@ -77,56 +73,56 @@ public final class IntervalArray { } /** - * Return an immutable view of {@code this} as an {@link XySequence}. + * An immutable view of {@code this} as an {@link XySequence}. */ public XySequence values() { return new ArrayXySequence(rows, data); } /** - * Return the lower edge of the lowermost bin. + * The lower edge of the lowermost bin. */ public double rowMin() { return rowMin; } /** - * Return the upper edge of the uppermost bin. + * The upper edge of the uppermost bin. */ public double rowMax() { return rowMax; } /** - * Return the row bin discretization. + * The row bin discretization. */ public double rowΔ() { return rowΔ; } /** - * Return an immutable list <i>view</i> of the row keys (bin centers). + * A lazily created copy of the row keys (bin centers). */ public List<Double> rows() { - return Collections.unmodifiableList(Doubles.asList(rows)); + return Arrays.stream(rows).boxed().collect(toList()); } /** - * Return the sum of the values in this array. + * The sum of the values in this array. */ public double sum() { return DoubleData.sum(data); } /** - * Return the index of the bin with smallest value. + * The index of the bin with smallest value. */ public int minIndex() { return Indexing.minIndex(data); } /** - * Return the index of the bin with largest value. + * The index of the bin with largest value. */ public int maxIndex() { return Indexing.maxIndex(data); @@ -136,23 +132,10 @@ public final class IntervalArray { public String toString() { StringBuilder sb = new StringBuilder(); IntervalData.appendArrayKeys(sb, "", rows()); - IntervalData.appendArrayValues(sb, values().yValues().boxed().collect(Collectors.toList())); + IntervalData.appendArrayValues(sb, values().yValues().boxed().collect(toList())); return sb.toString(); } - /** - * A supplier of values with which to fill an {@code IntervalArray}. - */ - interface Loader { - - /** - * Compute the value corresponding to the supplied row key (bin center). - * - * @param row value - */ - public double compute(double row); - } - /** * A builder of immutable {@code IntervalArray}s. * @@ -163,12 +146,6 @@ public final class IntervalArray { */ public static final class Builder { - /* - * TODO review Interval* classes; remove public constructor? static method - * access only enforces that init() is called and therefore all builder - * methods do not need to check init state first. - */ - private double[] data; private double rowMin; @@ -177,12 +154,8 @@ public final class IntervalArray { private double[] rows; private boolean built = false; - private boolean initialized = false; - /** - * Create a new builder. - */ - public Builder() {} + private Builder() {} /** * Create a new builder with the structure and content identical to that of @@ -191,7 +164,6 @@ public final class IntervalArray { * @param array to copy */ public static Builder copyOf(IntervalArray array) { - /* Safe covariant cast. */ Builder builder = copyStructure(array); builder.data = Arrays.copyOf( array.data, @@ -207,7 +179,6 @@ public final class IntervalArray { * @param model interval array */ public static Builder fromModel(IntervalArray model) { - /* Safe covariant cast. */ Builder builder = copyStructure(model); builder.init(); return builder; @@ -240,11 +211,9 @@ public final class IntervalArray { } private void init() { - checkState(!initialized, "Builder has already been initialized"); if (data == null) { data = new double[rows.length]; } - initialized = true; } /** @@ -302,7 +271,8 @@ public final class IntervalArray { } /** - * Add to the array being built. + * Add supplied values to this builder. Assumes that supplied value array is + * the same size or smaller than the internal builder data array. * * @param values to add * @throws IndexOutOfBoundsException if values overrun array @@ -317,62 +287,22 @@ public final class IntervalArray { } /** - * Add to the array being built. - * - * @param values to add - * @throws IndexOutOfBoundsException if values overrun array - */ - @Deprecated - public Builder add(List<Double> values) { - return add(Doubles.toArray(values)); - } - - /** - * Add the y-values of the supplied sequence to the array being built. + * Add the y-values of the supplied sequence to this builder. Assumes that + * supplied sequence is the same size or smaller than the internal builder + * data array. * * @param sequence to add * @throws IndexOutOfBoundsException if values overrun array */ - @Deprecated public Builder add(XySequence sequence) { // safe covariant cast return add(((ArrayXySequence) sequence).ys); } /** - * Add to the array being built starting at the specified row. - * - * @param row key from which to start adding values - * @param values to add - * @throws IndexOutOfBoundsException if values overrun array - */ - @Deprecated - public Builder add(double row, double[] values) { - int rowIndex = rowIndex(row); - checkElementIndex(rowIndex + values.length - 1, rows.length, - "Supplied values overrun end of row"); - for (int i = 0; i < values.length; i++) { - data[rowIndex + i] = values[i]; - } - return this; - } - - /** - * Add to the array being built starting at the specified row. - * - * @param row key from which to start adding values - * @param values to add - * @throws IndexOutOfBoundsException if values will overrun array - */ - @Deprecated - public Builder add(double row, List<Double> values) { - return add(row, Doubles.toArray(values)); - } - - /** - * Add the values in the supplied array to this builder. This operation is - * very efficient if this builder and the supplied array are sourced from - * the same model. + * Add the values in the supplied interval array to this builder. This + * operation is very efficient if this builder and the supplied array are + * sourced from the same model. * * @param array to add * @throws IllegalArgumentException if the rows of the supplied array do not @@ -385,10 +315,19 @@ public final class IntervalArray { return this; } + /* + * Check hash codes of row arrays in case fromModel or copyOf has been used, + * otherwise check array equality. + */ + IntervalArray validateArray(IntervalArray that) { + checkArgument(Arrays.equals(this.rows, that.rows)); + return that; + } + /** * Add each value-pair of the supplied sequence to the appropriate interval. * - * @param sequence to add + * @param sequence of values to add */ public Builder addEach(XySequence sequence) { sequence.stream().forEach(xy -> add(xy.x(), xy.y())); @@ -397,6 +336,7 @@ public final class IntervalArray { /** * Multiply ({@code scale}) all values in this builder. + * * @param scale factor */ public Builder multiply(double scale) { @@ -404,24 +344,12 @@ public final class IntervalArray { return this; } - /** - * Set the data - * @param data to set - */ + /** Internal method to directly set data array. */ Builder data(double[] data) { this.data = data; return this; } - /* - * Check hash codes of row arrays in case fromModel or copyOf has been used, - * otherwise check array equality. - */ - IntervalArray validateArray(IntervalArray that) { - checkArgument(Arrays.equals(this.rows, that.rows)); - return that; - } - /* * Data is not copied on build() so we dereference data arrays to prevent * lingering builders from further modifying data. @@ -431,22 +359,6 @@ public final class IntervalArray { rows = null; } - /** - * Return a newly-created, immutable, 2-dimensional data container populated - * with values computed by the supplied loader. Calling this method will - * overwrite any values already supplied via {@code set*} or {@code add*} - * methods. - * - * @param loader that will compute values - */ - public IntervalArray build(Loader loader) { - checkNotNull(loader); - for (int i = 0; i < rows.length; i++) { - data[i] = loader.compute(rows[i]); - } - return build(); - } - /** * Return a newly-created, immutable, interval data array populated with the * contents of this {@code Builder}. @@ -456,6 +368,7 @@ public final class IntervalArray { checkDataState(rows); IntervalArray array = new IntervalArray(this); dereference(); + built = true; return array; } } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/data/IntervalData.java b/src/main/java/gov/usgs/earthquake/nshmp/data/IntervalData.java index 31ae8e57ecf8eb1451086c3b48c89323c7052702..675d1e4f79134e88874cef8bf4957ecc52f16d54 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/data/IntervalData.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/data/IntervalData.java @@ -20,6 +20,8 @@ import gov.usgs.earthquake.nshmp.Text; */ public final class IntervalData { + private IntervalData() {} + /** * Create key values for use in an {@link IntervalArray}, * {@link IntervalTable} or {@link IntervalVolume}. These classes call this @@ -27,8 +29,8 @@ public final class IntervalData { * convenience as there are circumstances where a reference to the row or * column keys is helpful to have when working with the builders for these * classes. Internally, this method calls - * {@link DoubleData#buildCleanSequence(double, double, double, boolean, int)} - * with a precision value of 4 decimal places. This may change in the future. + * {@link Sequences#arrayBuilder(double, double, double)} with a precision + * value of 4 decimal places. This may change in the future. * * <p><b>Example:</b> {@code keys(5.0, 8.0, 1.0)} returns [5.5, 6.5, 7.5] * @@ -40,16 +42,11 @@ public final class IntervalData { return keyArray(min, max, checkDelta(min, max, delta)); } - /* - * Create clean sequence of keys. Precision is curently set to 4 decimal - * places. - */ + /* Create clean sequence of keys. */ private static double[] keyArray(double min, double max, double Δ) { - double Δby2 = Δ / 2.0; - return DoubleData.buildCleanSequence( - min + Δby2, - max - Δby2, - Δ, true, 4); + return Sequences.arrayBuilder(min, max, Δ) + .scale(4) + .build(); } /** @@ -72,24 +69,18 @@ public final class IntervalData { checkState(data != null, "%s data have not yet been fully specified", label); } - /* - * Ensure rows have been specified - */ + /* Ensure rows have been specified. */ static void checkDataState(double[] rows) { checkDataState(rows, "Row"); } - /* - * Ensure rows and columns have been specified - */ + /* Ensure rows and columns have been specified. */ static void checkDataState(double[] rows, double[] columns) { checkDataState(rows); checkDataState(columns, "Column"); } - /* - * Ensure rows, columns, and levels have been specified - */ + /* Ensure rows, columns, and levels have been specified. */ static void checkDataState(double[] rows, double[] columns, double[] levels) { checkDataState(rows, columns); checkDataState(levels, "Level"); diff --git a/src/main/java/gov/usgs/earthquake/nshmp/data/IntervalTable.java b/src/main/java/gov/usgs/earthquake/nshmp/data/IntervalTable.java index d944fa9c84e0e2e39aba0f73de2e710d549e6a42..ab7d07c692c47275556b785481270b52a7d8b4b1 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/data/IntervalTable.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/data/IntervalTable.java @@ -2,26 +2,23 @@ package gov.usgs.earthquake.nshmp.data; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkElementIndex; -import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static gov.usgs.earthquake.nshmp.data.IntervalData.checkDataState; import static gov.usgs.earthquake.nshmp.data.IntervalData.indexOf; import static gov.usgs.earthquake.nshmp.data.IntervalData.keys; +import static java.util.stream.Collectors.toList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.stream.Collectors; -import com.google.common.primitives.Doubles; - /** * A 2-dimensional table of immutable, double-valued data that is arranged * according to increasing and uniformly spaced double-valued keys. Interval - * tables are used to represent binned data, and so while row and column keys - * are bin centers, indexing is managed internally using bin edges. This - * simplifies issues related to rounding/precision errors that occur when - * indexing according to explicit double values. + * tables are used to represent binned data (or histograms), and so while row + * and column keys are bin centers, indexing is managed internally using bin + * edges. This simplifies issues related to rounding/precision errors that occur + * when indexing and binning according to explicit double values. * * <p>To create an instance of an {@code IntervalTable}, use a {@link Builder}. * @@ -115,31 +112,31 @@ public final class IntervalTable { } /** - * Return the lower edge of the lowermost row bin. + * The lower edge of the lowermost row bin. */ public double rowMin() { return rowMin; } /** - * Return the upper edge of the uppermost row bin. + * The upper edge of the uppermost row bin. */ public double rowMax() { return rowMax; } /** - * Return the row bin discretization. + * The row bin discretization. */ public double rowΔ() { return rowΔ; } /** - * Return an immutable list <i>view</i> of the row keys (bin centers). + * A lazily created copy of the row keys (bin centers). */ public List<Double> rows() { - return Collections.unmodifiableList(Doubles.asList(rows)); + return Arrays.stream(rows).boxed().collect(toList()); } /** @@ -164,10 +161,10 @@ public final class IntervalTable { } /** - * Return an immutable list <i>view</i> of the column keys (bin centers). + * A lazily created copy of the column keys (bin centers). */ public List<Double> columns() { - return Collections.unmodifiableList(Doubles.asList(columns)); + return Arrays.stream(columns).boxed().collect(toList()); } /** @@ -182,7 +179,7 @@ public final class IntervalTable { } /** - * Return the indices of the bin with smallest value in the form + * The indices of the bin with smallest value in the form * {@code [rowIndex, columnIndex]}. */ public int[] minIndex() { @@ -190,7 +187,7 @@ public final class IntervalTable { } /** - * Return the indices of the bin with largest value in the form + * The indices of the bin with largest value in the form * {@code [rowIndex, columnIndex]}. */ public int[] maxIndex() { @@ -209,21 +206,6 @@ public final class IntervalTable { return sb.toString(); } - /** - * A supplier of values with which to fill a {@code IntervalTable}. - */ - interface Loader { - - /** - * Compute the value corresponding to the supplied row and column keys (bin - * centers). - * - * @param row value - * @param column value - */ - public double compute(double row, double column); - } - /** * A builder of immutable {@code IntervalTable}s. * @@ -247,11 +229,8 @@ public final class IntervalTable { private double[] columns; private boolean built = false; - private boolean initialized = false; - /** - * Create a new builder. - */ + /** Create a new interval table builder. */ public Builder() {} /** @@ -261,7 +240,6 @@ public final class IntervalTable { * @param table to copy */ public static Builder copyOf(IntervalTable table) { - /* Safe covariant cast. */ Builder builder = copyStructure(table); builder.data = DoubleData.copyOf(table.data); builder.init(); @@ -275,7 +253,6 @@ public final class IntervalTable { * @param model data table */ public static Builder fromModel(IntervalTable model) { - /* Safe covariant cast. */ Builder builder = copyStructure(model); builder.init(); return builder; @@ -327,12 +304,10 @@ public final class IntervalTable { } private void init() { - checkState(!initialized, "Builder has already been initialized"); if (rows != null && columns != null) { if (data == null) { data = new double[rows.length][columns.length]; } - initialized = true; } } @@ -360,7 +335,6 @@ public final class IntervalTable { * @param column key * @param value to set */ - @Deprecated public Builder set(double row, double column, double value) { return set(rowIndex(row), columnIndex(column), value); } @@ -373,7 +347,6 @@ public final class IntervalTable { * @param column index * @param value to set */ - @Deprecated public Builder set(int row, int column, double value) { data[row][column] = value; return this; @@ -387,7 +360,6 @@ public final class IntervalTable { * @param column key * @param value to add */ - @Deprecated public Builder add(double row, double column, double value) { return add(rowIndex(row), columnIndex(column), value); } @@ -400,18 +372,19 @@ public final class IntervalTable { * @param column index * @param value to add */ - @Deprecated public Builder add(int row, int column, double value) { data[row][column] += value; return this; } /** - * Add to the values in the specified row. + * Add to the column array in the specified row. Assumes that supplied value + * array is the same size or smaller than the columns of the internal + * builder data array. * * @param row key * @param values to add - * @throws IndexOutOfBoundsException if values overrun row + * @throws IndexOutOfBoundsException if values overrun column */ public Builder add(double row, double[] values) { checkElementIndex(values.length - 1, columns.length, @@ -424,24 +397,13 @@ public final class IntervalTable { } /** - * Add to the values in the specified row. - * - * @param row key - * @param values to add - * @throws IndexOutOfBoundsException if values overrun row - */ - @Deprecated - public Builder add(double row, List<Double> values) { - return add(row, Doubles.toArray(values)); - } - - /** - * Add the y-values of the supplied sequence to the values in the specified - * row. + * Add the y-values of the supplied sequence to the column array at the + * specified row. Assumes that supplied sequence is the same size or smaller + * than the columns of the internal builder data array. * * @param row key * @param sequence to add - * @throws IndexOutOfBoundsException if values overrun row + * @throws IndexOutOfBoundsException if values overrun column */ public Builder add(double row, XySequence sequence) { // safe covariant cast @@ -449,84 +411,46 @@ public final class IntervalTable { } /** - * Add to the values in the specified row starting at the specified column. - * - * @param row key - * @param column key from which to start adding values - * @param values to add - * @throws IndexOutOfBoundsException if values overrun row - */ - @Deprecated - public Builder add(double row, double column, double[] values) { - int columnIndex = columnIndex(column); - checkElementIndex(columnIndex + values.length - 1, columns.length, - "Supplied values overrun end of row"); - double[] rowData = data[rowIndex(row)]; - for (int i = 0; i < values.length; i++) { - rowData[columnIndex + i] = values[i]; - } - return this; - } - - /** - * Add to the values in the specified row starting at the specified column. - * - * @param row key - * @param column key from which to start adding values - * @param values to add - * @throws IndexOutOfBoundsException if values will overrun row - */ - @Deprecated - public Builder add(double row, double column, List<Double> values) { - return add(row, column, Doubles.toArray(values)); - } - - /** - * Add the values in the supplied table to this builder. This operation is - * very efficient if this builder and the supplied table are sourced from - * the same model. + * Add the values in the supplied interval table to this builder. This + * operation is very efficient if this builder and the supplied table are + * sourced from the same model. * * @param table to add * @throws IllegalArgumentException if the rows and columns of the supplied * table do not match those of this table * @see #fromModel(IntervalTable) */ - @Deprecated public Builder add(IntervalTable table) { validateTable(table); DoubleData.uncheckedAdd(data, table.data); return this; } + /* + * Checks hash codes of row and column arrays in case fromModel or copyOf + * has been used, otherwise check array equality. + */ + IntervalTable validateTable(IntervalTable that) { + checkArgument(Arrays.equals(this.rows, that.rows)); + checkArgument(Arrays.equals(this.columns, that.columns)); + return that; + } + /** * Multiply ({@code scale}) all values in this builder. * @param scale factor */ - @Deprecated public Builder multiply(double scale) { DoubleData.multiply(scale, data); return this; } - /** - * Set the data. - * @param data to set - */ + /** Internal method to directly set data arrays. */ Builder data(double[][] data) { this.data = data; return this; } - /* - * Check hash codes of row and column arrays in case fromModel or copyOf has - * been used, otherwise check array equality. - */ - IntervalTable validateTable(IntervalTable that) { - checkArgument(Arrays.equals(this.rows, that.rows) && - Arrays.equals(this.columns, that.columns)); - return that; - } - /* * Data is not copied on build() so we dereference data arrays to prevent * lingering builders from further modifying data. @@ -537,25 +461,6 @@ public final class IntervalTable { columns = null; } - /** - * Return a newly-created, immutable, 2-dimensional data container populated - * with values computed by the supplied loader. Calling this method will - * overwrite any values already supplied via {@code set*} or {@code add*} - * methods. - * - * @param loader that will compute values - */ - public IntervalTable build(Loader loader) { - checkNotNull(loader); - for (int i = 0; i < rows.length; i++) { - double row = rows[i]; - for (int j = 0; j < columns.length; j++) { - data[i][j] = loader.compute(row, columns[j]); - } - } - return build(); - } - /** * Return a newly-created, immutable, 2-dimensional data container populated * with the contents of this {@code Builder}. @@ -565,6 +470,7 @@ public final class IntervalTable { checkDataState(rows, columns); IntervalTable table = new IntervalTable(this); dereference(); + built = true; return table; } } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/data/IntervalVolume.java b/src/main/java/gov/usgs/earthquake/nshmp/data/IntervalVolume.java index 1150e1f7fb6ff583799d881996aa9fb1ccc77dc5..d6343e0089b3aee2c243198917fdb83bede32007 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/data/IntervalVolume.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/data/IntervalVolume.java @@ -1,11 +1,12 @@ package gov.usgs.earthquake.nshmp.data; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkElementIndex; import static com.google.common.base.Preconditions.checkState; import static gov.usgs.earthquake.nshmp.data.IntervalData.checkDataState; import static gov.usgs.earthquake.nshmp.data.IntervalData.indexOf; import static gov.usgs.earthquake.nshmp.data.IntervalData.keys; +import static java.util.stream.Collectors.toList; import java.util.Arrays; import java.util.Collections; @@ -17,10 +18,10 @@ import com.google.common.primitives.Doubles; /** * A 3-dimensional volume of immutable, double-valued data that is arranged * according to increasing and uniformly spaced double-valued keys. Data volumes - * are used to represent binned data, and so while row, column, and level keys - * are bin centers, indexing is managed internally using bin edges. This - * simplifies issues related to rounding/precision errors that occur when - * indexing according to explicit double values. + * are used to represent binned data (or histograms), and so while row, column, + * and level keys are bin centers, indexing is managed internally using bin + * edges. This simplifies issues related to rounding/precision errors that occur + * when indexing and binning according to explicit double values. * * <p>To create an instance of an {@code IntervalVolume}, use a {@link Builder}. * @@ -107,9 +108,8 @@ public final class IntervalVolume { } /** - * Return an immutable view of the values that map to the supplied row and - * column values. Do not confuse with {@link #column(int, int)} retrieval by - * index. + * An immutable view of the values that map to the supplied row and column + * values. Do not confuse with {@link #column(int, int)} retrieval by index. * * @param rowValue of bin to retrieve * @param columnValue of bin to retrieve @@ -121,9 +121,9 @@ public final class IntervalVolume { } /** - * Return an immutable view of the values that map to the supplied row and - * column values. Do not confuse with {@link #column(double, double)} - * retrieval by index. + * An immutable view of the values that map to the supplied row and column + * indices. Do not confuse with {@link #column(double, double)} retrieval by + * index. * * @param rowIndex of bin to retrieve * @param columnIndex of bin to retrieve @@ -133,84 +133,84 @@ public final class IntervalVolume { } /** - * Return the lower edge of the lowermost row bin. + * The lower edge of the lowermost row bin. */ public double rowMin() { return rowMin; } /** - * Return the upper edge of the uppermost row bin. + * The upper edge of the uppermost row bin. */ public double rowMax() { return rowMax; } /** - * Return the row bin discretization. + * The row bin discretization. */ public double rowΔ() { return rowΔ; } /** - * Return an immutable list <i>view</i> of the row keys (bin centers). + * A lazily created copy of the row keys (bin centers). */ public List<Double> rows() { - return Collections.unmodifiableList(Doubles.asList(rows)); + return Arrays.stream(rows).boxed().collect(toList()); } /** - * Return the lower edge of the lowermost column bin. + * The lower edge of the lowermost column bin. */ public double columnMin() { return columnMin; } /** - * Return the upper edge of the uppermost column bin. + * The upper edge of the uppermost column bin. */ public double columnMax() { return columnMax; } /** - * Return the column bin discretization. + * The column bin discretization. */ public double columnΔ() { return columnΔ; } /** - * Return an immutable list <i>view</i> of the column keys (bin centers). + * A lazily created copy of the column keys (bin centers). */ public List<Double> columns() { return Collections.unmodifiableList(Doubles.asList(columns)); } /** - * Return the lower edge of the lowermost level bin. + * The lower edge of the lowermost level bin. */ public double levelMin() { return levelMin; } /** - * Return the upper edge of the uppermost level bin. + * The upper edge of the uppermost level bin. */ public double levelMax() { return levelMax; } /** - * Return the level bin discretization. + * The level bin discretization. */ public double levelΔ() { return levelΔ; } /** - * Return an immutable list <i>view</i> of the level keys (bin centers). + * A lazily created copy of the level keys (bin centers). */ public List<Double> levels() { return Collections.unmodifiableList(Doubles.asList(levels)); @@ -262,22 +262,6 @@ public final class IntervalVolume { return sb.toString(); } - /** - * A supplier of values with which to fill a {@code IntervalVolume}. - */ - interface Loader { - - /** - * Compute the value corresponding to the supplied row, column, and level - * keys. - * - * @param row value - * @param column value - * @param level value - */ - public double compute(double row, double column, double level); - } - /** * A builder of immutable {@code IntervalVolume}s. * @@ -306,11 +290,8 @@ public final class IntervalVolume { private double[] levels; private boolean built = false; - private boolean initialized = false; - /** - * Create a new builder. - */ + /** Create a new interval volume builder. */ public Builder() {} /** @@ -404,12 +385,10 @@ public final class IntervalVolume { } private void init() { - checkState(!initialized, "Builder has already been initialized"); if (rows != null && columns != null && levels != null) { if (data == null) { data = new double[rows.length][columns.length][levels.length]; } - initialized = true; } } @@ -446,7 +425,6 @@ public final class IntervalVolume { * @param level key * @param value to set */ - @Deprecated public Builder set(double row, double column, double level, double value) { return set(rowIndex(row), columnIndex(column), levelIndex(level), value); } @@ -460,7 +438,6 @@ public final class IntervalVolume { * @param level index * @param value to set */ - @Deprecated public Builder set(int row, int column, int level, double value) { data[row][column][level] = value; return this; @@ -475,7 +452,6 @@ public final class IntervalVolume { * @param level key * @param value to add */ - @Deprecated public Builder add(double row, double column, double level, double value) { return add(rowIndex(row), columnIndex(column), levelIndex(level), value); } @@ -496,9 +472,44 @@ public final class IntervalVolume { } /** - * Add the values in the supplied volume to this builder. This operation is - * very efficient if this builder and the supplied volume are sourced from - * the same model. + * Add to the level array at the specified row and column. Assumes that + * supplied value array is the same size or smaller than the levels of the + * internal builder data array. + * + * @param row key + * @param column key + * @param values to add + * @throws IndexOutOfBoundsException if values overrun level + */ + public Builder add(double row, double column, double[] values) { + checkElementIndex(values.length - 1, columns.length, + "Supplied values overrun end of row"); + double[] rowColumnData = data[rowIndex(row)][columnIndex(column)]; + for (int i = 0; i < values.length; i++) { + rowColumnData[i] += values[i]; + } + return this; + } + + /** + * Add the y-values of the supplied sequence to the level array at the + * specified row and column. Assumes that supplied sequence is the same size + * or smaller than the levels of the internal builder data array. + * + * @param row key + * @param column key + * @param sequence to add + * @throws IndexOutOfBoundsException if values overrun level + */ + public Builder add(double row, double column, XySequence sequence) { + // safe covariant cast + return add(row, column, ((ArrayXySequence) sequence).ys); + } + + /** + * Add the values in the supplied interval volume to this builder. This + * operation is very efficient if this builder and the supplied volume are + * sourced from the same model. * * @param volume to add * @throws IllegalArgumentException if the rows, columns, and levels of the @@ -511,6 +522,12 @@ public final class IntervalVolume { return this; } + /** Internal method to directly set data arrays. */ + Builder data(double[][][] data) { + this.data = data; + return this; + } + /** * Multiply ({@code scale}) all values in this builder. * @param scale factor @@ -525,9 +542,9 @@ public final class IntervalVolume { * copyOf has been used, otherwise check array equality. */ IntervalVolume validateVolume(IntervalVolume that) { - checkArgument(Arrays.equals(this.rows, that.rows) && - Arrays.equals(this.columns, that.columns) && - Arrays.equals(this.levels, that.levels)); + checkArgument(Arrays.equals(this.rows, that.rows)); + checkArgument(Arrays.equals(this.columns, that.columns)); + checkArgument(Arrays.equals(this.levels, that.levels)); return that; } @@ -542,28 +559,6 @@ public final class IntervalVolume { levels = null; } - /** - * Return a newly-created, immutable, 3-dimensional interval data container - * populated with values computed by the supplied loader. Calling this - * method will overwrite any values already supplied via {@code set*} or - * {@code add*} methods. - * - * @param loader that will compute values - */ - public IntervalVolume build(Loader loader) { - checkNotNull(loader); - for (int i = 0; i < rows.length; i++) { - double row = rows[i]; - for (int j = 0; j < columns.length; j++) { - double column = columns[j]; - for (int k = 0; k < levels.length; k++) { - data[i][j][k] = loader.compute(row, column, levels[k]); - } - } - } - return build(); - } - /** * Return a newly-created, immutable 3-dimensional interval data container * populated with the contents of this {@code Builder}. @@ -573,6 +568,7 @@ public final class IntervalVolume { checkDataState(rows, columns, levels); IntervalVolume volume = new IntervalVolume(this); dereference(); + built = true; return volume; } } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/data/Sequences.java b/src/main/java/gov/usgs/earthquake/nshmp/data/Sequences.java index 023cd0330cf852b18383ecd41158b95fa4a9bd83..2ada7a7c3ab2fd06ef68b47d9aeed2514a57d763 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/data/Sequences.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/data/Sequences.java @@ -20,6 +20,10 @@ public class Sequences { private Sequences() {} + /** + * Check that the supplied arrays are not empty, are the same size, and that + * the supplied xs increase monotonically. + */ static void validateArrays(double[] xs, double[] ys) { checkArgument(xs.length > 0, "x-values may not be empty"); checkArgument(xs.length == ys.length, "x- and y-values are different sizes"); @@ -30,9 +34,10 @@ public class Sequences { /** * Trim points from the start or end of a sequence for which y-values are - * zero. If no such leading or trailing points exist, sequence conatins only a + * zero. If no such leading or trailing points exist, sequence contains only a * single point, or all y-values are zero, method returns the supplied - * sequence. + * sequence. If supplied sequence is mutable, returned sequence is mutable; + * otherwise returned sequence is immutable. */ static XySequence trim(ArrayXySequence xy) { if (xy.size() == 1 || xy.isClear()) { @@ -46,7 +51,6 @@ public class Sequences { double[] xs = Arrays.copyOfRange(xy.xs, iMin, iMax); double[] ys = Arrays.copyOfRange(xy.ys, iMin, iMax); - // TODO can this cast be to MutableXySequence return (xy instanceof MutableArrayXySequence) ? new MutableArrayXySequence(xs, ys) : new ArrayXySequence(xs, ys); @@ -93,7 +97,7 @@ public class Sequences { return ((int) Maths.round(((max - min) / delta), 6)) + 1; } - /* + /** * Ensure validity of sequence discretization parameters. Confirms that for a * specified range [min..max] and Δ that (1) min, max, and Δ are finite, (2) * max > min, and (3) Δ > 0. Returns the supplied Δ for use inline. @@ -107,8 +111,9 @@ public class Sequences { return Δ; } - // TODO docs; consider moving to cumulate method in data/sequence package; - // not necessarily MFD specific + /** + * Create a new XySequence with reverse cumulative values. + */ public static XySequence toCumulative(XySequence incremental) { MutableXySequence cumulative = MutableXySequence.copyOf(incremental); double sum = 0.0; @@ -216,32 +221,12 @@ public class Sequences { : sequence; } - /* - * TODO clean - * - * Method rounds (max-min)/Δ to 1e-6 before casting to an integer to - * determine the number of elements in the sequence. If (max-min)/Δ is - * reasonably close to an integer but max is off by some double-precision - * error (e.g. 6.7999999999), then max of 6.8 will be the upper end of the - * sequence. - */ - // private static double[] buildSequence(double min, double max, double Δ) { - // checkSequenceParameters(min, max, Δ); - // int size = ((int) Maths.round(((max - min) / Δ), 6)) + 1; - // checkArgument(Range.openClosed(0, 100000).contains(size), "sequence - // size"); - // return IntStream.range(0, size) - // .mapToDouble(i -> min + Δ * i) - // .toArray(); - // } - private static double[] buildSequence(double min, double Δ, int size) { checkArgument(Range.openClosed(0, 100000).contains(size), "sequence size"); return IntStream.range(0, size) .mapToDouble(i -> min + Δ * i) .toArray(); } - } } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/mfd/Mfd.java b/src/main/java/gov/usgs/earthquake/nshmp/mfd/Mfd.java index 088b139a08a2900ae490aa757dea8cfbee85381b..1a08b70d6ab9b7cf4deb6a114cab5067657d2d81 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/mfd/Mfd.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/mfd/Mfd.java @@ -98,7 +98,8 @@ public final class Mfd { * additionally check that type is correctly set to defend against * deserialization errors. * - * TODO equals/hashCode? + * TODO equals/hashCode? TODO not sure deltaM is being rigorously checked + * agains min max etc * * TODO note in documentation default a and rate = 1 for correct scaling * behavior diff --git a/src/main/java/gov/usgs/earthquake/nshmp/model/GridSourceSet.java b/src/main/java/gov/usgs/earthquake/nshmp/model/GridSourceSet.java index 6a384f0b54a70c0c7af76c87bbc02ba440a74239..c5c17913d609163433f842675fb47cccb017a2b8 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/model/GridSourceSet.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/model/GridSourceSet.java @@ -34,6 +34,7 @@ import gov.usgs.earthquake.nshmp.Earthquakes; import gov.usgs.earthquake.nshmp.data.DoubleData; import gov.usgs.earthquake.nshmp.data.IntervalTable; import gov.usgs.earthquake.nshmp.data.MutableXySequence; +import gov.usgs.earthquake.nshmp.data.Sequences; import gov.usgs.earthquake.nshmp.data.XySequence; import gov.usgs.earthquake.nshmp.fault.FocalMech; import gov.usgs.earthquake.nshmp.fault.surface.RuptureScaling; @@ -1022,10 +1023,12 @@ public class GridSourceSet extends AbstractSourceSet<PointSource> { } private static final int PRECISION = 5; - private static final double[] OFFSET_SCALE_10 = DoubleData.buildCleanSequence( - -0.45, 0.45, 0.1, true, 2); - private static final double[] OFFSET_SCALE_4 = DoubleData.buildCleanSequence( - -0.375, 0.375, 0.25, true, 3); + private static final double[] OFFSET_SCALE_10 = Sequences + .arrayBuilder(-0.45, 0.45, 0.1) + .build(); + private static final double[] OFFSET_SCALE_4 = Sequences + .arrayBuilder(-0.375, 0.375, 0.25) + .build(); /* * Create lat-lon offset values for use when building list of distributed diff --git a/src/main/java/gov/usgs/earthquake/nshmp/model/SystemSourceSet.java b/src/main/java/gov/usgs/earthquake/nshmp/model/SystemSourceSet.java index c9aa38556579f5c1a741503ef07dccf44a53bf50..a3eb002e5e25c4733c20becf9f8d460a841d621d 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/model/SystemSourceSet.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/model/SystemSourceSet.java @@ -294,7 +294,7 @@ public final class SystemSourceSet extends AbstractSourceSet<SystemSourceSet.Sys // NOTE we're doublechecking a UCERF3 rule that ruptures be composed // of at least 2 sections; this may not be the case in the future. checkArgument(indices.length > 1, "Rupture index list must contain 2 or more values"); - bitsets.add(Indexing.indicesToBits(indices, sections.size())); + bitsets.add(Indexing.indicesToBits(sections.size(), indices)); return this; } diff --git a/src/test/java/gov/usgs/earthquake/nshmp/data/DoubleDataTests.java b/src/test/java/gov/usgs/earthquake/nshmp/data/DoubleDataTests.java index ea8e728d91ebd137dd921992cc2da67ef9f00b98..d43d0483fa4fd3b50c2236b4c2bd4f770e765abf 100644 --- a/src/test/java/gov/usgs/earthquake/nshmp/data/DoubleDataTests.java +++ b/src/test/java/gov/usgs/earthquake/nshmp/data/DoubleDataTests.java @@ -11,17 +11,11 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; -import java.util.EnumMap; import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.function.DoubleUnaryOperator; import java.util.stream.Collectors; import org.junit.jupiter.api.Test; -import com.google.common.collect.Lists; import com.google.common.collect.Range; import com.google.common.primitives.Doubles; @@ -174,23 +168,6 @@ final class DoubleDataTests { }); } - @Test - final void testAddMap() { - Map<TimeUnit, Double> m1 = new EnumMap<>(TimeUnit.class); - m1.put(TimeUnit.DAYS, 0.01); - m1.put(TimeUnit.MINUTES, 0.5); - Map<TimeUnit, Double> m2 = new EnumMap<>(TimeUnit.class); - m2.put(TimeUnit.DAYS, 0.01); - m2.put(TimeUnit.HOURS, 0.2); - m2.put(TimeUnit.MINUTES, 0.5); - Map<TimeUnit, Double> mExpect = new EnumMap<>(TimeUnit.class); - mExpect.put(TimeUnit.DAYS, 0.02); - mExpect.put(TimeUnit.HOURS, 0.2); - mExpect.put(TimeUnit.MINUTES, 1.0); - Map<TimeUnit, Double> m1p2 = DoubleData.add(m1, m2); - assertEquals(mExpect, m1p2); - } - @Test final void testSubtract() { // 1D array and list @@ -306,6 +283,14 @@ final class DoubleDataTests { assertArrayEquals(expectArray, actualArray, 0.0); } + @Test + final void testComplement() { + double[] expectArray = new double[] { 0.2, 0.5, 0.8 }; + double[] actualArray = new double[] { 0.8, 0.5, 0.2 }; + DoubleData.complement(actualArray); + assertArrayEquals(expectArray, actualArray, 0.01); + } + @Test final void testFlip() { double[] expectArray = { -1.0, -10.0, -100.0 }; @@ -343,30 +328,12 @@ final class DoubleDataTests { assertArrayEquals(expected, DoubleData.cumulate(data), 0.0); } - @Test - final void testTransform() { - DoubleUnaryOperator fn = new DoubleUnaryOperator() { - - @Override - public double applyAsDouble(double operand) { - return operand + 1; - } - }; - - double[] expectArray = { 2.0, 11.0, 101.0 }; - double[] actualArray = DoubleData.transform(fn, valueArray()); - List<Double> actualList = DoubleData.transform(fn, valueList()); - testArrayAndList(expectArray, actualArray, actualList); - } - @Test final void testNormalize() { double[] expectArray = { 0.2, 0.3, 0.5 }; double[] inputArray = { 20, 30, 50 }; - List<Double> inputList = Doubles.asList(Arrays.copyOf(inputArray, inputArray.length)); double[] actualArray = DoubleData.normalize(inputArray); - List<Double> actualList = DoubleData.normalize(inputList); - testArrayAndList(expectArray, actualArray, actualList); + assertArrayEquals(expectArray, actualArray, 0.0); } @Test @@ -460,11 +427,6 @@ final class DoubleDataTests { assertFalse(DoubleData.isPositiveAndReal(0.0)); assertFalse(DoubleData.isPositiveAndReal(Double.POSITIVE_INFINITY)); assertFalse(DoubleData.isPositiveAndReal(Double.NaN)); - // arePositiveAndReal - assertTrue(DoubleData.arePositiveAndReal(valueArray())); - assertFalse(DoubleData.arePositiveAndReal(DoubleData.flip(valueArray()))); - assertTrue(DoubleData.arePositiveAndReal(valueList())); - assertFalse(DoubleData.arePositiveAndReal(0)); // isPositiveAndRealOrZero assertTrue(DoubleData.isPositiveAndRealOrZero(10.0)); assertTrue(DoubleData.isPositiveAndRealOrZero(0.0)); @@ -473,13 +435,9 @@ final class DoubleDataTests { // arePositiveAndRealOrZero assertTrue(DoubleData.arePositiveAndRealOrZero(valueArray())); assertFalse(DoubleData.arePositiveAndRealOrZero(DoubleData.flip(valueArray()))); - assertTrue(DoubleData.arePositiveAndRealOrZero(valueList())); - assertTrue(DoubleData.arePositiveAndRealOrZero(0)); // areZeroValued assertTrue(DoubleData.areZeroValued(new double[] { 0, 0 })); assertFalse(DoubleData.areZeroValued(new double[] { 0, 1 })); - assertTrue(DoubleData.areZeroValued(Lists.<Double> newArrayList(0.0, 0.0))); - assertFalse(DoubleData.areZeroValued(Lists.<Double> newArrayList(0.0, 1.0))); // areMonotonic double[] increasing_dupes = { -10, -1, 0, 0, 1, 10 }; double[] increasing_nodupes = { -10, -1, 0, 1, 10 }; @@ -495,37 +453,9 @@ final class DoubleDataTests { @Test final void testStateIAE() { - assertThrows(IllegalArgumentException.class, () -> { - DoubleData.arePositiveAndReal(); - }); - - assertThrows(IllegalArgumentException.class, () -> { - DoubleData.arePositiveAndReal(new ArrayList<Double>()); - }); - - assertThrows(IllegalArgumentException.class, () -> { - DoubleData.arePositiveAndReal(); - }); - - assertThrows(IllegalArgumentException.class, () -> { - DoubleData.arePositiveAndReal(new ArrayList<Double>()); - }); - - assertThrows(IllegalArgumentException.class, () -> { - DoubleData.arePositiveAndReal(); - }); - - assertThrows(IllegalArgumentException.class, () -> { - DoubleData.arePositiveAndReal(new ArrayList<Double>()); - }); - assertThrows(IllegalArgumentException.class, () -> { DoubleData.areZeroValued(); }); - - assertThrows(IllegalArgumentException.class, () -> { - DoubleData.areZeroValued(new ArrayList<Double>()); - }); } /* * * * * * * * * * * * PRECONDITIONS * * * * * * * * * * * */ @@ -555,25 +485,6 @@ final class DoubleDataTests { }); } - @Test - final void testCheckFinite() { - // also tests checkFinite(double) - double[] expectArray = { 5.0, 2.0 }; - assertArrayEquals(expectArray, DoubleData.checkFinite(expectArray), 0.0); - assertSame(expectArray, DoubleData.checkFinite(expectArray)); - List<Double> expectCollect = Doubles.asList(expectArray); - assertEquals(expectCollect, DoubleData.checkFinite(expectCollect)); - assertSame(expectCollect, DoubleData.checkFinite(expectCollect)); - - assertThrows(IllegalArgumentException.class, () -> { - DoubleData.checkFinite(new double[] { 0, Double.NaN }); - }); - - assertThrows(IllegalArgumentException.class, () -> { - DoubleData.checkFinite(Doubles.asList(0, Double.NEGATIVE_INFINITY)); - }); - } - @Test final void testCheckInRange() { // also tests checkInRange(double) @@ -581,10 +492,6 @@ final class DoubleDataTests { Range<Double> r = Range.open(0.0, 10.0); assertArrayEquals(expectArray, DoubleData.checkInRange(r, expectArray), 0.0); assertSame(expectArray, DoubleData.checkInRange(r, expectArray)); - List<Double> expectCollect = Doubles.asList(expectArray); - assertEquals(expectCollect, DoubleData.checkInRange(r, expectCollect)); - assertSame(expectCollect, DoubleData.checkInRange(r, expectCollect)); - } @Test @@ -593,10 +500,13 @@ final class DoubleDataTests { Range<Double> r = Range.open(0.0, 10.0); DoubleData.checkInRange(r, new double[] { -1.0 }); }); + } + @Test + final void testCheckIsPositiveAndReal() { + assertEquals(DoubleData.checkIsPositiveAndReal(1.0), 1.0, 0.0); assertThrows(IllegalArgumentException.class, () -> { - Range<Double> r = Range.open(0.0, 10.0); - DoubleData.checkInRange(r, Doubles.asList(-1.0)); + DoubleData.checkIsPositiveAndReal(-1.0); }); } @@ -649,48 +559,6 @@ final class DoubleDataTests { } - /* * * * * * * * Build Sequence * * * * * * * * */ - - @Test - final void testBuildSequence() { - double min = 1.0; - double max = 3.0; - double step = 0.5; - - double[] expectedAsc = new double[] { 1.0, 1.5, 2.0, 2.5, 3.0 }; - List<Double> exList = Arrays.stream(expectedAsc).boxed().collect(Collectors.toList()); - Collections.reverse(exList); - double[] expectedDes = Doubles.toArray(exList); - - double[] actualAsc = DoubleData.buildSequence(min, max, step, true); - double[] actualDes = DoubleData.buildSequence(min, max, step, false); - - assertArrayEquals(expectedAsc, actualAsc, 0.0); - assertArrayEquals(expectedDes, actualDes, 0.0); - assertArrayEquals(new double[] { min }, DoubleData.buildSequence(min, min, step, true), 0); - - assertThrows(IllegalArgumentException.class, () -> { - DoubleData.buildSequence(1.0, 0.0, 1.0, true); - }); - - assertThrows(IllegalArgumentException.class, () -> { - DoubleData.buildSequence(0, 10001, 1, true); - }); - } - - @Test - final void testCleanBuildSequence() { - double min = 1.0; - double max = 3.0; - double step = 0.49; - int scale = 2; - - double[] expected = new double[] { 1.0, 1.49, 1.98, 2.47, 2.96, 3.0 }; - double[] actual = DoubleData.buildCleanSequence(min, max, step, true, scale); - - assertArrayEquals(expected, actual, 0.0); - } - /* * * * * * * * 2D & 3D ARRAYS EXTENSIONS * * * * * * * * */ @Test @@ -715,26 +583,4 @@ final class DoubleDataTests { } } - @Test - final void testToString() { - // 2D - double[][] expect2D = { valueArray(), valueArray(), {}, { 1.0, Double.NaN } }; - String expect2Dstr = "[[1.0, 10.0, 100.0],\n" + - " [1.0, 10.0, 100.0],\n" + - " [],\n" + - " [1.0, NaN]]"; - assertEquals(expect2Dstr, DoubleData.toString(expect2D)); - // 3D - double[][][] expect3D = { - { valueArray(), valueArray() }, - { valueArray() }, - { {}, { 1.0, Double.NaN } } }; - String expect3Dstr = "[[[1.0, 10.0, 100.0],\n" + - " [1.0, 10.0, 100.0]],\n" + - " [[1.0, 10.0, 100.0]],\n" + - " [[],\n" + - " [1.0, NaN]]]"; - assertEquals(expect3Dstr, DoubleData.toString(expect3D)); - } - } diff --git a/src/test/java/gov/usgs/earthquake/nshmp/data/IndexingTests.java b/src/test/java/gov/usgs/earthquake/nshmp/data/IndexingTests.java index c57446268d4f9d87ac95a71e3fdf98eb78685884..efa4e831eff20973b92aea4170dad18fd476ae75 100644 --- a/src/test/java/gov/usgs/earthquake/nshmp/data/IndexingTests.java +++ b/src/test/java/gov/usgs/earthquake/nshmp/data/IndexingTests.java @@ -9,6 +9,7 @@ import java.util.List; import org.junit.jupiter.api.Test; +import com.google.common.collect.Range; import com.google.common.primitives.Doubles; import com.google.common.primitives.Ints; @@ -69,20 +70,20 @@ final class IndexingTests { @Test final void testIndicesToBits() { - List<Integer> indices = Ints.asList(0, 1, 7, 9); + int[] indices = { 0, 1, 7, 9 }; int capacity = 10; BitSet expect = new BitSet(capacity); for (int i : indices) { expect.set(i); } - assertEquals(expect, Indexing.indicesToBits(indices, capacity)); + assertEquals(expect, Indexing.indicesToBits(capacity, indices)); assertThrows(IllegalArgumentException.class, () -> { - Indexing.indicesToBits(Ints.asList(), -1); + Indexing.indicesToBits(-1, new int[0]); }); assertThrows(IndexOutOfBoundsException.class, () -> { - Indexing.indicesToBits(Ints.asList(10), 10); + Indexing.indicesToBits(5, new int[] { 10 }); }); } @@ -111,4 +112,13 @@ final class IndexingTests { assertArrayEquals(new int[] { -1, -1, -1 }, Indexing.maxIndex(D3_2)); } + @Test + final void testCheckInRange() { + Range<Integer> range = Range.closed(2, 5); + assertThrows(IllegalArgumentException.class, () -> { + Indexing.checkInRange(range, "test", 1); + }); + assertEquals(3, Indexing.checkInRange(range, "test", 3)); + } + } diff --git a/src/test/java/gov/usgs/earthquake/nshmp/data/InterpolatorTest.java b/src/test/java/gov/usgs/earthquake/nshmp/data/InterpolatorTest.java deleted file mode 100644 index d673b21c491d090f440076e36a61b561a08e3a47..0000000000000000000000000000000000000000 --- a/src/test/java/gov/usgs/earthquake/nshmp/data/InterpolatorTest.java +++ /dev/null @@ -1,185 +0,0 @@ -package gov.usgs.earthquake.nshmp.data; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.DoubleStream; - -import org.junit.jupiter.api.Test; - -class InterpolatorTest { - - static final double[] X = { 0.01, 0.1, 0.3, 0.5, 0.7, 0.9, 1.0 }; - static final double[] Y = { 1, 0.9, 0.7, 0.5, 0.3, 0.1, 1e-2 }; - - static final List<Double> X_LIST; - static final List<Double> Y_LIST; - - static final XySequence XY_SEQUENCE; - - static final double[] X_TARGET = { 0.01, 0.11, 0.4, 0.5, 0.8, 0.99, 1 }; - static final int[] LOWER_BIN_INDEX = { 0, 1, 2, 3, 4, 5, 5 }; - - // Expected results calculated in Excel - static final double[] Y_TARGET_LINX_LINY = { 1.00, 0.89, 0.60, 0.50, 0.20, 0.019, 0.01 }; - static final double[] Y_TARGET_LOGX_LINY = { - 1.00, 0.882648987129, 0.587365841075, 0.50, 0.193733604125, 0.018585096809, 0.01 }; - static final double[] Y_TARGET_LINX_LOGY = { - 1.00, 0.888761607855, 0.591607978310, 0.50, 0.173205080757, 0.012589254118, 0.01 }; - static final double[] Y_TARGET_LOGX_LOGY = { - 1.00, 0.880589847272, 0.579165919197, 0.50, 0.167344511862, 0.012456325964, 0.01 }; - - static final Interpolator LINEAR_LINEAR_INTERPOLATOR = Interpolator.builder() - .build(); - static final Interpolator LINEAR_LINEAR_DOWN_INTERPOLATOR = Interpolator.builder() - .decreasingX() - .build(); - static final Interpolator LINEAR_LOG_INTERPOLATOR = Interpolator.builder() - .logy() - .build(); - static final Interpolator LOG_LINEAR_INTERPOLATOR = Interpolator.builder() - .logx() - .build(); - static final Interpolator LOG_LOG_INTERPOLATOR = Interpolator.builder() - .logx() - .logy() - .build(); - - static final double TOL = 1e-10; - - static { - assert (X.length == Y.length); - assert (X_TARGET.length == LOWER_BIN_INDEX.length); - assert (X_TARGET.length == Y_TARGET_LINX_LINY.length); - assert (X_TARGET.length == Y_TARGET_LOGX_LINY.length); - assert (X_TARGET.length == Y_TARGET_LINX_LOGY.length); - assert (X_TARGET.length == Y_TARGET_LOGX_LOGY.length); - - X_LIST = DoubleStream.of(X).boxed().collect(Collectors.toCollection(ArrayList::new)); - Y_LIST = DoubleStream.of(Y).boxed().collect(Collectors.toCollection(ArrayList::new)); - XY_SEQUENCE = XySequence.create(X, Y); - } - - @Test - final void testFindX_DoubleDoubleDoubleDoubleDouble() { - for (int i = 0; i < X_TARGET.length; i++) { - int j = LOWER_BIN_INDEX[i]; - int k = j + 1; - double actualX = Interpolator.findX(X[j], Y[j], X[k], Y[k], Y_TARGET_LINX_LINY[i]); - assertEquals(X_TARGET[i], actualX, TOL); - } - } - - @Test - final void testFindY_DoubleDoubleDoubleDoubleDouble() { - for (int i = 0; i < X_TARGET.length; i++) { - int j = LOWER_BIN_INDEX[i]; - int k = j + 1; - double actualY = Interpolator.findY(X[j], Y[j], X[k], Y[k], X_TARGET[i]); - assertEquals(Y_TARGET_LINX_LINY[i], actualY, TOL); - } - } - - @Test - final void testFindX_DoubleArrayDoubleArrayDouble() { - for (int i = 0; i < X_TARGET.length; i++) { - // need to use an interpolator with decreasingX() since we're using the - // same data and swapping X & Y - double actualX = LINEAR_LINEAR_DOWN_INTERPOLATOR.findX(X, Y, Y_TARGET_LINX_LINY[i]); - assertEquals(X_TARGET[i], actualX, TOL); - } - } - - @Test - final void testFindX_ListOfDoubleListOfDoubleDouble() { - for (int i = 0; i < X_TARGET.length; i++) { - // need to use an interpolator with decreasingX() since we're using the - // same data and swapping X & Y - double actualX = LINEAR_LINEAR_DOWN_INTERPOLATOR.findX(X_LIST, Y_LIST, Y_TARGET_LINX_LINY[i]); - assertEquals(X_TARGET[i], actualX, TOL); - } - } - - @Test - final void testFindX_XySequenceDouble() { - for (int i = 0; i < X_TARGET.length; i++) { - // need to use an interpolator with decreasingX() since we're using the - // same data and swapping X & Y - double actualX = LINEAR_LINEAR_DOWN_INTERPOLATOR.findX(XY_SEQUENCE, Y_TARGET_LINX_LINY[i]); - assertEquals(X_TARGET[i], actualX, TOL); - } - } - - @Test - final void testFindY_DoubleArrayDoubleArrayDouble() { - for (int i = 0; i < X_TARGET.length; i++) { - double actualY = LINEAR_LINEAR_INTERPOLATOR.findY(X, Y, X_TARGET[i]); - assertEquals(Y_TARGET_LINX_LINY[i], actualY, TOL); - } - } - - @Test - final void testFindY_ListOfDoubleListOfDoubleDouble() { - for (int i = 0; i < X_TARGET.length; i++) { - double actualY = LINEAR_LINEAR_INTERPOLATOR.findY(X_LIST, Y_LIST, X_TARGET[i]); - assertEquals(Y_TARGET_LINX_LINY[i], actualY, TOL); - } - } - - @Test - final void testFindY_XySequenceDouble() { - for (int i = 0; i < X_TARGET.length; i++) { - double actualY = LINEAR_LINEAR_INTERPOLATOR.findY(XY_SEQUENCE, X_TARGET[i]); - assertEquals(Y_TARGET_LINX_LINY[i], actualY, TOL); - } - } - - @Test - final void testFindY_DoubleArrayDoubleArrayDoubleArray() { - double[] actual = LINEAR_LINEAR_INTERPOLATOR.findY(X, Y, X_TARGET); - assertArrayEquals(Y_TARGET_LINX_LINY, actual, TOL); - - actual = LOG_LINEAR_INTERPOLATOR.findY(X, Y, X_TARGET); - assertArrayEquals(Y_TARGET_LOGX_LINY, actual, TOL); - - actual = LINEAR_LOG_INTERPOLATOR.findY(X, Y, X_TARGET); - assertArrayEquals(Y_TARGET_LINX_LOGY, actual, TOL); - - actual = LOG_LOG_INTERPOLATOR.findY(X, Y, X_TARGET); - assertArrayEquals(Y_TARGET_LOGX_LOGY, actual, TOL); - } - - @Test - final void testFindY_ListOfDoubleListOfDoubleDoubleArray() { - double[] actual = LINEAR_LINEAR_INTERPOLATOR.findY(X_LIST, Y_LIST, X_TARGET); - assertArrayEquals(Y_TARGET_LINX_LINY, actual, TOL); - - actual = LOG_LINEAR_INTERPOLATOR.findY(X_LIST, Y_LIST, X_TARGET); - assertArrayEquals(Y_TARGET_LOGX_LINY, actual, TOL); - - actual = LINEAR_LOG_INTERPOLATOR.findY(X_LIST, Y_LIST, X_TARGET); - assertArrayEquals(Y_TARGET_LINX_LOGY, actual, TOL); - - actual = LOG_LOG_INTERPOLATOR.findY(X_LIST, Y_LIST, X_TARGET); - assertArrayEquals(Y_TARGET_LOGX_LOGY, actual, TOL); - } - - @Test - final void testFindY_XySequenceDoubleArray() { - double[] actual = LINEAR_LINEAR_INTERPOLATOR.findY(XY_SEQUENCE, X_TARGET); - assertArrayEquals(Y_TARGET_LINX_LINY, actual, TOL); - - actual = LOG_LINEAR_INTERPOLATOR.findY(XY_SEQUENCE, X_TARGET); - assertArrayEquals(Y_TARGET_LOGX_LINY, actual, TOL); - - actual = LINEAR_LOG_INTERPOLATOR.findY(XY_SEQUENCE, X_TARGET); - assertArrayEquals(Y_TARGET_LINX_LOGY, actual, TOL); - - actual = LOG_LOG_INTERPOLATOR.findY(XY_SEQUENCE, X_TARGET); - assertArrayEquals(Y_TARGET_LOGX_LOGY, actual, TOL); - } - -} diff --git a/src/test/java/gov/usgs/earthquake/nshmp/data/InterpolatorTests.java b/src/test/java/gov/usgs/earthquake/nshmp/data/InterpolatorTests.java index 33c2fe0bdb9ab2a0703cee3058659ff14cc059a8..2df2af71864716bb9e10ef2136e9f24610047b19 100644 --- a/src/test/java/gov/usgs/earthquake/nshmp/data/InterpolatorTests.java +++ b/src/test/java/gov/usgs/earthquake/nshmp/data/InterpolatorTests.java @@ -1,22 +1,246 @@ package gov.usgs.earthquake.nshmp.data; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.DoubleStream; + +import org.junit.jupiter.api.Test; + +import com.google.common.primitives.Doubles; + class InterpolatorTests { - /* - * Developer notes: - * - * How are single valued XySequences handled; should empty XySequences be - * allowed, probably not; singletons should be however; so how would this - * behave in interpolator if extrapolation is allowed for y-interpolation; - * answer: singletons shouldn't be allowed as arguments; it's just simpler - * - * add checkArgument(xys.size() > 1), test with XySeq.size = 1; add to docs - */ - - // @Test - // public void test() { - // - // fail("Not yet implemented"); - // } + static final double[] X = { 0.01, 0.1, 0.3, 0.5, 0.7, 0.9, 1.0 }; + static final double[] Y = { 1, 0.9, 0.7, 0.5, 0.3, 0.1, 1e-2 }; + + static final List<Double> X_LIST; + static final List<Double> Y_LIST; + + static final XySequence XY_SEQUENCE; + + static final double[] X_TARGET = { 0.01, 0.11, 0.4, 0.5, 0.8, 0.99, 1 }; + static final int[] LOWER_BIN_INDEX = { 0, 1, 2, 3, 4, 5, 5 }; + + // Expected results calculated in Excel + static final double[] Y_TARGET_LINX_LINY = { 1.00, 0.89, 0.60, 0.50, 0.20, 0.019, 0.01 }; + static final double[] Y_TARGET_LOGX_LINY = { + 1.00, 0.882648987129, 0.587365841075, 0.50, 0.193733604125, 0.018585096809, 0.01 }; + static final double[] Y_TARGET_LINX_LOGY = { + 1.00, 0.888761607855, 0.591607978310, 0.50, 0.173205080757, 0.012589254118, 0.01 }; + static final double[] Y_TARGET_LOGX_LOGY = { + 1.00, 0.880589847272, 0.579165919197, 0.50, 0.167344511862, 0.012456325964, 0.01 }; + + static final Interpolator LINEAR_LINEAR_INTERPOLATOR = Interpolator.builder() + .build(); + static final Interpolator LINEAR_LINEAR_DOWN_INTERPOLATOR = Interpolator.builder() + .decreasingY() + .build(); + static final Interpolator LINEAR_LOG_INTERPOLATOR = Interpolator.builder() + .logy() + .build(); + static final Interpolator LOG_LINEAR_INTERPOLATOR = Interpolator.builder() + .logx() + .build(); + static final Interpolator LOG_LOG_INTERPOLATOR = Interpolator.builder() + .logx() + .logy() + .build(); + + static final double TOL = 1e-10; + + static { + assert (X.length == Y.length); + assert (X_TARGET.length == LOWER_BIN_INDEX.length); + assert (X_TARGET.length == Y_TARGET_LINX_LINY.length); + assert (X_TARGET.length == Y_TARGET_LOGX_LINY.length); + assert (X_TARGET.length == Y_TARGET_LINX_LOGY.length); + assert (X_TARGET.length == Y_TARGET_LOGX_LOGY.length); + + X_LIST = DoubleStream.of(X).boxed().collect(Collectors.toCollection(ArrayList::new)); + Y_LIST = DoubleStream.of(Y).boxed().collect(Collectors.toCollection(ArrayList::new)); + XY_SEQUENCE = XySequence.create(X, Y); + } + + @Test + final void testFindX_DoubleDoubleDoubleDoubleDouble() { + for (int i = 0; i < X_TARGET.length; i++) { + int j = LOWER_BIN_INDEX[i]; + int k = j + 1; + double actualX = Interpolator.findX(X[j], Y[j], X[k], Y[k], Y_TARGET_LINX_LINY[i]); + assertEquals(X_TARGET[i], actualX, TOL); + } + } + + @Test + final void testFindY_DoubleDoubleDoubleDoubleDouble() { + for (int i = 0; i < X_TARGET.length; i++) { + int j = LOWER_BIN_INDEX[i]; + int k = j + 1; + double actualY = Interpolator.findY(X[j], Y[j], X[k], Y[k], X_TARGET[i]); + assertEquals(Y_TARGET_LINX_LINY[i], actualY, TOL); + } + } + + @Test + final void testFindX_DoubleArrayDoubleArrayDouble() { + for (int i = 0; i < X_TARGET.length; i++) { + // need to use an interpolator with decreasingY() since we're using the + // same data and swapping X & Y + double actualX = LINEAR_LINEAR_DOWN_INTERPOLATOR.findX(X, Y, Y_TARGET_LINX_LINY[i]); + assertEquals(X_TARGET[i], actualX, TOL); + } + } + + @Test + final void testFindX_ListOfDoubleListOfDoubleDouble() { + for (int i = 0; i < X_TARGET.length; i++) { + // need to use an interpolator with decreasingY() since we're using the + // same data and swapping X & Y + double actualX = LINEAR_LINEAR_DOWN_INTERPOLATOR.findX(X_LIST, Y_LIST, Y_TARGET_LINX_LINY[i]); + assertEquals(X_TARGET[i], actualX, TOL); + } + } + + @Test + final void testFindX_XySequenceDouble() { + for (int i = 0; i < X_TARGET.length; i++) { + // need to use an interpolator with decreasingY() since we're using the + // same data and swapping X & Y + double actualX = LINEAR_LINEAR_DOWN_INTERPOLATOR.findX(XY_SEQUENCE, Y_TARGET_LINX_LINY[i]); + assertEquals(X_TARGET[i], actualX, TOL); + } + } + + @Test + final void testFindY_DoubleArrayDoubleArrayDouble() { + for (int i = 0; i < X_TARGET.length; i++) { + double actualY = LINEAR_LINEAR_INTERPOLATOR.findY(X, Y, X_TARGET[i]); + assertEquals(Y_TARGET_LINX_LINY[i], actualY, TOL); + } + } + + @Test + final void testFindY_ListOfDoubleListOfDoubleDouble() { + for (int i = 0; i < X_TARGET.length; i++) { + double actualY = LINEAR_LINEAR_INTERPOLATOR.findY(X_LIST, Y_LIST, X_TARGET[i]); + assertEquals(Y_TARGET_LINX_LINY[i], actualY, TOL); + } + } + + @Test + final void testFindY_XySequenceDouble() { + for (int i = 0; i < X_TARGET.length; i++) { + double actualY = LINEAR_LINEAR_INTERPOLATOR.findY(XY_SEQUENCE, X_TARGET[i]); + assertEquals(Y_TARGET_LINX_LINY[i], actualY, TOL); + } + } + + @Test + final void testFindXin_DoubleArrayDoubleArrayDoubleArray() { + + double[] xs = { 1, 2, 3 }; + double[] ys = { 0.2, 0.5, 0.8 }; + + /* Missing coverage for findX functions (XFn) */ + + double expected = 1.259921049894; + double actual = LOG_LINEAR_INTERPOLATOR.findX(xs, ys, 0.3); + assertEquals(expected, actual, TOL); + + expected = 1.442507049349; + actual = LINEAR_LOG_INTERPOLATOR.findX(xs, ys, 0.3); + assertEquals(expected, actual, TOL); + + expected = 1.358963821816; + actual = LOG_LOG_INTERPOLATOR.findX(xs, ys, 0.3); + assertEquals(expected, actual, TOL); + } + + @Test + final void testFindY_DoubleArrayDoubleArrayDoubleArray() { + double[] actual = LINEAR_LINEAR_INTERPOLATOR.findY(X, Y, X_TARGET); + assertArrayEquals(Y_TARGET_LINX_LINY, actual, TOL); + + actual = LOG_LINEAR_INTERPOLATOR.findY(X, Y, X_TARGET); + assertArrayEquals(Y_TARGET_LOGX_LINY, actual, TOL); + + actual = LINEAR_LOG_INTERPOLATOR.findY(X, Y, X_TARGET); + assertArrayEquals(Y_TARGET_LINX_LOGY, actual, TOL); + + actual = LOG_LOG_INTERPOLATOR.findY(X, Y, X_TARGET); + assertArrayEquals(Y_TARGET_LOGX_LOGY, actual, TOL); + } + + @Test + final void testFindY_ListOfDoubleListOfDoubleDoubleArray() { + double[] actual = LINEAR_LINEAR_INTERPOLATOR.findY(X_LIST, Y_LIST, X_TARGET); + assertArrayEquals(Y_TARGET_LINX_LINY, actual, TOL); + + actual = LOG_LINEAR_INTERPOLATOR.findY(X_LIST, Y_LIST, X_TARGET); + assertArrayEquals(Y_TARGET_LOGX_LINY, actual, TOL); + + actual = LINEAR_LOG_INTERPOLATOR.findY(X_LIST, Y_LIST, X_TARGET); + assertArrayEquals(Y_TARGET_LINX_LOGY, actual, TOL); + + actual = LOG_LOG_INTERPOLATOR.findY(X_LIST, Y_LIST, X_TARGET); + assertArrayEquals(Y_TARGET_LOGX_LOGY, actual, TOL); + } + + @Test + final void testFindY_XySequenceDoubleArray() { + double[] actual = LINEAR_LINEAR_INTERPOLATOR.findY(XY_SEQUENCE, X_TARGET); + assertArrayEquals(Y_TARGET_LINX_LINY, actual, TOL); + + actual = LOG_LINEAR_INTERPOLATOR.findY(XY_SEQUENCE, X_TARGET); + assertArrayEquals(Y_TARGET_LOGX_LINY, actual, TOL); + + actual = LINEAR_LOG_INTERPOLATOR.findY(XY_SEQUENCE, X_TARGET); + assertArrayEquals(Y_TARGET_LINX_LOGY, actual, TOL); + + actual = LOG_LOG_INTERPOLATOR.findY(XY_SEQUENCE, X_TARGET); + assertArrayEquals(Y_TARGET_LOGX_LOGY, actual, TOL); + } + + /* Errors and Edge Cases */ + @Test + final void testErrorsAndEdgeCases() { + + double[] xs = { 2, 3, 4 }; + double[] ys = { 0.2, 0.5, 0.8 }; + Interpolator interp = Interpolator.builder().build(); + + /* linear index */ + + double yBelow = 0.1; + double yAbove = 0.9; + + // out of range below + double actual = interp.findX(xs, ys, yBelow); + assertEquals(0.0, actual, 0.0); + actual = interp.findX( + Doubles.asList(xs), + Doubles.asList(ys), yBelow); + assertEquals(0.0, actual, 0.0); + + // out of range above + actual = interp.findX(xs, ys, yAbove); + assertEquals(0.0, actual, 0.0); + actual = interp.findX( + Doubles.asList(xs), + Doubles.asList(ys), yAbove); + assertEquals(0.0, actual, 0.0); + + /* binary index */ + + // out of range below + double xBelow = 1.0; + actual = interp.findY(xs, ys, xBelow); + assertEquals(-0.1, actual, TOL); + + } } diff --git a/src/test/java/gov/usgs/earthquake/nshmp/data/IntervalArrayTests.java b/src/test/java/gov/usgs/earthquake/nshmp/data/IntervalArrayTests.java index cde08292ef446a4effe24aa2a25582dc35e9d8c3..fa06779a530127cec9c0a6e5138439bdd5ed976a 100644 --- a/src/test/java/gov/usgs/earthquake/nshmp/data/IntervalArrayTests.java +++ b/src/test/java/gov/usgs/earthquake/nshmp/data/IntervalArrayTests.java @@ -165,29 +165,42 @@ class IntervalArrayTests { IntervalArray actualA = Builder.copyOf(ARRAY) .add(ROWS[index], ROWS[index]) .build(); - assertEquals(expected, actualA.get(index), 0); // add(int row, double value) IntervalArray actualB = Builder.copyOf(ARRAY) .add(index, ROWS[index]) .build(); - assertEquals(expected, actualB.get(index), 0); // add(double[] values) IntervalArray actualC = Builder.copyOf(ARRAY) .add(ROWS) .build(); - assertArrayEquals(expectedArray, actualC.values().yValues().toArray(), 0); - // add(IntervalArray array) + // add(XySequence values) + XySequence xy = ARRAY.values(); IntervalArray actualD = Builder.copyOf(ARRAY) + .add(xy) + .build(); + assertArrayEquals(expectedArray, actualD.values().yValues().toArray(), 0); + + // add(IntervalArray array) + IntervalArray actualE = Builder.copyOf(ARRAY) .add(ARRAY) .build(); + assertArrayEquals(expectedArray, actualE.values().yValues().toArray(), 0); + } - assertArrayEquals(expectedArray, actualD.values().yValues().toArray(), 0); + @Test + final void builderSetData() { + double[] data = { 5, 4, 3, 2 }; + IntervalArray ia = Builder.copyOf(ARRAY) + .data(data) + .build(); + // really should be assertSame, but data is not visible + assertArrayEquals(data, ia.values().yValues().toArray()); } @Test @@ -204,4 +217,13 @@ class IntervalArrayTests { assertArrayEquals(expected, actual, 0); } + @Test + final void builderBuiltTests() { + assertThrows(IllegalStateException.class, () -> { + Builder b = Builder.copyOf(ARRAY); + b.build(); + b.build(); + }); + } + } diff --git a/src/test/java/gov/usgs/earthquake/nshmp/data/IntervalTableTests.java b/src/test/java/gov/usgs/earthquake/nshmp/data/IntervalTableTests.java index 13cf82c12f99702e4d409907ea65f29ccd7432f8..63d736292937baea8f4edf3cd3fc174761a8440b 100644 --- a/src/test/java/gov/usgs/earthquake/nshmp/data/IntervalTableTests.java +++ b/src/test/java/gov/usgs/earthquake/nshmp/data/IntervalTableTests.java @@ -127,6 +127,18 @@ class IntervalTableTests { assertTrue(expected.equals(actual)); } + @Test + final void builderInitTests() { + // test branch where columns have been initialized but not rows. + Builder builder = new Builder() + .columns(COL_MIN, COL_MAX, Δ) + .rows(ROW_MIN, ROW_MAX, Δ); + Arrays.stream(ROWS).forEach(row -> builder.add(row, COLS)); + IntervalTable table = builder.build(); + assertEquals(table.rows(), TABLE.rows()); + assertEquals(table.columns(), TABLE.columns()); + } + @Test final void builderCopyOfTests() { IntervalTable actual = Builder.copyOf(TABLE).build(); @@ -174,24 +186,83 @@ class IntervalTableTests { assertEquals(expected, actualCol); } + @Test + final void builderSetTests() { + int index = 0; + double expected = 1.25; + IntervalTable actual = Builder.copyOf(TABLE) + .set(ROWS[index], COLS[index], expected) + .build(); + + assertEquals(expected, actual.get(ROWS[index], COLS[index]), 0); + assertEquals(expected, actual.get(index, index), 0); + } + @Test final void builderAddTests() { + + // add(double row, double column, double value) and + // add(int row, int column, double value) int index = 0; + double addValue = 1.25; + double expect = 5.75; + IntervalTable actual = Builder.copyOf(TABLE) + .add(ROWS[index], COLS[index], addValue) + .build(); + + assertEquals(expect, actual.get(ROWS[index], COLS[index]), 0); + assertEquals(expect, actual.get(index, index), 0); + double[] expected = Arrays.stream(COLS).map(x -> x + x).toArray(); // add(double row, double[] values) IntervalTable actualA = Builder.copyOf(TABLE) .add(ROWS[index], COLS) .build(); - assertArrayEquals(expected, actualA.row(index).yValues().toArray(), 0); // add(double row, XySequence values) IntervalTable actualB = Builder.copyOf(TABLE) .add(ROWS[index], XY) .build(); - assertArrayEquals(expected, actualB.row(index).yValues().toArray(), 0); + + // add(IntervalTable) + IntervalTable tableToAdd = Builder.copyOf(TABLE).build(); + IntervalTable actualD = Builder.copyOf(TABLE) + .add(tableToAdd) + .build(); + assertEquals(9.0, actualD.get(0, 0), 0.0); + } + + @Test + final void builderSetData() { + double[][] data = { { 5, 4, 3, 2 }, { 6, 7, 8, 9 }, { 1, 0, -1, -2 } }; + IntervalTable ia = Builder.copyOf(TABLE) + .data(data) + .build(); + // really should be assertSame, but data is not visible + assertArrayEquals(data[0], ia.row(0).yValues().toArray()); + assertArrayEquals(data[1], ia.row(1).yValues().toArray()); + assertArrayEquals(data[2], ia.row(2).yValues().toArray()); + } + + @Test + final void builderMultiplyTests() { + IntervalTable actual = Builder.copyOf(TABLE) + .multiply(3.0) + .build(); + assertEquals(13.5, actual.get(0, 0)); + assertEquals(22.5, actual.get(3, 3)); + } + + @Test + final void builderBuiltTests() { + assertThrows(IllegalStateException.class, () -> { + Builder b = Builder.copyOf(TABLE); + b.build(); + b.build(); + }); } } diff --git a/src/test/java/gov/usgs/earthquake/nshmp/data/IntervalVolumeTests.java b/src/test/java/gov/usgs/earthquake/nshmp/data/IntervalVolumeTests.java index b5d4a3894d35e2ba0bf03c9afb79495bfd9f37fc..6c95f61362c04cc95bb60e6dc180aab305189ad0 100644 --- a/src/test/java/gov/usgs/earthquake/nshmp/data/IntervalVolumeTests.java +++ b/src/test/java/gov/usgs/earthquake/nshmp/data/IntervalVolumeTests.java @@ -30,6 +30,7 @@ class IntervalVolumeTests { static final double[] LVLS = IntervalData.keys(LVL_MIN, LVL_MAX, Δ); static final IntervalVolume VOLUME; + static final XySequence XY = XySequence.create(COLS, LVLS); static { Builder builder = new Builder() @@ -164,6 +165,19 @@ class IntervalVolumeTests { assertTrue(expected.equals(actual)); } + @Test + final void builderInitTests() { + // test branch where columns have been initialized but not rows. + IntervalVolume iv = new Builder() + .columns(COL_MIN, COL_MAX, Δ) + .rows(ROW_MIN, ROW_MAX, Δ) + .levels(LVL_MIN, LVL_MAX, Δ) + .build(); + assertEquals(iv.rows(), VOLUME.rows()); + assertEquals(iv.columns(), VOLUME.columns()); + assertEquals(iv.levels(), VOLUME.levels()); + } + @Test final void builderCopyOfTests() { IntervalVolume actual = Builder.copyOf(VOLUME).build(); @@ -225,10 +239,23 @@ class IntervalVolumeTests { assertEquals(expected, actualLvl); } + @Test + final void builderSetTests() { + int i = 0; + double expected = 1.25; + IntervalVolume actual = Builder.copyOf(VOLUME) + .set(ROWS[i], COLS[i], LVLS[i], expected) + .build(); + + assertEquals(expected, actual.get(ROWS[i], COLS[i], LVLS[i]), 0); + assertEquals(expected, actual.get(i, i, i), 0); + } + @Test final void builderAddTests() { int index = 0; + // add by index Builder builder = Builder.copyOf(VOLUME); IntStream.range(0, LVLS.length).forEach(lvlIndex -> { @@ -241,6 +268,70 @@ class IntervalVolumeTests { double expected = LVLS[lvlIndex] * 2; assertEquals(expected, actual.get(index, index, lvlIndex), 0); }); + + // add(double row, double column, double[] values) + double[] expectedA = DoubleData.multiply(2, Arrays.copyOf(LVLS, LVLS.length)); + IntervalVolume actualA = Builder.copyOf(VOLUME) + .add(ROWS[index], COLS[index], LVLS) + .build(); + assertArrayEquals(expectedA, actualA.column(index, index).yValues().toArray(), 0); + + // add(double row, double column, XySequence values) + double[] expectedB = DoubleData.multiply(2, Arrays.copyOf(LVLS, LVLS.length)); + IntervalVolume actualB = Builder.copyOf(VOLUME) + .add(ROWS[index], COLS[index], XY) + .build(); + assertArrayEquals(expectedB, actualB.column(index, index).yValues().toArray(), 0); + + // add by value + IntervalVolume iv = Builder.copyOf(VOLUME) + .add(4.7, 5.3, 8.7, 42.0) + .build(); + assertEquals(50.5, iv.get(2, 1, 2), 0.0); + + // add(IntervalVolume) + IntervalVolume volumeToAdd = Builder.copyOf(VOLUME).build(); + IntervalVolume actual2 = Builder.copyOf(VOLUME) + .add(volumeToAdd) + .build(); + assertEquals(13.0, actual2.get(0, 0, 0), 0.0); + assertEquals(19.0, actual2.get(3, 3, 3), 0.0); + } + + @Test + final void builderSetData() { + double[][][] data = { + { { 5, 4, 3 }, { 6, 7, 8 }, { 1, 0, -1 } }, + { { 5, 4, 3 }, { 6, 7, 8 }, { 1, 0, -1 } }, + { { 5, 4, 3 }, { 6, 7, 8 }, { 1, 0, -1 } } + }; + + IntervalVolume iv = Builder.copyOf(VOLUME) + .data(data) + .build(); + + // really should be assertSame, but data is not visible + assertArrayEquals(data[0][0], iv.column(0, 0).yValues().toArray()); + assertArrayEquals(data[1][1], iv.column(1, 1).yValues().toArray()); + assertArrayEquals(data[2][2], iv.column(2, 2).yValues().toArray()); + } + + @Test + final void builderMultiplyTests() { + IntervalVolume actual = Builder.copyOf(VOLUME) + .multiply(3.0) + .build(); + assertEquals(19.5, actual.get(0, 0, 0)); + assertEquals(28.5, actual.get(3, 3, 3)); + } + + @Test + final void builderBuiltTests() { + assertThrows(IllegalStateException.class, () -> { + Builder b = Builder.copyOf(VOLUME); + b.build(); + b.build(); + }); } } diff --git a/src/test/java/gov/usgs/earthquake/nshmp/data/SequencesTests.java b/src/test/java/gov/usgs/earthquake/nshmp/data/SequencesTests.java index 185e9137a355b0beb40833886953475274dc4972..e70d8a6dc67ae5a64facaa8eb4a76d171976ce15 100644 --- a/src/test/java/gov/usgs/earthquake/nshmp/data/SequencesTests.java +++ b/src/test/java/gov/usgs/earthquake/nshmp/data/SequencesTests.java @@ -1,10 +1,13 @@ package gov.usgs.earthquake.nshmp.data; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import java.util.Arrays; +import java.util.Optional; import org.junit.jupiter.api.Test; @@ -12,10 +15,80 @@ import com.google.common.primitives.Doubles; class SequencesTests { + @Test + final void validateArraysTests() { + // empty xs array + assertThrows(IllegalArgumentException.class, () -> { + Sequences.validateArrays(new double[0], new double[0]); + }); + // xs and ys different sizes + assertThrows(IllegalArgumentException.class, () -> { + Sequences.validateArrays(new double[2], new double[3]); + }); + // xs not monotonic + assertThrows(IllegalArgumentException.class, () -> { + Sequences.validateArrays(new double[] { 1, 0 }, new double[2]); + }); + // xs not monotonic + assertDoesNotThrow(() -> { + Sequences.validateArrays(new double[] { 0, 1 }, new double[2]); + }); + // single values makes it through monotonic test + assertDoesNotThrow(() -> { + Sequences.validateArrays(new double[1], new double[1]); + }); + } + + @Test + final void trimTests() { + double[] xs1 = new double[1]; + double[] ys1 = new double[1]; + + double[] xs3 = { 1, 2, 3 }; + double[] ys3zero = new double[3]; + double[] ys3ones = { 1, 1, 1 }; + + // size and clear check + ArrayXySequence xy1 = (ArrayXySequence) XySequence.create(xs1, ys1); + assertSame(xy1, Sequences.trim(xy1)); + ArrayXySequence xy3a = (ArrayXySequence) XySequence.create(xs3, ys3zero); + assertSame(xy3a, Sequences.trim(xy3a)); + + // start and end y values > 0 + ArrayXySequence xy3b = (ArrayXySequence) XySequence.create(xs3, ys3ones); + assertSame(xy3b, Sequences.trim(xy3b)); + + double[] xs4toTrim = { 1, 2, 3, 4 }; + double[] ys4toTrim = { 1, 1, 1, 0 }; + double[] xs4expect = { 1, 2, 3 }; + double[] ys4expect = { 1, 1, 1 }; + + // extra test that gets upper end y = 0 + ArrayXySequence xy4toTrim = (ArrayXySequence) XySequence.create(xs4toTrim, ys4toTrim); + ArrayXySequence xy4expect = (ArrayXySequence) XySequence.create(xs4expect, ys4expect); + assertEquals(xy4expect, Sequences.trim(xy4toTrim)); + + double[] xs5toTrim = { 1, 2, 3, 4, 5 }; + double[] ys5toTrim = { 0, 1, 1, 1, 0 }; + double[] xs5expect = { 2, 3, 4 }; + double[] ys5expect = { 1, 1, 1 }; + + // immutable trimmed + ArrayXySequence xy5toTrim = (ArrayXySequence) XySequence.create(xs5toTrim, ys5toTrim); + ArrayXySequence xy5expect = (ArrayXySequence) XySequence.create(xs5expect, ys5expect); + assertEquals(xy5expect, Sequences.trim(xy5toTrim)); + + // mutable trimmed + MutableArrayXySequence xy5toTrimMutable = (MutableArrayXySequence) MutableXySequence.create( + xs5toTrim, + Optional.of(ys5toTrim)); + assertEquals(xy5expect, Sequences.trim(xy5toTrimMutable)); + } + @Test final void nonZeroIndexTests() { - double[] values = new double[] { 0.0, 0.0, 1.0, 1.0, 0.0, 0.0 }; - double[] zeros = new double[] { 0.0 }; + double[] values = { 0.0, 0.0, 1.0, 1.0, 0.0, 0.0 }; + double[] zeros = { 0.0 }; int first = 2; int last = 3; @@ -25,11 +98,22 @@ class SequencesTests { assertEquals(-1, Sequences.lastNonZeroIndex(zeros), 0.0); } + @Test + final void toCumulativeTests() { + double[] xs = { 1, 2, 3, 4, 5 }; + double[] ys = { 1, 1, 1, 1, 1 }; + double[] ysExpect = { 5, 4, 3, 2, 1 }; + + XySequence xyActual = Sequences.toCumulative(XySequence.create(xs, ys)); + XySequence xyExpect = XySequence.create(xs, ysExpect); + assertEquals(xyExpect, xyActual); + } + @Test final void arrayBuilderTests() { double[] expected = - new double[] { 5.05, 5.1499999999999995, 5.25, 5.35, 5.45, 5.55, 5.65, 5.75 }; + { 5.05, 5.1499999999999995, 5.25, 5.35, 5.45, 5.55, 5.65, 5.75 }; double[] actual = Sequences.arrayBuilder(5.05, 5.83, 0.1).centered().build(); assertArrayEquals(expected, actual); @@ -58,7 +142,7 @@ class SequencesTests { assertArrayEquals(expected, actual); double maxNoRound = 6.7999999; // 2 value array expected - double[] expectNoRound = new double[] { 6.5, 6.65 }; + double[] expectNoRound = { 6.5, 6.65 }; double[] actualNoRound = Sequences.arrayBuilder(6.5, maxNoRound, 0.15) .scale(5) .centered() @@ -66,7 +150,7 @@ class SequencesTests { assertArrayEquals(expectNoRound, actualNoRound); double maxRound = 6.79999999; // 3 value array expected - double[] expectRound = new double[] { 6.5, 6.65, 6.8 }; + double[] expectRound = { 6.5, 6.65, 6.8 }; double[] actualRound = Sequences.arrayBuilder(6.5, maxRound, 0.15) .scale(5) .centered() diff --git a/src/test/java/gov/usgs/earthquake/nshmp/data/XySequenceTests.java b/src/test/java/gov/usgs/earthquake/nshmp/data/XySequenceTests.java index 77f7a2c7d1ec874b72cf6909701d8e5da3a38e35..1c3fc403e677d2abb631500e15dad28ad83095c5 100644 --- a/src/test/java/gov/usgs/earthquake/nshmp/data/XySequenceTests.java +++ b/src/test/java/gov/usgs/earthquake/nshmp/data/XySequenceTests.java @@ -98,6 +98,14 @@ class XySequenceTests { }); } + @Test + final void pointIndexTest() { + int index = 0; + for (XyPoint p : xy) { + assertEquals(index++, p.index()); + } + } + @Test final void xyTest() { for (int index = 0; index < xy.size(); index++) {