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++) {