You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datasketches.apache.org by le...@apache.org on 2023/03/22 22:31:00 UTC

[datasketches-java] 01/01: I discovered another bug in the design of the method getQuantiles(evenlySpaced).

This is an automated email from the ASF dual-hosted git repository.

leerho pushed a commit to branch fix_evenSpacebug
in repository https://gitbox.apache.org/repos/asf/datasketches-java.git

commit c584df5ba1c263a6d0e2d110d0f17cc76cad2752
Author: Lee Rhodes <le...@users.noreply.github.com>
AuthorDate: Wed Mar 22 12:54:10 2023 -0700

    I discovered another bug in the design of the method
    getQuantiles(evenlySpaced).
    
    The general method in QuantilesUtil.evenlySpacedFloats which is used in
    several places in the library was also being used in the
    getQuantiles(evenlySpaced) method.  It turns out that for the
    getQuantiles, the evenlySpaced algo must omit 0.0, which the general
    method does not.  It really needs its own generator for the evenly
    spaced ranks.
    
    Given that I fixed this issue, the documentation was also not correct,
    so the javadocs had to be revised, again.
---
 .../apache/datasketches/kll/KllDoublesSketch.java  |  3 +-
 .../apache/datasketches/kll/KllFloatsSketch.java   |  3 +-
 .../datasketches/quantiles/DoublesSketch.java      |  3 +-
 .../apache/datasketches/quantiles/ItemsSketch.java |  3 +-
 .../quantilescommon/QuantilesDoublesAPI.java       | 27 ++++++------
 .../quantilescommon/QuantilesFloatsAPI.java        | 27 ++++++------
 .../quantilescommon/QuantilesGenericAPI.java       | 27 ++++++------
 .../quantilescommon/QuantilesUtil.java             | 50 ++++++++++++++--------
 .../org/apache/datasketches/req/BaseReqSketch.java |  3 +-
 .../kll/KllDirectDoublesSketchTest.java            |  9 ++--
 .../kll/KllDirectFloatsSketchTest.java             |  9 ++--
 .../datasketches/kll/KllDoublesSketchTest.java     |  9 ++--
 .../datasketches/kll/KllFloatsSketchTest.java      |  9 ++--
 .../quantiles/HeapUpdateDoublesSketchTest.java     |  7 +--
 .../datasketches/quantiles/ItemsSketchTest.java    |  4 +-
 .../quantilescommon/QuantilesUtilTest.java         | 32 ++++++++++----
 .../org/apache/datasketches/req/ReqSketchTest.java |  3 +-
 17 files changed, 133 insertions(+), 95 deletions(-)

diff --git a/src/main/java/org/apache/datasketches/kll/KllDoublesSketch.java b/src/main/java/org/apache/datasketches/kll/KllDoublesSketch.java
index da110095..8c469b82 100644
--- a/src/main/java/org/apache/datasketches/kll/KllDoublesSketch.java
+++ b/src/main/java/org/apache/datasketches/kll/KllDoublesSketch.java
@@ -26,6 +26,7 @@ import static org.apache.datasketches.kll.KllSketch.Error.MUST_NOT_BE_UPDATABLE_
 import static org.apache.datasketches.kll.KllSketch.Error.MUST_NOT_CALL;
 import static org.apache.datasketches.kll.KllSketch.Error.TGT_IS_READ_ONLY;
 import static org.apache.datasketches.kll.KllSketch.Error.kllSketchThrow;
+import static org.apache.datasketches.quantilescommon.QuantilesUtil.evenlySpacedRanks;
 import static org.apache.datasketches.quantilescommon.QuantilesUtil.THROWS_EMPTY;
 
 import java.util.Objects;
@@ -215,7 +216,7 @@ public abstract class KllDoublesSketch extends KllSketch implements QuantilesDou
   @Override
   public double[] getQuantiles(final int numEvenlySpaced, final QuantileSearchCriteria searchCrit) {
     if (isEmpty()) { throw new IllegalArgumentException(THROWS_EMPTY); }
-    return getQuantiles(org.apache.datasketches.quantilescommon.QuantilesUtil.evenlySpaced(0.0, 1.0, numEvenlySpaced),
+    return getQuantiles(evenlySpacedRanks(numEvenlySpaced),
         searchCrit);
   }
 
diff --git a/src/main/java/org/apache/datasketches/kll/KllFloatsSketch.java b/src/main/java/org/apache/datasketches/kll/KllFloatsSketch.java
index d012d0c5..5da842ac 100644
--- a/src/main/java/org/apache/datasketches/kll/KllFloatsSketch.java
+++ b/src/main/java/org/apache/datasketches/kll/KllFloatsSketch.java
@@ -26,6 +26,7 @@ import static org.apache.datasketches.kll.KllSketch.Error.MUST_NOT_BE_UPDATABLE_
 import static org.apache.datasketches.kll.KllSketch.Error.MUST_NOT_CALL;
 import static org.apache.datasketches.kll.KllSketch.Error.TGT_IS_READ_ONLY;
 import static org.apache.datasketches.kll.KllSketch.Error.kllSketchThrow;
+import static org.apache.datasketches.quantilescommon.QuantilesUtil.evenlySpacedRanks;
 import static org.apache.datasketches.quantilescommon.QuantilesUtil.THROWS_EMPTY;
 
 import java.util.Objects;
@@ -215,7 +216,7 @@ public abstract class KllFloatsSketch extends KllSketch implements QuantilesFloa
   @Override
   public float[] getQuantiles(final int numEvenlySpaced, final QuantileSearchCriteria searchCrit) {
     if (isEmpty()) { throw new IllegalArgumentException(THROWS_EMPTY); }
-    return getQuantiles(org.apache.datasketches.quantilescommon.QuantilesUtil.evenlySpaced(0.0, 1.0, numEvenlySpaced),
+    return getQuantiles(evenlySpacedRanks(numEvenlySpaced),
         searchCrit);
   }
 
diff --git a/src/main/java/org/apache/datasketches/quantiles/DoublesSketch.java b/src/main/java/org/apache/datasketches/quantiles/DoublesSketch.java
index 3a982572..440426a7 100644
--- a/src/main/java/org/apache/datasketches/quantiles/DoublesSketch.java
+++ b/src/main/java/org/apache/datasketches/quantiles/DoublesSketch.java
@@ -23,6 +23,7 @@ import static java.lang.Math.max;
 import static java.lang.Math.min;
 import static org.apache.datasketches.common.Util.ceilingIntPowerOf2;
 import static org.apache.datasketches.quantiles.ClassicUtil.checkIsCompactMemory;
+import static org.apache.datasketches.quantilescommon.QuantilesUtil.evenlySpacedRanks;
 import static org.apache.datasketches.quantilescommon.QuantilesUtil.THROWS_EMPTY;
 
 import java.util.Random;
@@ -200,7 +201,7 @@ public abstract class DoublesSketch implements QuantilesDoublesAPI {
   @Override
   public double[] getQuantiles(final int numEvenlySpaced, final QuantileSearchCriteria searchCrit) {
     if (isEmpty()) { throw new IllegalArgumentException(THROWS_EMPTY); }
-    return getQuantiles(org.apache.datasketches.quantilescommon.QuantilesUtil.evenlySpaced(0.0, 1.0, numEvenlySpaced),
+    return getQuantiles(evenlySpacedRanks(numEvenlySpaced),
         searchCrit);
   }
 
diff --git a/src/main/java/org/apache/datasketches/quantiles/ItemsSketch.java b/src/main/java/org/apache/datasketches/quantiles/ItemsSketch.java
index 3df49447..f1d05cad 100644
--- a/src/main/java/org/apache/datasketches/quantiles/ItemsSketch.java
+++ b/src/main/java/org/apache/datasketches/quantiles/ItemsSketch.java
@@ -31,6 +31,7 @@ import static org.apache.datasketches.quantiles.PreambleUtil.extractN;
 import static org.apache.datasketches.quantiles.PreambleUtil.extractPreLongs;
 import static org.apache.datasketches.quantiles.PreambleUtil.extractSerVer;
 import static org.apache.datasketches.quantilescommon.QuantileSearchCriteria.INCLUSIVE;
+import static org.apache.datasketches.quantilescommon.QuantilesUtil.evenlySpacedRanks;
 import static org.apache.datasketches.quantilescommon.QuantilesUtil.THROWS_EMPTY;
 
 import java.lang.reflect.Array;
@@ -544,7 +545,7 @@ public final class ItemsSketch<T> implements QuantilesAPI {
    */
   public T[] getQuantiles(final int numEvenlySpaced, final QuantileSearchCriteria searchCrit) {
     if (isEmpty()) { throw new IllegalArgumentException(THROWS_EMPTY); }
-    return getQuantiles(org.apache.datasketches.quantilescommon.QuantilesUtil.evenlySpaced(0.0, 1.0, numEvenlySpaced),
+    return getQuantiles(evenlySpacedRanks(numEvenlySpaced),
         searchCrit);
   }
 
diff --git a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesDoublesAPI.java b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesDoublesAPI.java
index e4083567..3868e413 100644
--- a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesDoublesAPI.java
+++ b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesDoublesAPI.java
@@ -235,24 +235,25 @@ public interface QuantilesDoublesAPI extends QuantilesAPI {
   }
 
   /**
-   * This is a version of getQuantiles() where the caller only specifies the number of of desired evenly spaced,
-   * normalized ranks, and returns an array of the corresponding quantiles.
+   * This is a version of getQuantiles() where the caller only specifies the number of of desired quantile intervals 
+   * that are computed from an array of evenly spaced normalized ranks between 0 and 1.0 determined from the given
+   * <i>numEvenlySpaced</i> parameter.
    *
-   * @param numEvenlySpaced an integer that specifies the number of evenly spaced normalized ranks.
-   * This must be a positive integer greater than 0.
-   * <ul><li>Let <i>Smallest</i> and <i>Largest</i> be the smallest and largest quantiles
-   * retained by the sketch algorithm, respectively.
-   * (This should not to be confused with {@link #getMinItem} and {@link #getMaxItem},
-   * which are the smallest and largest quantiles of the stream.)</li>
-   * <li>A 1 will return the Smallest quantile.</li>
-   * <li>A 2 will return the Smallest and Largest quantiles.</li>
-   * <li>A 3 will return the Smallest, the Median, and the Largest quantiles.</li>
+   * @param numEvenlySpaced an integer that specifies the number of desired quantile intervals to be returned
+   * from the sketch. This must be a positive integer greater than 0.
+   * <ul><li>Let <i>Largest</i> be the largest quantile retained by the sketch algorithm.
+   * (This should not to be confused with {@link #getMaxItem},
+   * which is the largest quantile of the stream. They may be equal, but not necessarily.)</li>
+   * <li>A 1 will return the Largest quantile.</li>
+   * <li>A 2 will return 2 quantiles, including the Largest, dividing the quantile domain into two regions. 
+   * The first returned quantile should roughly correspond to the median quantile</li>
+   * <li>A 3 will return 3 quantiles, including the Largest, dividing the quantile domain into three regions.</li>
    * <li>Etc.</li>
    * </ul>
    *
    * @param searchCrit if INCLUSIVE, the given ranks include all quantiles &le; the quantile directly corresponding to
-   * each rank.
-   * @return an array of quantiles that are evenly spaced by their ranks.
+   * any given rank. In this case the ranks are generated internally by this method.
+   * @return an array of quantiles that divide the quantile domain into numEvenlySpaced quantile intervals.
    * @throws IllegalArgumentException if sketch is empty or if <i>numEvenlySpaced is less than 1</i>.
    * @see org.apache.datasketches.quantilescommon.QuantileSearchCriteria
    */
diff --git a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesFloatsAPI.java b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesFloatsAPI.java
index 91ecfff4..5b73ad10 100644
--- a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesFloatsAPI.java
+++ b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesFloatsAPI.java
@@ -234,24 +234,25 @@ public interface QuantilesFloatsAPI extends QuantilesAPI {
   }
 
   /**
-   * This is a version of getQuantiles() where the caller only specifies the number of of desired evenly spaced,
-   * normalized ranks, and returns an array of the corresponding quantiles.
+   * This is a version of getQuantiles() where the caller only specifies the number of of desired quantile intervals 
+   * that are computed from an array of evenly spaced normalized ranks between 0 and 1.0 determined from the given
+   * <i>numEvenlySpaced</i> parameter.
    *
-   * @param numEvenlySpaced an integer that specifies the number of evenly spaced normalized ranks.
-   * This must be a positive integer greater than 0.
-   * <ul><li>Let <i>Smallest</i> and <i>Largest</i> be the smallest and largest quantiles
-   * retained by the sketch algorithm, respectively.
-   * (This should not to be confused with {@link #getMinItem} and {@link #getMaxItem},
-   * which are the smallest and largest quantiles of the stream.)</li>
-   * <li>A 1 will return the Smallest quantile.</li>
-   * <li>A 2 will return the Smallest and Largest quantiles.</li>
-   * <li>A 3 will return the Smallest, the Median, and the Largest quantiles.</li>
+   * @param numEvenlySpaced an integer that specifies the number of desired quantile intervals to be returned
+   * from the sketch. This must be a positive integer greater than 0.
+   * <ul><li>Let <i>Largest</i> be the largest quantile retained by the sketch algorithm.
+   * (This should not to be confused with {@link #getMaxItem},
+   * which is the largest quantile of the stream. They may be equal, but not necessarily.)</li>
+   * <li>A 1 will return the Largest quantile.</li>
+   * <li>A 2 will return 2 quantiles, including the Largest, dividing the quantile domain into two regions. 
+   * The first returned quantile should roughly correspond to the median quantile</li>
+   * <li>A 3 will return 3 quantiles, including the Largest, dividing the quantile domain into three regions.</li>
    * <li>Etc.</li>
    * </ul>
    *
    * @param searchCrit if INCLUSIVE, the given ranks include all quantiles &le; the quantile directly corresponding to
-   * each rank.
-   * @return an array of quantiles that are evenly spaced by their ranks.
+   * any given rank. In this case the ranks are generated internally by this method.
+   * @return an array of quantiles that divide the quantile domain into numEvenlySpaced quantile intervals.
    * @throws IllegalArgumentException if sketch is empty or if <i>numEvenlySpaced is less than 1</i>.
    * @see org.apache.datasketches.quantilescommon.QuantileSearchCriteria
    */
diff --git a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesGenericAPI.java b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesGenericAPI.java
index b6d15b60..a0d1177b 100644
--- a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesGenericAPI.java
+++ b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesGenericAPI.java
@@ -235,24 +235,25 @@ public interface QuantilesGenericAPI<T> extends QuantilesAPI {
   }
 
   /**
-   * This is a version of getQuantiles() where the caller only specifies the number of of desired evenly spaced,
-   * normalized ranks, and returns an array of the corresponding quantiles.
+   * This is a version of getQuantiles() where the caller only specifies the number of of desired quantile intervals 
+   * that are computed from an array of evenly spaced normalized ranks between 0 and 1.0 determined from the given
+   * <i>numEvenlySpaced</i> parameter.
    *
-   * @param numEvenlySpaced an integer that specifies the number of evenly spaced normalized ranks.
-   * This must be a positive integer greater than 0.
-   * <ul><li>Let <i>Smallest</i> and <i>Largest</i> be the smallest and largest quantiles
-   * retained by the sketch algorithm, respectively.
-   * (This should not to be confused with {@link #getMinItem} and {@link #getMaxItem},
-   * which are the smallest and largest quantiles of the stream.)</li>
-   * <li>A 1 will return the Smallest quantile.</li>
-   * <li>A 2 will return the Smallest and Largest quantiles.</li>
-   * <li>A 3 will return the Smallest, the Median, and the Largest quantiles.</li>
+   * @param numEvenlySpaced an integer that specifies the number of desired quantile intervals to be returned
+   * from the sketch. This must be a positive integer greater than 0.
+   * <ul><li>Let <i>Largest</i> be the largest quantile retained by the sketch algorithm.
+   * (This should not to be confused with {@link #getMaxItem},
+   * which is the largest quantile of the stream. They may be equal, but not necessarily.)</li>
+   * <li>A 1 will return the Largest quantile.</li>
+   * <li>A 2 will return 2 quantiles, including the Largest, dividing the quantile domain into two regions. 
+   * The first returned quantile should roughly correspond to the median quantile</li>
+   * <li>A 3 will return 3 quantiles, including the Largest, dividing the quantile domain into three regions.</li>
    * <li>Etc.</li>
    * </ul>
    *
    * @param searchCrit if INCLUSIVE, the given ranks include all quantiles &le; the quantile directly corresponding to
-   * each rank.
-   * @return an array of quantiles that are evenly spaced by their ranks.
+   * any given rank. In this case the ranks are generated internally by this method.
+   * @return an array of quantiles that divide the quantile domain into numEvenlySpaced quantile intervals.
    * @throws IllegalArgumentException if sketch is empty or if <i>numEvenlySpaced is less than 1</i>.
    * @see org.apache.datasketches.quantilescommon.QuantileSearchCriteria
    */
diff --git a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesUtil.java b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesUtil.java
index dc469200..a0aed292 100644
--- a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesUtil.java
+++ b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesUtil.java
@@ -80,33 +80,49 @@ public final class QuantilesUtil {
   }
 
   /**
-   * Returns a double array of evenly spaced values between value1 and value2 inclusive.
+   * Returns a double array of evenly spaced values between 0.0, exclusive, and 1.0, inclusive.
+   * If num == 1, 1.0 will be in index 0 of the returned array.
+   * @param num the total number of values excluding 0 and including 1.0. Must be 1 or greater.
+   * @return a double array of evenly spaced values between 0.0, exclusive, and 1.0, inclusive.
+   * @throws IllegalArgumentException if <i>num</i> is less than 1.
+   */
+  public static double[] evenlySpacedRanks(final int num) {
+    if (num < 1) { throw new IllegalArgumentException("num must be >= 1"); }
+    final double[] out = new double[num];
+    if (num == 1) { return new double[] { 1.0 }; }
+    out[num - 1] = 1.0;
+
+    final double delta = 1.0 / num;
+    for (int i = 0; i < num - 1; i++) { out[i] = (i + 1) * delta; }
+    return out;
+  }
+
+  /**
+   * Returns a float array of evenly spaced values between value1 and value2 inclusive.
    * If value2 &gt; value1, the resulting sequence will be increasing.
    * If value2 &lt; value1, the resulting sequence will be decreasing.
-   * If num == 1, value1 will be in index 0 of the returned array.
    * @param value1 will be in index 0 of the returned array
    * @param value2 will be in the highest index of the returned array
-   * @param num the total number of values including value1 and value2. Must be 1 or greater.
-   * @return a double array of evenly spaced values between value1 and value2 inclusive.
-   * @throws IllegalArgumentException if <i>num</i> is less than 1.
+   * @param num the total number of values including value1 and value2. Must be 2 or greater.
+   * @return a float array of evenly spaced values between value1 and value2 inclusive.
    */
-  public static double[] evenlySpaced(final double value1, final double value2, final int num) {
-    if (num < 1) {
-      throw new IllegalArgumentException("num must be >= 1");
+  public static float[] evenlySpacedFloats(final float value1, final float value2, final int num) {
+    if (num < 2) {
+      throw new SketchesArgumentException("num must be >= 2");
     }
-    final double[] out = new double[num];
+    final float[] out = new float[num];
     out[0] = value1;
-    if (num == 1) { return out; }
     out[num - 1] = value2;
     if (num == 2) { return out; }
 
-    final double delta = (value2 - value1) / (num - 1);
+    final float delta = (value2 - value1) / (num - 1);
+
     for (int i = 1; i < num - 1; i++) { out[i] = i * delta + value1; }
     return out;
   }
 
   /**
-   * Returns a float array of evenly spaced values between value1 and value2 inclusive.
+   * Returns a double array of evenly spaced values between value1, inclusive, and value2 inclusive.
    * If value2 &gt; value1, the resulting sequence will be increasing.
    * If value2 &lt; value1, the resulting sequence will be decreasing.
    * @param value1 will be in index 0 of the returned array
@@ -114,21 +130,21 @@ public final class QuantilesUtil {
    * @param num the total number of values including value1 and value2. Must be 2 or greater.
    * @return a float array of evenly spaced values between value1 and value2 inclusive.
    */
-  public static float[] evenlySpacedFloats(final float value1, final float value2, final int num) {
+  public static double[] evenlySpacedDoubles(final double value1, final double value2, final int num) {
     if (num < 2) {
       throw new SketchesArgumentException("num must be >= 2");
     }
-    final float[] out = new float[num];
+    final double[] out = new double[num];
     out[0] = value1;
     out[num - 1] = value2;
     if (num == 2) { return out; }
 
-    final float delta = (value2 - value1) / (num - 1);
+    final double delta = (value2 - value1) / (num - 1);
 
     for (int i = 1; i < num - 1; i++) { out[i] = i * delta + value1; }
     return out;
   }
-
+  
   /**
    * Returns a double array of values between min and max inclusive where the log of the
    * returned values are evenly spaced.
@@ -147,7 +163,7 @@ public final class QuantilesUtil {
       throw new SketchesArgumentException("value1 and value2 must be > 0.");
     }
 
-    final double[] arr = evenlySpaced(log(value1) / Util.LOG2, log(value2) / Util.LOG2, num);
+    final double[] arr = evenlySpacedDoubles(log(value1) / Util.LOG2, log(value2) / Util.LOG2, num);
     for (int i = 0; i < arr.length; i++) { arr[i] = pow(2.0,arr[i]); }
     return arr;
   }
diff --git a/src/main/java/org/apache/datasketches/req/BaseReqSketch.java b/src/main/java/org/apache/datasketches/req/BaseReqSketch.java
index 82fd4dee..4e4e0af2 100644
--- a/src/main/java/org/apache/datasketches/req/BaseReqSketch.java
+++ b/src/main/java/org/apache/datasketches/req/BaseReqSketch.java
@@ -20,6 +20,7 @@
 package org.apache.datasketches.req;
 
 import static org.apache.datasketches.quantilescommon.QuantilesUtil.THROWS_EMPTY;
+import static org.apache.datasketches.quantilescommon.QuantilesUtil.evenlySpacedRanks;
 
 import org.apache.datasketches.quantilescommon.FloatsSortedView;
 import org.apache.datasketches.quantilescommon.QuantileSearchCriteria;
@@ -91,7 +92,7 @@ abstract class BaseReqSketch implements QuantilesFloatsAPI {
   @Override
   public float[] getQuantiles(final int numEvenlySpaced, final QuantileSearchCriteria searchCrit) {
     if (isEmpty()) { throw new IllegalArgumentException(THROWS_EMPTY); }
-    return getQuantiles(org.apache.datasketches.quantilescommon.QuantilesUtil.evenlySpaced(0.0, 1.0, numEvenlySpaced),
+    return getQuantiles(evenlySpacedRanks(numEvenlySpaced),
         searchCrit);
   }
 
diff --git a/src/test/java/org/apache/datasketches/kll/KllDirectDoublesSketchTest.java b/src/test/java/org/apache/datasketches/kll/KllDirectDoublesSketchTest.java
index 0a10b001..7579f916 100644
--- a/src/test/java/org/apache/datasketches/kll/KllDirectDoublesSketchTest.java
+++ b/src/test/java/org/apache/datasketches/kll/KllDirectDoublesSketchTest.java
@@ -429,12 +429,11 @@ public class KllDirectDoublesSketchTest {
     sketch.update(1);
     sketch.update(2);
     sketch.update(3);
-    final double[] quantiles1 = sketch.getQuantiles(new double[] {0, 0.5, 1}, EXCLUSIVE);
-    final double[] quantiles2 = sketch.getQuantiles(3, EXCLUSIVE);
+    final double[] quantiles1 = sketch.getQuantiles(new double[] {0.5, 1}, EXCLUSIVE);
+    final double[] quantiles2 = sketch.getQuantiles(2, EXCLUSIVE);
     assertEquals(quantiles1, quantiles2);
-    assertEquals(quantiles1[0], 1.0);
-    assertEquals(quantiles1[1], 2.0);
-    assertEquals(quantiles1[2], 3.0);
+    assertEquals(quantiles1[0], 2.0);
+    assertEquals(quantiles1[1], 3.0);
   }
 
   @Test
diff --git a/src/test/java/org/apache/datasketches/kll/KllDirectFloatsSketchTest.java b/src/test/java/org/apache/datasketches/kll/KllDirectFloatsSketchTest.java
index 81407776..dd0fe91d 100644
--- a/src/test/java/org/apache/datasketches/kll/KllDirectFloatsSketchTest.java
+++ b/src/test/java/org/apache/datasketches/kll/KllDirectFloatsSketchTest.java
@@ -430,12 +430,11 @@ public class KllDirectFloatsSketchTest {
     sketch.update(1);
     sketch.update(2);
     sketch.update(3);
-    final float[] quantiles1 = sketch.getQuantiles(new double[] {0, 0.5, 1}, EXCLUSIVE);
-    final float[] quantiles2 = sketch.getQuantiles(3, EXCLUSIVE);
+    final float[] quantiles1 = sketch.getQuantiles(new double[] {0.5, 1}, EXCLUSIVE);
+    final float[] quantiles2 = sketch.getQuantiles(2, EXCLUSIVE);
     assertEquals(quantiles1, quantiles2);
-    assertEquals(quantiles1[0], 1f);
-    assertEquals(quantiles1[1], 2f);
-    assertEquals(quantiles1[2], 3f);
+    assertEquals(quantiles1[0], 2f);
+    assertEquals(quantiles1[1], 3f);
   }
 
   @Test
diff --git a/src/test/java/org/apache/datasketches/kll/KllDoublesSketchTest.java b/src/test/java/org/apache/datasketches/kll/KllDoublesSketchTest.java
index 8c7d927d..b9718cd4 100644
--- a/src/test/java/org/apache/datasketches/kll/KllDoublesSketchTest.java
+++ b/src/test/java/org/apache/datasketches/kll/KllDoublesSketchTest.java
@@ -474,12 +474,11 @@ public class KllDoublesSketchTest {
     sketch.update(1);
     sketch.update(2);
     sketch.update(3);
-    final double[] quantiles1 = sketch.getQuantiles(new double[] {0, 0.5, 1}, EXCLUSIVE);
-    final double[] quantiles2 = sketch.getQuantiles(3, EXCLUSIVE);
+    final double[] quantiles1 = sketch.getQuantiles(new double[] {0.5, 1}, EXCLUSIVE);
+    final double[] quantiles2 = sketch.getQuantiles(2, EXCLUSIVE);
     assertEquals(quantiles1, quantiles2);
-    assertEquals(quantiles1[0], 1.0);
-    assertEquals(quantiles1[1], 2.0);
-    assertEquals(quantiles1[2], 3.0);
+    assertEquals(quantiles1[0], 2.0);
+    assertEquals(quantiles1[1], 3.0);
   }
 
   @Test
diff --git a/src/test/java/org/apache/datasketches/kll/KllFloatsSketchTest.java b/src/test/java/org/apache/datasketches/kll/KllFloatsSketchTest.java
index e79e102b..2df69a2a 100644
--- a/src/test/java/org/apache/datasketches/kll/KllFloatsSketchTest.java
+++ b/src/test/java/org/apache/datasketches/kll/KllFloatsSketchTest.java
@@ -475,12 +475,11 @@ public class KllFloatsSketchTest {
     sketch.update(1);
     sketch.update(2);
     sketch.update(3);
-    final float[] quantiles1 = sketch.getQuantiles(new double[] {0, 0.5, 1}, EXCLUSIVE);
-    final float[] quantiles2 = sketch.getQuantiles(3, EXCLUSIVE);
+    final float[] quantiles1 = sketch.getQuantiles(new double[] {0.5, 1}, EXCLUSIVE);
+    final float[] quantiles2 = sketch.getQuantiles(2, EXCLUSIVE);
     assertEquals(quantiles1, quantiles2);
-    assertEquals(quantiles1[0], 1f);
-    assertEquals(quantiles1[1], 2f);
-    assertEquals(quantiles1[2], 3f);
+    assertEquals(quantiles1[0], 2f);
+    assertEquals(quantiles1[1], 3f);
   }
 
   @Test
diff --git a/src/test/java/org/apache/datasketches/quantiles/HeapUpdateDoublesSketchTest.java b/src/test/java/org/apache/datasketches/quantiles/HeapUpdateDoublesSketchTest.java
index 7feaa36f..49a10c3b 100644
--- a/src/test/java/org/apache/datasketches/quantiles/HeapUpdateDoublesSketchTest.java
+++ b/src/test/java/org/apache/datasketches/quantiles/HeapUpdateDoublesSketchTest.java
@@ -29,6 +29,7 @@ import static org.apache.datasketches.quantiles.PreambleUtil.COMPACT_FLAG_MASK;
 import static org.apache.datasketches.quantiles.PreambleUtil.EMPTY_FLAG_MASK;
 import static org.apache.datasketches.quantilescommon.QuantileSearchCriteria.EXCLUSIVE;
 import static org.apache.datasketches.quantilescommon.QuantileSearchCriteria.INCLUSIVE;
+import static org.apache.datasketches.quantilescommon.QuantilesUtil.evenlySpacedRanks;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNull;
@@ -800,12 +801,12 @@ public class HeapUpdateDoublesSketchTest {
 
   @Test
   public void checkEvenlySpaced() {
-    int n = 11;
-    double[] es = org.apache.datasketches.quantilescommon.QuantilesUtil.evenlySpaced(0.0, 1.0, n);
+    int n = 10;
+    double[] es = evenlySpacedRanks(n);
     int len = es.length;
     for (int j=0; j<len; j++) {
       double f = es[j];
-      assertEquals(f, j/10.0, (j/10.0) * 0.001);
+      assertEquals(f, (j+1)/10.0, ((j+1)/10.0) * 0.001);
       print(es[j]+", ");
     }
     println("");
diff --git a/src/test/java/org/apache/datasketches/quantiles/ItemsSketchTest.java b/src/test/java/org/apache/datasketches/quantiles/ItemsSketchTest.java
index 80829930..73e258f9 100644
--- a/src/test/java/org/apache/datasketches/quantiles/ItemsSketchTest.java
+++ b/src/test/java/org/apache/datasketches/quantiles/ItemsSketchTest.java
@@ -212,9 +212,9 @@ public class ItemsSketchTest {
 
 
 
-    quantiles = sketch.getQuantiles(3);
+    quantiles = sketch.getQuantiles(2);
 
-    assertEquals(quantiles[1], Integer.valueOf(500), 17); // median
+    assertEquals(quantiles[0], Integer.valueOf(500), 17); // median
 
 
     final double normErr = sketch.getNormalizedRankError(true);
diff --git a/src/test/java/org/apache/datasketches/quantilescommon/QuantilesUtilTest.java b/src/test/java/org/apache/datasketches/quantilescommon/QuantilesUtilTest.java
index ca2187bd..b4f5679c 100644
--- a/src/test/java/org/apache/datasketches/quantilescommon/QuantilesUtilTest.java
+++ b/src/test/java/org/apache/datasketches/quantilescommon/QuantilesUtilTest.java
@@ -28,15 +28,15 @@ import org.testng.annotations.Test;
 public class QuantilesUtilTest {
 
   @Test
-  public void checkEvenlySpaced() {
-    double[] arr = QuantilesUtil.evenlySpaced(0, 1, 3);
-    assertEquals(arr[0], 0.0);
+  public void checkEvenlySpacedRanks() {
+    double[] arr = QuantilesUtil.evenlySpacedRanks(2);
+    assertEquals(arr[0], 0.5);
+    assertEquals(arr[1], 1.0);
+    arr = QuantilesUtil.evenlySpacedRanks(4);
+    assertEquals(arr[0], 0.25);
     assertEquals(arr[1], 0.5);
-    assertEquals(arr[2], 1.0);
-    arr = QuantilesUtil.evenlySpaced(3, 7, 3);
-    assertEquals(arr[0], 3.0);
-    assertEquals(arr[1], 5.0);
-    assertEquals(arr[2], 7.0);
+    assertEquals(arr[2], 0.75);
+    assertEquals(arr[3], 1.0);
   }
 
   @Test
@@ -55,6 +55,22 @@ public class QuantilesUtilTest {
     try { QuantilesUtil.evenlySpacedFloats(0f, 1f, 1); fail(); } catch (SketchesArgumentException e) {}
   }
 
+  @Test
+  public void checkEvenlySpacedDoubles() {
+    double[] arr = QuantilesUtil.evenlySpacedDoubles(0, 1, 3);
+    assertEquals(arr[0], 0.0);
+    assertEquals(arr[1], 0.5);
+    assertEquals(arr[2], 1.0);
+    arr = QuantilesUtil.evenlySpacedDoubles(3, 7, 3);
+    assertEquals(arr[0], 3.0);
+    assertEquals(arr[1], 5.0);
+    assertEquals(arr[2], 7.0);
+    arr = QuantilesUtil.evenlySpacedDoubles(0, 1.0, 2);
+    assertEquals(arr[0], 0);
+    assertEquals(arr[1], 1.0);
+    try { QuantilesUtil.evenlySpacedDoubles(0, 1.0, 1); fail(); } catch (SketchesArgumentException e) {}
+  }
+  
   @Test
   public void checkEvenlyLogSpaced() {
     final double[] arr = QuantilesUtil.evenlyLogSpaced(1, 8, 4);
diff --git a/src/test/java/org/apache/datasketches/req/ReqSketchTest.java b/src/test/java/org/apache/datasketches/req/ReqSketchTest.java
index e1bbaf89..e72ecb0f 100644
--- a/src/test/java/org/apache/datasketches/req/ReqSketchTest.java
+++ b/src/test/java/org/apache/datasketches/req/ReqSketchTest.java
@@ -21,6 +21,7 @@ package org.apache.datasketches.req;
 
 import static org.apache.datasketches.quantilescommon.QuantileSearchCriteria.EXCLUSIVE;
 import static org.apache.datasketches.quantilescommon.QuantileSearchCriteria.INCLUSIVE;
+import static org.apache.datasketches.quantilescommon.QuantilesUtil.evenlySpacedDoubles;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.fail;
@@ -119,7 +120,7 @@ public class ReqSketchTest {
   private static void checkGetRank(final ReqSketch sk, final int min, final int max, final int iDebug) {
     if (iDebug > 0) { println("GetRank Test: INCLUSIVE"); }
     final float[] spArr = QuantilesUtil.evenlySpacedFloats(0, max, 11);
-    final double[] trueRanks = QuantilesUtil.evenlySpaced(0, 1.0, 11);
+    final double[] trueRanks = evenlySpacedDoubles(0, 1.0, 11);
     final String dfmt = "%10.2f%10.6f" + LS;
     final String sfmt = "%10s%10s" + LS;
     if (iDebug > 0) { printf(sfmt, "Value", "Rank"); }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datasketches.apache.org
For additional commands, e-mail: commits-help@datasketches.apache.org