From 0bea5d67123803a38a8b609f4ec9d1109b1f7a4b Mon Sep 17 00:00:00 2001 From: Lee Rhodes Date: Sun, 19 Mar 2023 19:54:46 -0700 Subject: [PATCH 1/4] Found a minor discrepancy between the documentation of the getQuantiles(int numEvenlySpaced) methods and the implementation of the QuantilesUtil.evenlySpaced(...) method. The javadocs of the getQuantiles methods (for all quantiles sketches) claimed that a "numEvenlySpaced == 1" was valid, yet the implementation would throw an exception if "numEvenlySpaced < 2". So I updated the implementation so that the implementation matched the javadocs. I also updated the relevant quantiles javadocs to reflect that this exception could be thrown. The only place this QuantilesUtil.evenlySpaced(...) method was used was for these quantiles methods, so no place else in the library used this method. --- .../quantilescommon/QuantilesDoublesAPI.java | 2 +- .../quantilescommon/QuantilesFloatsAPI.java | 2 +- .../quantilescommon/QuantilesGenericAPI.java | 2 +- .../datasketches/quantilescommon/QuantilesUtil.java | 10 ++++++---- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesDoublesAPI.java b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesDoublesAPI.java index 7dd57f595..5479b2949 100644 --- a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesDoublesAPI.java +++ b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesDoublesAPI.java @@ -253,7 +253,7 @@ default double[] getQuantiles(int numEvenlySpaced) { * @param searchCrit if INCLUSIVE, the given ranks include all quantiles ≤ the quantile directly corresponding to * each rank. * @return an array of quantiles that are evenly spaced by their ranks. - * @throws IllegalArgumentException if sketch is empty. + * @throws IllegalArgumentException if sketch is empty or if numEvenlySpaced < 1. * @see org.apache.datasketches.quantilescommon.QuantileSearchCriteria */ double[] getQuantiles(int numEvenlySpaced, QuantileSearchCriteria searchCrit); diff --git a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesFloatsAPI.java b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesFloatsAPI.java index 24032b42c..528a9212b 100644 --- a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesFloatsAPI.java +++ b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesFloatsAPI.java @@ -252,7 +252,7 @@ default float[] getQuantiles(int numEvenlySpaced) { * @param searchCrit if INCLUSIVE, the given ranks include all quantiles ≤ the quantile directly corresponding to * each rank. * @return an array of quantiles that are evenly spaced by their ranks. - * @throws IllegalArgumentException if sketch is empty. + * @throws IllegalArgumentException if sketch is empty or if numEvenlySpaced < 1. * @see org.apache.datasketches.quantilescommon.QuantileSearchCriteria */ float[] getQuantiles(int numEvenlySpaced, QuantileSearchCriteria searchCrit); diff --git a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesGenericAPI.java b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesGenericAPI.java index 06e51f098..20150870b 100644 --- a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesGenericAPI.java +++ b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesGenericAPI.java @@ -253,7 +253,7 @@ default T[] getQuantiles(int numEvenlySpaced) { * @param searchCrit if INCLUSIVE, the given ranks include all quantiles ≤ the quantile directly corresponding to * each rank. * @return an array of quantiles that are evenly spaced by their ranks. - * @throws IllegalArgumentException if sketch is empty. + * @throws IllegalArgumentException if sketch is empty or if numEvenlySpaced < 1. * @see org.apache.datasketches.quantilescommon.QuantileSearchCriteria */ T[] getQuantiles(int numEvenlySpaced, QuantileSearchCriteria searchCrit); diff --git a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesUtil.java b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesUtil.java index 8900ebcd9..a57cb8e42 100644 --- a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesUtil.java +++ b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesUtil.java @@ -83,22 +83,24 @@ public static final void checkFloatsSplitPointsOrder(final float[] values) { * Returns a double array of evenly spaced values between value1 and value2 inclusive. * If value2 > value1, the resulting sequence will be increasing. * If value2 < 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 2 or greater. + * @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 num < 1. */ public static double[] evenlySpaced(final double value1, final double value2, final int num) { - if (num < 2) { - throw new SketchesArgumentException("num must be >= 2"); + if (num < 1) { + throw new IllegalArgumentException("num must be >= 1"); } final double[] out = new double[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); - for (int i = 1; i < num - 1; i++) { out[i] = i * delta + value1; } return out; } From d01fa8b3d8b83b6880493d2f9094094474bf4f59 Mon Sep 17 00:00:00 2001 From: Lee Rhodes Date: Sun, 19 Mar 2023 20:28:08 -0700 Subject: [PATCH 2/4] Oops, forgot to change the tests affected by this update. --- .../HeapUpdateDoublesSketchTest.java | 1072 ----------------- .../quantiles/ItemsSketchTest.java | 2 +- 2 files changed, 1 insertion(+), 1073 deletions(-) delete mode 100644 src/test/java/org/apache/datasketches/quantiles/HeapUpdateDoublesSketchTest.java diff --git a/src/test/java/org/apache/datasketches/quantiles/HeapUpdateDoublesSketchTest.java b/src/test/java/org/apache/datasketches/quantiles/HeapUpdateDoublesSketchTest.java deleted file mode 100644 index 4de24ff79..000000000 --- a/src/test/java/org/apache/datasketches/quantiles/HeapUpdateDoublesSketchTest.java +++ /dev/null @@ -1,1072 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.datasketches.quantiles; - -import static java.lang.Math.floor; -import static org.apache.datasketches.common.Util.log2; -import static org.apache.datasketches.quantiles.ClassicUtil.LS; -import static org.apache.datasketches.quantiles.ClassicUtil.computeCombinedBufferItemCapacity; -import static org.apache.datasketches.quantiles.ClassicUtil.computeNumLevelsNeeded; -import static org.apache.datasketches.quantiles.HeapUpdateDoublesSketch.checkPreLongsFlagsSerVer; -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.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; - -import java.nio.ByteOrder; - -import org.apache.datasketches.common.SketchesArgumentException; -import org.apache.datasketches.memory.Memory; -import org.apache.datasketches.memory.WritableMemory; -import org.apache.datasketches.quantilescommon.QuantilesUtil; -import org.testng.Assert; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -public class HeapUpdateDoublesSketchTest { - - @BeforeMethod - public void setUp() { - DoublesSketch.rand.setSeed(32749); // make sketches deterministic for testing - } - - // Please note that this is a randomized test that could probabilistically fail - // if we didn't set the seed. (The probability of failure could be reduced by increasing k.) - // Setting the seed has now made it deterministic. - @Test - public void checkEndToEnd() { - int k = 256; - UpdateDoublesSketch qs = DoublesSketch.builder().setK(k).build(); - UpdateDoublesSketch qs2 = DoublesSketch.builder().setK(k).build(); - int n = 1000000; - for (int item = n; item >= 1; item--) { - if ((item % 4) == 0) { - qs.update(item); - } - else { - qs2.update(item); - } - } - assertEquals(qs.getN() + qs2.getN(), n); - DoublesUnion union = DoublesUnion.heapify(qs); - union.union(qs2); - DoublesSketch result = union.getResult(); - - int numPhiValues = 99; - double[] phiArr = new double[numPhiValues]; - for (int q = 1; q <= 99; q++) { - phiArr[q-1] = q / 100.0; - } - double[] splitPoints = result.getQuantiles(phiArr); - -// for (int i = 0; i < 99; i++) { -// String s = String.format("%d\t%.6f\t%.6f", i, phiArr[i], splitPoints[i]); -// println(s); -// } - - for (int q = 1; q <= 99; q++) { - double nominal = (1e6 * q) / 100.0; - double reported = splitPoints[q-1]; - assertTrue(reported >= (nominal - 10000.0)); - assertTrue(reported <= (nominal + 10000.0)); - } - - double[] pmfResult = result.getPMF(splitPoints); - double subtotal = 0.0; - for (int q = 1; q <= 100; q++) { - double phi = q / 100.0; - subtotal += pmfResult[q-1]; - assertTrue(subtotal >= (phi - 0.01)); - assertTrue(subtotal <= (phi + 0.01)); - } - - double[] cdfResult = result.getCDF(splitPoints); - for (int q = 1; q <= 100; q++) { - double phi = q / 100.0; - subtotal = cdfResult[q-1]; - assertTrue(subtotal >= (phi - 0.01)); - assertTrue(subtotal <= (phi + 0.01)); - } - - assertEquals(result.getRank(500000), 0.5, 0.01); - } - - @Test - public void checkSmallMinMax () { - int k = 32; - int n = 8; - UpdateDoublesSketch qs1 = DoublesSketch.builder().setK(k).build(); - UpdateDoublesSketch qs2 = DoublesSketch.builder().setK(k).build(); - UpdateDoublesSketch qs3 = DoublesSketch.builder().setK(k).build(); - - for (int i = n; i >= 1; i--) { - qs1.update(i); - qs2.update(10+i); - qs3.update(i); - } - assertEquals(qs1.getQuantile (0.0, EXCLUSIVE), 1.0); - assertEquals(qs1.getQuantile (0.5, EXCLUSIVE), 5.0); - assertEquals(qs1.getQuantile (1.0, EXCLUSIVE), 8.0); - - assertEquals(qs2.getQuantile (0.0, EXCLUSIVE), 11.0); - assertEquals(qs2.getQuantile (0.5, EXCLUSIVE), 15.0); - assertEquals(qs2.getQuantile (1.0, EXCLUSIVE), 18.0); - - assertEquals(qs3.getQuantile (0.0, EXCLUSIVE), 1.0); - assertEquals(qs3.getQuantile (0.5, EXCLUSIVE), 5.0); - assertEquals(qs3.getQuantile (1.0, EXCLUSIVE), 8.0); - - double[] queries = {0.0, 0.5, 1.0}; - - double[] resultsA = qs1.getQuantiles(queries, EXCLUSIVE); - assertEquals(resultsA[0], 1.0); - assertEquals(resultsA[1], 5.0); - assertEquals(resultsA[2], 8.0); - - DoublesUnion union1 = DoublesUnion.heapify(qs1); - union1.union(qs2); - DoublesSketch result1 = union1.getResult(); - - DoublesUnion union2 = DoublesUnion.heapify(qs2); - union2.union(qs3); - DoublesSketch result2 = union2.getResult(); - - double[] resultsB = result1.getQuantiles(queries, EXCLUSIVE); - assertEquals(resultsB[0], 1.0); - assertEquals(resultsB[1], 11.0); - assertEquals(resultsB[2], 18.0); - - double[] resultsC = result2.getQuantiles(queries, EXCLUSIVE); - assertEquals(resultsC[0], 1.0); - assertEquals(resultsC[1], 11.0); - assertEquals(resultsC[2], 18.0); - } - - @Test - public void checkMisc() { - int k = PreambleUtil.DEFAULT_K; - int n = 10000; - UpdateDoublesSketch qs = buildAndLoadQS(k, n); - qs.update(Double.NaN); //ignore - int n2 = (int)qs.getN(); - assertEquals(n2, n); - - qs.reset(); - assertEquals(qs.getN(), 0); - } - - @SuppressWarnings("unused") - @Test - public void checkToStringDetail() { - int k = PreambleUtil.DEFAULT_K; - int n = 1000000; - UpdateDoublesSketch qs = buildAndLoadQS(k, 0); - String s = qs.toString(); - s = qs.toString(false, true); - //println(s); - qs = buildAndLoadQS(k, n); - s = qs.toString(); - //println(s); - s = qs.toString(false, true); - //println(qs.toString(false, true)); - - int n2 = (int)qs.getN(); - assertEquals(n2, n); - qs.update(Double.NaN); //ignore - qs.reset(); - assertEquals(qs.getN(), 0); - } - - @Test(expectedExceptions = SketchesArgumentException.class) - public void checkConstructorException() { - DoublesSketch.builder().setK(0).build(); - } - - @Test - public void checkPreLongsFlagsAndSize() { - byte[] byteArr; - UpdateDoublesSketch ds = DoublesSketch.builder().build(); //k = 128 - //empty - byteArr = ds.toByteArray(true); // compact - assertEquals(byteArr.length, 8); - - byteArr = ds.toByteArray(false); // not compact - assertEquals(byteArr.length, 8); - assertEquals(byteArr[3], EMPTY_FLAG_MASK); - - //not empty - ds.update(1); - byteArr = ds.toByteArray(true); // compact - assertEquals(byteArr.length, 40); //compact, 1 value - - byteArr = ds.toByteArray(false); // not compact - assertEquals(byteArr.length, 64); // 32 + MIN_K(=2) * 2 * 8 = 64 - } - - @Test - public void checkPreLongsFlagsSerVerB() { - checkPreLongsFlagsSerVer(EMPTY_FLAG_MASK, 1, 1); //38 - checkPreLongsFlagsSerVer(0, 1, 5); //164 - checkPreLongsFlagsSerVer(EMPTY_FLAG_MASK, 2, 1); //42 - checkPreLongsFlagsSerVer(0, 2, 2); //72 - checkPreLongsFlagsSerVer(EMPTY_FLAG_MASK | COMPACT_FLAG_MASK, 3, 1); //47 - checkPreLongsFlagsSerVer(EMPTY_FLAG_MASK | COMPACT_FLAG_MASK, 3, 2); //79 - checkPreLongsFlagsSerVer(EMPTY_FLAG_MASK, 3, 2); //78 - checkPreLongsFlagsSerVer(COMPACT_FLAG_MASK, 3, 2);//77 - checkPreLongsFlagsSerVer(0, 3, 2); //76 - } - - @Test(expectedExceptions = SketchesArgumentException.class) - public void checkPreLongsFlagsSerVer3() { - checkPreLongsFlagsSerVer(EMPTY_FLAG_MASK, 1, 2); - } - - @Test(expectedExceptions = SketchesArgumentException.class) - public void checkGetQuantiles() { - int k = PreambleUtil.DEFAULT_K; - int n = 1000000; - DoublesSketch qs = buildAndLoadQS(k, n); - double[] frac = {-0.5}; - qs.getQuantiles(frac); - } - - @Test(expectedExceptions = SketchesArgumentException.class) - public void checkGetQuantile() { - int k = PreambleUtil.DEFAULT_K; - int n = 1000000; - DoublesSketch qs = buildAndLoadQS(k, n); - double frac = -0.5; //negative not allowed - qs.getQuantile(frac); - } - - //@Test //visual only - public void summaryCheckViaMemory() { - DoublesSketch qs = buildAndLoadQS(256, 1000000); - String s = qs.toString(); - println(s); - println(""); - - Memory srcMem = WritableMemory.writableWrap(qs.toByteArray()); - - HeapUpdateDoublesSketch qs2 = HeapUpdateDoublesSketch.heapifyInstance(srcMem); - s = qs2.toString(); - println(s); - } - - @Test - public void checkComputeNumLevelsNeeded() { - int n = 1 << 20; - int k = PreambleUtil.DEFAULT_K; - int lvls1 = computeNumLevelsNeeded(k, n); - int lvls2 = (int)Math.max(floor(log2((double)n/k)),0); - assertEquals(lvls1, lvls2); - } - - @Test - public void checkComputeBitPattern() { - int n = 1 << 20; - int k = PreambleUtil.DEFAULT_K; - long bitP = ClassicUtil.computeBitPattern(k, n); - assertEquals(bitP, n/(2L*k)); - } - - @Test(expectedExceptions = SketchesArgumentException.class) - public void checkValidateSplitPointsOrder() { - double[] arr = {2, 1}; - QuantilesUtil.checkDoublesSplitPointsOrder(arr); - } - - @Test - public void checkGetStorageBytes() { - int k = PreambleUtil.DEFAULT_K; //128 - DoublesSketch qs = buildAndLoadQS(k, 0); //k, n - int stor = qs.getCurrentCompactSerializedSizeBytes(); - assertEquals(stor, 8); - - qs = buildAndLoadQS(k, 2*k); //forces one level - stor = qs.getCurrentCompactSerializedSizeBytes(); - - int retItems = ClassicUtil.computeRetainedItems(k, 2*k); - assertEquals(stor, 32 + (retItems << 3)); - - qs = buildAndLoadQS(k, (2*k)-1); //just Base Buffer - stor = qs.getCurrentCompactSerializedSizeBytes(); - retItems = ClassicUtil.computeRetainedItems(k, (2*k)-1); - assertEquals(stor, 32 + (retItems << 3)); - } - - @Test - public void checkGetStorageBytes2() { - int k = PreambleUtil.DEFAULT_K; - long v = 1; - UpdateDoublesSketch qs = DoublesSketch.builder().setK(k).build(); - for (int i = 0; i< 1000; i++) { - qs.update(v++); -// for (int j = 0; j < 1000; j++) { -// qs.update(v++); -// } - byte[] byteArr = qs.toByteArray(false); - assertEquals(byteArr.length, qs.getCurrentUpdatableSerializedSizeBytes()); - } - } - - @Test - public void checkMerge() { - int k = PreambleUtil.DEFAULT_K; - int n = 1000000; - DoublesSketch qs1 = buildAndLoadQS(k,n,0); - DoublesSketch qs2 = buildAndLoadQS(k,0,0); //empty - DoublesUnion union = DoublesUnion.heapify(qs2); - union.union(qs1); - DoublesSketch result = union.getResult(); - double med1 = qs1.getQuantile(0.5); - double med2 = result.getQuantile(0.5); - assertEquals(med1, med2, 0.0); - //println(med1+","+med2); - } - - @Test - public void checkReverseMerge() { - int k = PreambleUtil.DEFAULT_K; - DoublesSketch qs1 = buildAndLoadQS(k, 1000, 0); - DoublesSketch qs2 = buildAndLoadQS(2*k,1000, 1000); - DoublesUnion union = DoublesUnion.heapify(qs2); - union.union(qs1); //attempt merge into larger k - DoublesSketch result = union.getResult(); - assertEquals(result.getK(), k); - } - - @Test - public void checkInternalBuildHistogram() { - int k = PreambleUtil.DEFAULT_K; - int n = 1000000; - DoublesSketch qs = buildAndLoadQS(k,n,0); - double eps = qs.getNormalizedRankError(true); - //println("EPS:"+eps); - double[] spts = {100000, 500000, 900000}; - double[] fracArr = qs.getPMF(spts); -// println(fracArr[0]+", "+ (fracArr[0]-0.1)); -// println(fracArr[1]+", "+ (fracArr[1]-0.4)); -// println(fracArr[2]+", "+ (fracArr[2]-0.4)); -// println(fracArr[3]+", "+ (fracArr[3]-0.1)); - assertEquals(fracArr[0], .1, eps); - assertEquals(fracArr[1], .4, eps); - assertEquals(fracArr[2], .4, eps); - assertEquals(fracArr[3], .1, eps); - } - - @Test - public void checkComputeBaseBufferCount() { - int n = 1 << 20; - int k = PreambleUtil.DEFAULT_K; - long bbCnt = ClassicUtil.computeBaseBufferItems(k, n); - assertEquals(bbCnt, n % (2L*k)); - } - - @Test - public void checkToFromByteArray() { - checkToFromByteArray2(128, 1300); //generates a pattern of 5 -> 101 - checkToFromByteArray2(4, 7); - checkToFromByteArray2(4, 8); - checkToFromByteArray2(4, 9); - } - - private static void checkToFromByteArray2(int k, int n) { - DoublesSketch qs = buildAndLoadQS(k, n); - byte[] byteArr; - Memory mem; - DoublesSketch qs2; - - // from compact - byteArr = qs.toByteArray(true); - mem = Memory.wrap(byteArr); - qs2 = UpdateDoublesSketch.heapify(mem); - for (double f = 0.1; f < 0.95; f += 0.1) { - assertEquals(qs.getQuantile(f), qs2.getQuantile(f), 0.0); - } - - // ordered, non-compact - byteArr = qs.toByteArray(false); - mem = Memory.wrap(byteArr); - qs2 = DoublesSketch.heapify(mem); - final DoublesSketchAccessor dsa = DoublesSketchAccessor.wrap(qs2); - dsa.sort(); - for (double f = 0.1; f < 0.95; f += 0.1) { - assertEquals(qs.getQuantile(f), qs2.getQuantile(f), 0.0); - } - - // not ordered, not compact - byteArr = qs.toByteArray(false); - mem = Memory.wrap(byteArr); - qs2 = DoublesSketch.heapify(mem); - for (double f = 0.1; f < 0.95; f += 0.1) { - assertEquals(qs.getQuantile(f), qs2.getQuantile(f), 0.0); - } - } - - @Test - public void checkEmpty() { - int k = PreambleUtil.DEFAULT_K; - DoublesSketch qs1 = buildAndLoadQS(k, 0); - byte[] byteArr = qs1.toByteArray(); - Memory mem = Memory.wrap(byteArr); - DoublesSketch qs2 = DoublesSketch.heapify(mem); - assertTrue(qs2.isEmpty()); - final int expectedSizeBytes = 8; //COMBINED_BUFFER + ((2 * MIN_K) << 3); - assertEquals(byteArr.length, expectedSizeBytes); - try { qs2.getQuantile(0.5); fail(); } catch (IllegalArgumentException e) { } - try { qs2.getQuantiles(new double[] {0.0, 0.5, 1.0}); fail(); } catch (IllegalArgumentException e) { } - try { qs2.getRank(0); fail(); } catch (IllegalArgumentException e) { } - } - - @Test(expectedExceptions = SketchesArgumentException.class) - public void checkMemTooSmall1() { - Memory mem = Memory.wrap(new byte[7]); - HeapUpdateDoublesSketch.heapifyInstance(mem); - fail(); - //qs2.getQuantile(0.5); - } - - //Corruption tests - @Test(expectedExceptions = SketchesArgumentException.class) - public void checkSerVer() { - DoublesUtil.checkDoublesSerVer(0, HeapUpdateDoublesSketch.MIN_HEAP_DOUBLES_SER_VER); - } - - @Test(expectedExceptions = SketchesArgumentException.class) - public void checkFamilyID() { - ClassicUtil.checkFamilyID(3); - } - - @Test(expectedExceptions = SketchesArgumentException.class) - public void checkMemCapacityException() { - int k = PreambleUtil.DEFAULT_K; - long n = 1000; - int serVer = 3; - int combBufItemCap = computeCombinedBufferItemCapacity(k, n); - int memCapBytes = (combBufItemCap + 4) << 3; - int badCapBytes = memCapBytes - 1; //corrupt - HeapUpdateDoublesSketch.checkHeapMemCapacity(k, n, false, serVer, badCapBytes); - } - - @Test(expectedExceptions = SketchesArgumentException.class) - public void checkBufAllocAndCap() { - int k = PreambleUtil.DEFAULT_K; - long n = 1000; - int serVer = 3; - int combBufItemCap = computeCombinedBufferItemCapacity(k, n); //non-compact cap - int memCapBytes = (combBufItemCap + 4) << 3; - int memCapBytesV1 = (combBufItemCap + 5) << 3; - HeapUpdateDoublesSketch.checkHeapMemCapacity(k, n, false, 1, memCapBytesV1); - HeapUpdateDoublesSketch.checkHeapMemCapacity(k, n, false, serVer, memCapBytes - 1); //corrupt - } - - @Test(expectedExceptions = SketchesArgumentException.class) - public void checkPreLongsFlagsCap() { - int preLongs = 5; - int flags = EMPTY_FLAG_MASK; - int memCap = 8; - ClassicUtil.checkPreLongsFlagsCap(preLongs, flags, memCap); //corrupt - } - - @Test(expectedExceptions = SketchesArgumentException.class) - public void checkPreLongsFlagsCap2() { - int preLongs = 5; - int flags = 0; - int memCap = 8; - ClassicUtil.checkPreLongsFlagsCap(preLongs, flags, memCap); //corrupt - } - - @Test(expectedExceptions = SketchesArgumentException.class) - public void checkFlags() { - int flags = 1; - ClassicUtil.checkHeapFlags(flags); - } - - @Test - public void checkZeroPatternReturn() { - int k = PreambleUtil.DEFAULT_K; - DoublesSketch qs1 = buildAndLoadQS(k, 64); - byte[] byteArr = qs1.toByteArray(); - Memory mem = Memory.wrap(byteArr); - HeapUpdateDoublesSketch.heapifyInstance(mem); - } - - @Test(expectedExceptions = SketchesArgumentException.class) - public void checkBadDownSamplingRatio() { - int k1 = 64; - DoublesSketch qs1 = buildAndLoadQS(k1, k1); - qs1.downSample(qs1, 2*k1, null);//should be smaller - } - - @Test - public void checkImproperKvalues() { - checksForImproperK(0); - checksForImproperK(1<<16); - } - - //Primarily visual only tests - static void testDownSampling(int bigK, int smallK) { - HeapUpdateDoublesSketch sketch1 = HeapUpdateDoublesSketch.newInstance(bigK); - HeapUpdateDoublesSketch sketch2 = HeapUpdateDoublesSketch.newInstance(smallK); - for (int i = 127; i >= 1; i--) { - sketch1.update (i); - sketch2.update (i); - } - HeapUpdateDoublesSketch downSketch = - (HeapUpdateDoublesSketch)sketch1.downSample(sketch1, smallK, null); - println (LS+"Sk1"+LS); - String s1, s2, down; - s1 = sketch1.toString(true, true); - println(s1); - println (LS+"Down"+LS); - down = downSketch.toString(true, true); - println(down); - println(LS+"Sk2"+LS); - s2 = sketch2.toString(true, true); - println(s2); - assertEquals(downSketch.getNumRetained(), sketch2.getNumRetained()); - } - - @Test - public void checkDownSampling() { - testDownSampling(4,4); //no down sampling - testDownSampling(16,4); - //testDownSampling(12,3); - } - - @Test - public void testDownSampling2() { - HeapUpdateDoublesSketch sketch1 = HeapUpdateDoublesSketch.newInstance(8); - HeapUpdateDoublesSketch sketch2 = HeapUpdateDoublesSketch.newInstance(2); - DoublesSketch downSketch; - downSketch = sketch1.downSample(sketch1, 2, null); - assertTrue(sameStructurePredicate(sketch2, downSketch)); - for (int i = 0; i < 50; i++) { - sketch1.update (i); - sketch2.update (i); - downSketch = sketch1.downSample(sketch1, 2, null); - assertTrue (sameStructurePredicate(sketch2, downSketch)); - } - } - - @Test - public void testDownSampling3() { - int k1 = 8; - int k2 = 2; - int n = 50; - UpdateDoublesSketch sketch1 = DoublesSketch.builder().setK(k1).build(); - UpdateDoublesSketch sketch2 = DoublesSketch.builder().setK(k2).build(); - DoublesSketch downSketch; - for (int i = 0; i < n; i++) { - sketch1.update (i); - sketch2.update (i); - downSketch = sketch1.downSample(sketch1, k2, null); - assertTrue (sameStructurePredicate(sketch2, downSketch)); - } - } - - @Test // - public void testDownSampling3withMem() { - int k1 = 8; - int k2 = 2; - int n = 50; - UpdateDoublesSketch sketch1 = DoublesSketch.builder().setK(k1).build(); - UpdateDoublesSketch sketch2 = DoublesSketch.builder().setK(k2).build(); - DoublesSketch downSketch; - int bytes = DoublesSketch.getUpdatableStorageBytes(k2, n); - WritableMemory mem = WritableMemory.writableWrap(new byte[bytes]); - for (int i = 0; i < n; i++) { - sketch1.update (i); - sketch2.update (i); - - downSketch = sketch1.downSample(sketch1, k2, mem); - assertTrue (sameStructurePredicate(sketch2, downSketch)); - } - - } - - - @Test - public void testDownSampling4() { - for (int n1 = 0; n1 < 50; n1++ ) { - HeapUpdateDoublesSketch bigSketch = HeapUpdateDoublesSketch.newInstance(8); - for (int i1 = 1; i1 <= n1; i1++ ) { - bigSketch.update(i1); - } - for (int n2 = 0; n2 < 50; n2++ ) { - HeapUpdateDoublesSketch directSketch = HeapUpdateDoublesSketch.newInstance(2); - for (int i1 = 1; i1 <= n1; i1++ ) { - directSketch.update(i1); - } - for (int i2 = 1; i2 <= n2; i2++ ) { - directSketch.update(i2); - } - HeapUpdateDoublesSketch smlSketch = HeapUpdateDoublesSketch.newInstance(2); - for (int i2 = 1; i2 <= n2; i2++ ) { - smlSketch.update(i2); - } - DoublesMergeImpl.downSamplingMergeInto(bigSketch, smlSketch); - assertTrue (sameStructurePredicate(directSketch, smlSketch)); - } - } - } - - @Test(expectedExceptions = SketchesArgumentException.class) - public void testDownSamplingExceptions1() { - UpdateDoublesSketch qs1 = DoublesSketch.builder().setK(4).build(); // not smaller - DoublesSketch qs2 = DoublesSketch.builder().setK(3).build(); - DoublesMergeImpl.mergeInto(qs2, qs1); - } - - @Test(expectedExceptions = SketchesArgumentException.class) - public void testDownSamplingExceptions2() { - UpdateDoublesSketch qs1 = DoublesSketch.builder().setK(4).build(); - DoublesSketch qs2 = DoublesSketch.builder().setK(7).build(); // 7/4 not pwr of 2 - DoublesMergeImpl.mergeInto(qs2, qs1); - } - - @Test(expectedExceptions = SketchesArgumentException.class) - public void testDownSamplingExceptions3() { - UpdateDoublesSketch qs1 = DoublesSketch.builder().setK(4).build(); - DoublesSketch qs2 = DoublesSketch.builder().setK(12).build(); // 12/4 not pwr of 2 - DoublesMergeImpl.mergeInto(qs2, qs1); - } - - //@Test //visual only - public void quantilesCheckViaMemory() { - int k = 256; - int n = 1000000; - DoublesSketch qs = buildAndLoadQS(k, n); - double[] ranks = {0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}; - String s = getRanksTable(qs, ranks); - println(s); - println(""); - - Memory srcMem = Memory.wrap(qs.toByteArray()); - - HeapUpdateDoublesSketch qs2 = HeapUpdateDoublesSketch.heapifyInstance(srcMem); - println(getRanksTable(qs2, ranks)); - } - - static String getRanksTable(DoublesSketch qs, double[] ranks) { - double rankError = qs.getNormalizedRankError(false); - double[] values = qs.getQuantiles(ranks); - double maxV = qs.getMaxItem(); - double minV = qs.getMinItem(); - double delta = maxV - minV; - println("Note: This prints the relative value errors for illustration."); - println("The quantiles sketch does not and can not guarantee relative value errors"); - - StringBuilder sb = new StringBuilder(); - sb.append(LS); - sb.append("N = ").append(qs.getN()).append(LS); - sb.append("K = ").append(qs.getK()).append(LS); - String formatStr1 = "%10s%15s%10s%15s%10s%10s"; - String formatStr2 = "%10.1f%15.5f%10.0f%15.5f%10.5f%10.5f"; - String hdr = String.format( - formatStr1, "Rank", "ValueLB", "<= Value", "<= ValueUB", "RelErrLB", "RelErrUB"); - sb.append(hdr).append(LS); - for (int i=0; i valueLB); - - double valRelPctErrUB = (valueUB/ value) -1.0; - double valRelPctErrLB = (valueLB/ value) -1.0; - String row = String.format( - formatStr2,rank, valueLB, value, valueUB, valRelPctErrLB, valRelPctErrUB); - sb.append(row).append(LS); - } - } - return sb.toString(); - } - - @Test - public void checkKisTwo() { - int k = 2; - UpdateDoublesSketch qs1 = DoublesSketch.builder().setK(k).build(); - double err = qs1.getNormalizedRankError(false); - assertTrue(err < 1.0); - byte[] arr = qs1.toByteArray(true); //8 - assertEquals(arr.length, DoublesSketch.getCompactSerialiedSizeBytes(k, 0)); - qs1.update(1.0); - arr = qs1.toByteArray(true); //40 - assertEquals(arr.length, DoublesSketch.getCompactSerialiedSizeBytes(k, 1)); - } - - @Test - public void checkKisTwoDeprecated() { - int k = 2; - UpdateDoublesSketch qs1 = DoublesSketch.builder().setK(k).build(); - double err = qs1.getNormalizedRankError(false); - assertTrue(err < 1.0); - byte[] arr = qs1.toByteArray(true); //8 - assertEquals(arr.length, DoublesSketch.getCompactSerialiedSizeBytes(k, 0)); - assertEquals(arr.length, qs1.getCurrentCompactSerializedSizeBytes()); - qs1.update(1.0); - arr = qs1.toByteArray(true); //40 - assertEquals(arr.length, DoublesSketch.getCompactSerialiedSizeBytes(k, 1)); - assertEquals(arr.length, qs1.getCurrentCompactSerializedSizeBytes()); - } - - @Test - public void checkPutMemory() { - UpdateDoublesSketch qs1 = DoublesSketch.builder().build(); //k = 128 - for (int i=0; i<1000; i++) { - qs1.update(i); - } - int bytes = qs1.getCurrentUpdatableSerializedSizeBytes(); - WritableMemory dstMem = WritableMemory.writableWrap(new byte[bytes]); - qs1.putMemory(dstMem, false); - Memory srcMem = dstMem; - DoublesSketch qs2 = DoublesSketch.heapify(srcMem); - assertEquals(qs1.getMinItem(), qs2.getMinItem(), 0.0); - assertEquals(qs1.getMaxItem(), qs2.getMaxItem(), 0.0); - } - - @Test(expectedExceptions = SketchesArgumentException.class) - public void checkPutMemoryTooSmall() { - UpdateDoublesSketch qs1 = DoublesSketch.builder().build(); //k = 128 - for (int i=0; i<1000; i++) { - qs1.update(i); - } - int bytes = qs1.getCurrentCompactSerializedSizeBytes(); - WritableMemory dstMem = WritableMemory.writableWrap(new byte[bytes-1]); //too small - qs1.putMemory(dstMem); - } - - //Himanshu's case - @Test - public void testIt() { - java.nio.ByteBuffer bb = java.nio.ByteBuffer.allocate(1<<20).order(ByteOrder.nativeOrder()); - WritableMemory mem = WritableMemory.writableWrap(bb); - - int k = 1024; - DoublesSketch qsk = new DoublesSketchBuilder().setK(k).build(); - DoublesUnion u1 = DoublesUnion.heapify(qsk); - u1.getResult().putMemory(mem); - DoublesUnion u2 = DoublesUnion.heapify(mem); - DoublesSketch qsk2 = u2.getResult(); - assertTrue(qsk2.isEmpty()); - } - - @Test - public void checkEvenlySpacedQuantiles() { - DoublesSketch qsk = buildAndLoadQS(32, 1001); - double[] values = qsk.getQuantiles(11); - for (int i = 0; i= ClassicUtil.computeCombinedBufferItemCapacity(mq1.getK(), mq1.getN())) - && (mq2.getCombinedBufferItemCapacity() - >= ClassicUtil.computeCombinedBufferItemCapacity(mq2.getK(), mq2.getN())) - && (mq1.getBaseBufferCount() == mq2.getBaseBufferCount()) - && (mq1.getBitPattern() == mq2.getBitPattern()) ); - - final boolean b2; - if (mq1.isEmpty()) { - b2 = mq2.isEmpty(); - } else { - b2 = (mq1.getMinItem() == mq2.getMinItem()) && (mq1.getMaxItem() == mq2.getMaxItem()); - } - return b1 && b2; - } - - static UpdateDoublesSketch buildAndLoadQS(int k, int n) { - return buildAndLoadQS(k, n, 0); - } - - static UpdateDoublesSketch buildAndLoadQS(int k, int n, int startV) { - UpdateDoublesSketch qs = DoublesSketch.builder().setK(k).build(); - for (int i=1; i<=n; i++) { - qs.update(startV + i); - } - return qs; - } - - @Test - public void printlnTest() { - println("PRINTING: "+this.getClass().getName()); - print("PRINTING: "+this.getClass().getName() + LS); - } - - /** - * @param s value to print - */ - static void println(String s) { - print(s+LS); - } - - /** - * @param s value to print - */ - static void print(String s) { - //System.err.print(s); //disable here - } - -} diff --git a/src/test/java/org/apache/datasketches/quantiles/ItemsSketchTest.java b/src/test/java/org/apache/datasketches/quantiles/ItemsSketchTest.java index 4d8882499..808299301 100644 --- a/src/test/java/org/apache/datasketches/quantiles/ItemsSketchTest.java +++ b/src/test/java/org/apache/datasketches/quantiles/ItemsSketchTest.java @@ -378,7 +378,7 @@ public void checkDownsampleException() { sketch.downSample(32); } - @Test(expectedExceptions = SketchesArgumentException.class) + @Test(expectedExceptions = IllegalArgumentException.class) public void zeroEvenlySpacedMustThrow() { final ItemsSketch sketch = ItemsSketch.getInstance(String.class, 16, Comparator.naturalOrder()); sketch.update("a"); From 9c88a5a7404a02f1b73cfeb52067e84532c15682 Mon Sep 17 00:00:00 2001 From: Lee Rhodes Date: Sun, 19 Mar 2023 20:59:33 -0700 Subject: [PATCH 3/4] Reverted and added back the file that was accidentally removed. --- .../HeapUpdateDoublesSketchTest.java | 1072 +++++++++++++++++ 1 file changed, 1072 insertions(+) create mode 100644 src/test/java/org/apache/datasketches/quantiles/HeapUpdateDoublesSketchTest.java diff --git a/src/test/java/org/apache/datasketches/quantiles/HeapUpdateDoublesSketchTest.java b/src/test/java/org/apache/datasketches/quantiles/HeapUpdateDoublesSketchTest.java new file mode 100644 index 000000000..7feaa36fb --- /dev/null +++ b/src/test/java/org/apache/datasketches/quantiles/HeapUpdateDoublesSketchTest.java @@ -0,0 +1,1072 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.datasketches.quantiles; + +import static java.lang.Math.floor; +import static org.apache.datasketches.common.Util.log2; +import static org.apache.datasketches.quantiles.ClassicUtil.LS; +import static org.apache.datasketches.quantiles.ClassicUtil.computeCombinedBufferItemCapacity; +import static org.apache.datasketches.quantiles.ClassicUtil.computeNumLevelsNeeded; +import static org.apache.datasketches.quantiles.HeapUpdateDoublesSketch.checkPreLongsFlagsSerVer; +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.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +import java.nio.ByteOrder; + +import org.apache.datasketches.common.SketchesArgumentException; +import org.apache.datasketches.memory.Memory; +import org.apache.datasketches.memory.WritableMemory; +import org.apache.datasketches.quantilescommon.QuantilesUtil; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +public class HeapUpdateDoublesSketchTest { + + @BeforeMethod + public void setUp() { + DoublesSketch.rand.setSeed(32749); // make sketches deterministic for testing + } + + // Please note that this is a randomized test that could probabilistically fail + // if we didn't set the seed. (The probability of failure could be reduced by increasing k.) + // Setting the seed has now made it deterministic. + @Test + public void checkEndToEnd() { + int k = 256; + UpdateDoublesSketch qs = DoublesSketch.builder().setK(k).build(); + UpdateDoublesSketch qs2 = DoublesSketch.builder().setK(k).build(); + int n = 1000000; + for (int item = n; item >= 1; item--) { + if ((item % 4) == 0) { + qs.update(item); + } + else { + qs2.update(item); + } + } + assertEquals(qs.getN() + qs2.getN(), n); + DoublesUnion union = DoublesUnion.heapify(qs); + union.union(qs2); + DoublesSketch result = union.getResult(); + + int numPhiValues = 99; + double[] phiArr = new double[numPhiValues]; + for (int q = 1; q <= 99; q++) { + phiArr[q-1] = q / 100.0; + } + double[] splitPoints = result.getQuantiles(phiArr); + +// for (int i = 0; i < 99; i++) { +// String s = String.format("%d\t%.6f\t%.6f", i, phiArr[i], splitPoints[i]); +// println(s); +// } + + for (int q = 1; q <= 99; q++) { + double nominal = (1e6 * q) / 100.0; + double reported = splitPoints[q-1]; + assertTrue(reported >= (nominal - 10000.0)); + assertTrue(reported <= (nominal + 10000.0)); + } + + double[] pmfResult = result.getPMF(splitPoints); + double subtotal = 0.0; + for (int q = 1; q <= 100; q++) { + double phi = q / 100.0; + subtotal += pmfResult[q-1]; + assertTrue(subtotal >= (phi - 0.01)); + assertTrue(subtotal <= (phi + 0.01)); + } + + double[] cdfResult = result.getCDF(splitPoints); + for (int q = 1; q <= 100; q++) { + double phi = q / 100.0; + subtotal = cdfResult[q-1]; + assertTrue(subtotal >= (phi - 0.01)); + assertTrue(subtotal <= (phi + 0.01)); + } + + assertEquals(result.getRank(500000), 0.5, 0.01); + } + + @Test + public void checkSmallMinMax () { + int k = 32; + int n = 8; + UpdateDoublesSketch qs1 = DoublesSketch.builder().setK(k).build(); + UpdateDoublesSketch qs2 = DoublesSketch.builder().setK(k).build(); + UpdateDoublesSketch qs3 = DoublesSketch.builder().setK(k).build(); + + for (int i = n; i >= 1; i--) { + qs1.update(i); + qs2.update(10+i); + qs3.update(i); + } + assertEquals(qs1.getQuantile (0.0, EXCLUSIVE), 1.0); + assertEquals(qs1.getQuantile (0.5, EXCLUSIVE), 5.0); + assertEquals(qs1.getQuantile (1.0, EXCLUSIVE), 8.0); + + assertEquals(qs2.getQuantile (0.0, EXCLUSIVE), 11.0); + assertEquals(qs2.getQuantile (0.5, EXCLUSIVE), 15.0); + assertEquals(qs2.getQuantile (1.0, EXCLUSIVE), 18.0); + + assertEquals(qs3.getQuantile (0.0, EXCLUSIVE), 1.0); + assertEquals(qs3.getQuantile (0.5, EXCLUSIVE), 5.0); + assertEquals(qs3.getQuantile (1.0, EXCLUSIVE), 8.0); + + double[] queries = {0.0, 0.5, 1.0}; + + double[] resultsA = qs1.getQuantiles(queries, EXCLUSIVE); + assertEquals(resultsA[0], 1.0); + assertEquals(resultsA[1], 5.0); + assertEquals(resultsA[2], 8.0); + + DoublesUnion union1 = DoublesUnion.heapify(qs1); + union1.union(qs2); + DoublesSketch result1 = union1.getResult(); + + DoublesUnion union2 = DoublesUnion.heapify(qs2); + union2.union(qs3); + DoublesSketch result2 = union2.getResult(); + + double[] resultsB = result1.getQuantiles(queries, EXCLUSIVE); + assertEquals(resultsB[0], 1.0); + assertEquals(resultsB[1], 11.0); + assertEquals(resultsB[2], 18.0); + + double[] resultsC = result2.getQuantiles(queries, EXCLUSIVE); + assertEquals(resultsC[0], 1.0); + assertEquals(resultsC[1], 11.0); + assertEquals(resultsC[2], 18.0); + } + + @Test + public void checkMisc() { + int k = PreambleUtil.DEFAULT_K; + int n = 10000; + UpdateDoublesSketch qs = buildAndLoadQS(k, n); + qs.update(Double.NaN); //ignore + int n2 = (int)qs.getN(); + assertEquals(n2, n); + + qs.reset(); + assertEquals(qs.getN(), 0); + } + + @SuppressWarnings("unused") + @Test + public void checkToStringDetail() { + int k = PreambleUtil.DEFAULT_K; + int n = 1000000; + UpdateDoublesSketch qs = buildAndLoadQS(k, 0); + String s = qs.toString(); + s = qs.toString(false, true); + //println(s); + qs = buildAndLoadQS(k, n); + s = qs.toString(); + //println(s); + s = qs.toString(false, true); + //println(qs.toString(false, true)); + + int n2 = (int)qs.getN(); + assertEquals(n2, n); + qs.update(Double.NaN); //ignore + qs.reset(); + assertEquals(qs.getN(), 0); + } + + @Test(expectedExceptions = SketchesArgumentException.class) + public void checkConstructorException() { + DoublesSketch.builder().setK(0).build(); + } + + @Test + public void checkPreLongsFlagsAndSize() { + byte[] byteArr; + UpdateDoublesSketch ds = DoublesSketch.builder().build(); //k = 128 + //empty + byteArr = ds.toByteArray(true); // compact + assertEquals(byteArr.length, 8); + + byteArr = ds.toByteArray(false); // not compact + assertEquals(byteArr.length, 8); + assertEquals(byteArr[3], EMPTY_FLAG_MASK); + + //not empty + ds.update(1); + byteArr = ds.toByteArray(true); // compact + assertEquals(byteArr.length, 40); //compact, 1 value + + byteArr = ds.toByteArray(false); // not compact + assertEquals(byteArr.length, 64); // 32 + MIN_K(=2) * 2 * 8 = 64 + } + + @Test + public void checkPreLongsFlagsSerVerB() { + checkPreLongsFlagsSerVer(EMPTY_FLAG_MASK, 1, 1); //38 + checkPreLongsFlagsSerVer(0, 1, 5); //164 + checkPreLongsFlagsSerVer(EMPTY_FLAG_MASK, 2, 1); //42 + checkPreLongsFlagsSerVer(0, 2, 2); //72 + checkPreLongsFlagsSerVer(EMPTY_FLAG_MASK | COMPACT_FLAG_MASK, 3, 1); //47 + checkPreLongsFlagsSerVer(EMPTY_FLAG_MASK | COMPACT_FLAG_MASK, 3, 2); //79 + checkPreLongsFlagsSerVer(EMPTY_FLAG_MASK, 3, 2); //78 + checkPreLongsFlagsSerVer(COMPACT_FLAG_MASK, 3, 2);//77 + checkPreLongsFlagsSerVer(0, 3, 2); //76 + } + + @Test(expectedExceptions = SketchesArgumentException.class) + public void checkPreLongsFlagsSerVer3() { + checkPreLongsFlagsSerVer(EMPTY_FLAG_MASK, 1, 2); + } + + @Test(expectedExceptions = SketchesArgumentException.class) + public void checkGetQuantiles() { + int k = PreambleUtil.DEFAULT_K; + int n = 1000000; + DoublesSketch qs = buildAndLoadQS(k, n); + double[] frac = {-0.5}; + qs.getQuantiles(frac); + } + + @Test(expectedExceptions = SketchesArgumentException.class) + public void checkGetQuantile() { + int k = PreambleUtil.DEFAULT_K; + int n = 1000000; + DoublesSketch qs = buildAndLoadQS(k, n); + double frac = -0.5; //negative not allowed + qs.getQuantile(frac); + } + + //@Test //visual only + public void summaryCheckViaMemory() { + DoublesSketch qs = buildAndLoadQS(256, 1000000); + String s = qs.toString(); + println(s); + println(""); + + Memory srcMem = WritableMemory.writableWrap(qs.toByteArray()); + + HeapUpdateDoublesSketch qs2 = HeapUpdateDoublesSketch.heapifyInstance(srcMem); + s = qs2.toString(); + println(s); + } + + @Test + public void checkComputeNumLevelsNeeded() { + int n = 1 << 20; + int k = PreambleUtil.DEFAULT_K; + int lvls1 = computeNumLevelsNeeded(k, n); + int lvls2 = (int)Math.max(floor(log2((double)n/k)),0); + assertEquals(lvls1, lvls2); + } + + @Test + public void checkComputeBitPattern() { + int n = 1 << 20; + int k = PreambleUtil.DEFAULT_K; + long bitP = ClassicUtil.computeBitPattern(k, n); + assertEquals(bitP, n/(2L*k)); + } + + @Test(expectedExceptions = SketchesArgumentException.class) + public void checkValidateSplitPointsOrder() { + double[] arr = {2, 1}; + QuantilesUtil.checkDoublesSplitPointsOrder(arr); + } + + @Test + public void checkGetStorageBytes() { + int k = PreambleUtil.DEFAULT_K; //128 + DoublesSketch qs = buildAndLoadQS(k, 0); //k, n + int stor = qs.getCurrentCompactSerializedSizeBytes(); + assertEquals(stor, 8); + + qs = buildAndLoadQS(k, 2*k); //forces one level + stor = qs.getCurrentCompactSerializedSizeBytes(); + + int retItems = ClassicUtil.computeRetainedItems(k, 2*k); + assertEquals(stor, 32 + (retItems << 3)); + + qs = buildAndLoadQS(k, (2*k)-1); //just Base Buffer + stor = qs.getCurrentCompactSerializedSizeBytes(); + retItems = ClassicUtil.computeRetainedItems(k, (2*k)-1); + assertEquals(stor, 32 + (retItems << 3)); + } + + @Test + public void checkGetStorageBytes2() { + int k = PreambleUtil.DEFAULT_K; + long v = 1; + UpdateDoublesSketch qs = DoublesSketch.builder().setK(k).build(); + for (int i = 0; i< 1000; i++) { + qs.update(v++); +// for (int j = 0; j < 1000; j++) { +// qs.update(v++); +// } + byte[] byteArr = qs.toByteArray(false); + assertEquals(byteArr.length, qs.getCurrentUpdatableSerializedSizeBytes()); + } + } + + @Test + public void checkMerge() { + int k = PreambleUtil.DEFAULT_K; + int n = 1000000; + DoublesSketch qs1 = buildAndLoadQS(k,n,0); + DoublesSketch qs2 = buildAndLoadQS(k,0,0); //empty + DoublesUnion union = DoublesUnion.heapify(qs2); + union.union(qs1); + DoublesSketch result = union.getResult(); + double med1 = qs1.getQuantile(0.5); + double med2 = result.getQuantile(0.5); + assertEquals(med1, med2, 0.0); + //println(med1+","+med2); + } + + @Test + public void checkReverseMerge() { + int k = PreambleUtil.DEFAULT_K; + DoublesSketch qs1 = buildAndLoadQS(k, 1000, 0); + DoublesSketch qs2 = buildAndLoadQS(2*k,1000, 1000); + DoublesUnion union = DoublesUnion.heapify(qs2); + union.union(qs1); //attempt merge into larger k + DoublesSketch result = union.getResult(); + assertEquals(result.getK(), k); + } + + @Test + public void checkInternalBuildHistogram() { + int k = PreambleUtil.DEFAULT_K; + int n = 1000000; + DoublesSketch qs = buildAndLoadQS(k,n,0); + double eps = qs.getNormalizedRankError(true); + //println("EPS:"+eps); + double[] spts = {100000, 500000, 900000}; + double[] fracArr = qs.getPMF(spts); +// println(fracArr[0]+", "+ (fracArr[0]-0.1)); +// println(fracArr[1]+", "+ (fracArr[1]-0.4)); +// println(fracArr[2]+", "+ (fracArr[2]-0.4)); +// println(fracArr[3]+", "+ (fracArr[3]-0.1)); + assertEquals(fracArr[0], .1, eps); + assertEquals(fracArr[1], .4, eps); + assertEquals(fracArr[2], .4, eps); + assertEquals(fracArr[3], .1, eps); + } + + @Test + public void checkComputeBaseBufferCount() { + int n = 1 << 20; + int k = PreambleUtil.DEFAULT_K; + long bbCnt = ClassicUtil.computeBaseBufferItems(k, n); + assertEquals(bbCnt, n % (2L*k)); + } + + @Test + public void checkToFromByteArray() { + checkToFromByteArray2(128, 1300); //generates a pattern of 5 -> 101 + checkToFromByteArray2(4, 7); + checkToFromByteArray2(4, 8); + checkToFromByteArray2(4, 9); + } + + private static void checkToFromByteArray2(int k, int n) { + DoublesSketch qs = buildAndLoadQS(k, n); + byte[] byteArr; + Memory mem; + DoublesSketch qs2; + + // from compact + byteArr = qs.toByteArray(true); + mem = Memory.wrap(byteArr); + qs2 = UpdateDoublesSketch.heapify(mem); + for (double f = 0.1; f < 0.95; f += 0.1) { + assertEquals(qs.getQuantile(f), qs2.getQuantile(f), 0.0); + } + + // ordered, non-compact + byteArr = qs.toByteArray(false); + mem = Memory.wrap(byteArr); + qs2 = DoublesSketch.heapify(mem); + final DoublesSketchAccessor dsa = DoublesSketchAccessor.wrap(qs2); + dsa.sort(); + for (double f = 0.1; f < 0.95; f += 0.1) { + assertEquals(qs.getQuantile(f), qs2.getQuantile(f), 0.0); + } + + // not ordered, not compact + byteArr = qs.toByteArray(false); + mem = Memory.wrap(byteArr); + qs2 = DoublesSketch.heapify(mem); + for (double f = 0.1; f < 0.95; f += 0.1) { + assertEquals(qs.getQuantile(f), qs2.getQuantile(f), 0.0); + } + } + + @Test + public void checkEmpty() { + int k = PreambleUtil.DEFAULT_K; + DoublesSketch qs1 = buildAndLoadQS(k, 0); + byte[] byteArr = qs1.toByteArray(); + Memory mem = Memory.wrap(byteArr); + DoublesSketch qs2 = DoublesSketch.heapify(mem); + assertTrue(qs2.isEmpty()); + final int expectedSizeBytes = 8; //COMBINED_BUFFER + ((2 * MIN_K) << 3); + assertEquals(byteArr.length, expectedSizeBytes); + try { qs2.getQuantile(0.5); fail(); } catch (IllegalArgumentException e) { } + try { qs2.getQuantiles(new double[] {0.0, 0.5, 1.0}); fail(); } catch (IllegalArgumentException e) { } + try { qs2.getRank(0); fail(); } catch (IllegalArgumentException e) { } + } + + @Test(expectedExceptions = SketchesArgumentException.class) + public void checkMemTooSmall1() { + Memory mem = Memory.wrap(new byte[7]); + HeapUpdateDoublesSketch.heapifyInstance(mem); + fail(); + //qs2.getQuantile(0.5); + } + + //Corruption tests + @Test(expectedExceptions = SketchesArgumentException.class) + public void checkSerVer() { + DoublesUtil.checkDoublesSerVer(0, HeapUpdateDoublesSketch.MIN_HEAP_DOUBLES_SER_VER); + } + + @Test(expectedExceptions = SketchesArgumentException.class) + public void checkFamilyID() { + ClassicUtil.checkFamilyID(3); + } + + @Test(expectedExceptions = SketchesArgumentException.class) + public void checkMemCapacityException() { + int k = PreambleUtil.DEFAULT_K; + long n = 1000; + int serVer = 3; + int combBufItemCap = computeCombinedBufferItemCapacity(k, n); + int memCapBytes = (combBufItemCap + 4) << 3; + int badCapBytes = memCapBytes - 1; //corrupt + HeapUpdateDoublesSketch.checkHeapMemCapacity(k, n, false, serVer, badCapBytes); + } + + @Test(expectedExceptions = SketchesArgumentException.class) + public void checkBufAllocAndCap() { + int k = PreambleUtil.DEFAULT_K; + long n = 1000; + int serVer = 3; + int combBufItemCap = computeCombinedBufferItemCapacity(k, n); //non-compact cap + int memCapBytes = (combBufItemCap + 4) << 3; + int memCapBytesV1 = (combBufItemCap + 5) << 3; + HeapUpdateDoublesSketch.checkHeapMemCapacity(k, n, false, 1, memCapBytesV1); + HeapUpdateDoublesSketch.checkHeapMemCapacity(k, n, false, serVer, memCapBytes - 1); //corrupt + } + + @Test(expectedExceptions = SketchesArgumentException.class) + public void checkPreLongsFlagsCap() { + int preLongs = 5; + int flags = EMPTY_FLAG_MASK; + int memCap = 8; + ClassicUtil.checkPreLongsFlagsCap(preLongs, flags, memCap); //corrupt + } + + @Test(expectedExceptions = SketchesArgumentException.class) + public void checkPreLongsFlagsCap2() { + int preLongs = 5; + int flags = 0; + int memCap = 8; + ClassicUtil.checkPreLongsFlagsCap(preLongs, flags, memCap); //corrupt + } + + @Test(expectedExceptions = SketchesArgumentException.class) + public void checkFlags() { + int flags = 1; + ClassicUtil.checkHeapFlags(flags); + } + + @Test + public void checkZeroPatternReturn() { + int k = PreambleUtil.DEFAULT_K; + DoublesSketch qs1 = buildAndLoadQS(k, 64); + byte[] byteArr = qs1.toByteArray(); + Memory mem = Memory.wrap(byteArr); + HeapUpdateDoublesSketch.heapifyInstance(mem); + } + + @Test(expectedExceptions = SketchesArgumentException.class) + public void checkBadDownSamplingRatio() { + int k1 = 64; + DoublesSketch qs1 = buildAndLoadQS(k1, k1); + qs1.downSample(qs1, 2*k1, null);//should be smaller + } + + @Test + public void checkImproperKvalues() { + checksForImproperK(0); + checksForImproperK(1<<16); + } + + //Primarily visual only tests + static void testDownSampling(int bigK, int smallK) { + HeapUpdateDoublesSketch sketch1 = HeapUpdateDoublesSketch.newInstance(bigK); + HeapUpdateDoublesSketch sketch2 = HeapUpdateDoublesSketch.newInstance(smallK); + for (int i = 127; i >= 1; i--) { + sketch1.update (i); + sketch2.update (i); + } + HeapUpdateDoublesSketch downSketch = + (HeapUpdateDoublesSketch)sketch1.downSample(sketch1, smallK, null); + println (LS+"Sk1"+LS); + String s1, s2, down; + s1 = sketch1.toString(true, true); + println(s1); + println (LS+"Down"+LS); + down = downSketch.toString(true, true); + println(down); + println(LS+"Sk2"+LS); + s2 = sketch2.toString(true, true); + println(s2); + assertEquals(downSketch.getNumRetained(), sketch2.getNumRetained()); + } + + @Test + public void checkDownSampling() { + testDownSampling(4,4); //no down sampling + testDownSampling(16,4); + //testDownSampling(12,3); + } + + @Test + public void testDownSampling2() { + HeapUpdateDoublesSketch sketch1 = HeapUpdateDoublesSketch.newInstance(8); + HeapUpdateDoublesSketch sketch2 = HeapUpdateDoublesSketch.newInstance(2); + DoublesSketch downSketch; + downSketch = sketch1.downSample(sketch1, 2, null); + assertTrue(sameStructurePredicate(sketch2, downSketch)); + for (int i = 0; i < 50; i++) { + sketch1.update (i); + sketch2.update (i); + downSketch = sketch1.downSample(sketch1, 2, null); + assertTrue (sameStructurePredicate(sketch2, downSketch)); + } + } + + @Test + public void testDownSampling3() { + int k1 = 8; + int k2 = 2; + int n = 50; + UpdateDoublesSketch sketch1 = DoublesSketch.builder().setK(k1).build(); + UpdateDoublesSketch sketch2 = DoublesSketch.builder().setK(k2).build(); + DoublesSketch downSketch; + for (int i = 0; i < n; i++) { + sketch1.update (i); + sketch2.update (i); + downSketch = sketch1.downSample(sketch1, k2, null); + assertTrue (sameStructurePredicate(sketch2, downSketch)); + } + } + + @Test // + public void testDownSampling3withMem() { + int k1 = 8; + int k2 = 2; + int n = 50; + UpdateDoublesSketch sketch1 = DoublesSketch.builder().setK(k1).build(); + UpdateDoublesSketch sketch2 = DoublesSketch.builder().setK(k2).build(); + DoublesSketch downSketch; + int bytes = DoublesSketch.getUpdatableStorageBytes(k2, n); + WritableMemory mem = WritableMemory.writableWrap(new byte[bytes]); + for (int i = 0; i < n; i++) { + sketch1.update (i); + sketch2.update (i); + + downSketch = sketch1.downSample(sketch1, k2, mem); + assertTrue (sameStructurePredicate(sketch2, downSketch)); + } + + } + + + @Test + public void testDownSampling4() { + for (int n1 = 0; n1 < 50; n1++ ) { + HeapUpdateDoublesSketch bigSketch = HeapUpdateDoublesSketch.newInstance(8); + for (int i1 = 1; i1 <= n1; i1++ ) { + bigSketch.update(i1); + } + for (int n2 = 0; n2 < 50; n2++ ) { + HeapUpdateDoublesSketch directSketch = HeapUpdateDoublesSketch.newInstance(2); + for (int i1 = 1; i1 <= n1; i1++ ) { + directSketch.update(i1); + } + for (int i2 = 1; i2 <= n2; i2++ ) { + directSketch.update(i2); + } + HeapUpdateDoublesSketch smlSketch = HeapUpdateDoublesSketch.newInstance(2); + for (int i2 = 1; i2 <= n2; i2++ ) { + smlSketch.update(i2); + } + DoublesMergeImpl.downSamplingMergeInto(bigSketch, smlSketch); + assertTrue (sameStructurePredicate(directSketch, smlSketch)); + } + } + } + + @Test(expectedExceptions = SketchesArgumentException.class) + public void testDownSamplingExceptions1() { + UpdateDoublesSketch qs1 = DoublesSketch.builder().setK(4).build(); // not smaller + DoublesSketch qs2 = DoublesSketch.builder().setK(3).build(); + DoublesMergeImpl.mergeInto(qs2, qs1); + } + + @Test(expectedExceptions = SketchesArgumentException.class) + public void testDownSamplingExceptions2() { + UpdateDoublesSketch qs1 = DoublesSketch.builder().setK(4).build(); + DoublesSketch qs2 = DoublesSketch.builder().setK(7).build(); // 7/4 not pwr of 2 + DoublesMergeImpl.mergeInto(qs2, qs1); + } + + @Test(expectedExceptions = SketchesArgumentException.class) + public void testDownSamplingExceptions3() { + UpdateDoublesSketch qs1 = DoublesSketch.builder().setK(4).build(); + DoublesSketch qs2 = DoublesSketch.builder().setK(12).build(); // 12/4 not pwr of 2 + DoublesMergeImpl.mergeInto(qs2, qs1); + } + + //@Test //visual only + public void quantilesCheckViaMemory() { + int k = 256; + int n = 1000000; + DoublesSketch qs = buildAndLoadQS(k, n); + double[] ranks = {0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}; + String s = getRanksTable(qs, ranks); + println(s); + println(""); + + Memory srcMem = Memory.wrap(qs.toByteArray()); + + HeapUpdateDoublesSketch qs2 = HeapUpdateDoublesSketch.heapifyInstance(srcMem); + println(getRanksTable(qs2, ranks)); + } + + static String getRanksTable(DoublesSketch qs, double[] ranks) { + double rankError = qs.getNormalizedRankError(false); + double[] values = qs.getQuantiles(ranks); + double maxV = qs.getMaxItem(); + double minV = qs.getMinItem(); + double delta = maxV - minV; + println("Note: This prints the relative value errors for illustration."); + println("The quantiles sketch does not and can not guarantee relative value errors"); + + StringBuilder sb = new StringBuilder(); + sb.append(LS); + sb.append("N = ").append(qs.getN()).append(LS); + sb.append("K = ").append(qs.getK()).append(LS); + String formatStr1 = "%10s%15s%10s%15s%10s%10s"; + String formatStr2 = "%10.1f%15.5f%10.0f%15.5f%10.5f%10.5f"; + String hdr = String.format( + formatStr1, "Rank", "ValueLB", "<= Value", "<= ValueUB", "RelErrLB", "RelErrUB"); + sb.append(hdr).append(LS); + for (int i=0; i valueLB); + + double valRelPctErrUB = (valueUB/ value) -1.0; + double valRelPctErrLB = (valueLB/ value) -1.0; + String row = String.format( + formatStr2,rank, valueLB, value, valueUB, valRelPctErrLB, valRelPctErrUB); + sb.append(row).append(LS); + } + } + return sb.toString(); + } + + @Test + public void checkKisTwo() { + int k = 2; + UpdateDoublesSketch qs1 = DoublesSketch.builder().setK(k).build(); + double err = qs1.getNormalizedRankError(false); + assertTrue(err < 1.0); + byte[] arr = qs1.toByteArray(true); //8 + assertEquals(arr.length, DoublesSketch.getCompactSerialiedSizeBytes(k, 0)); + qs1.update(1.0); + arr = qs1.toByteArray(true); //40 + assertEquals(arr.length, DoublesSketch.getCompactSerialiedSizeBytes(k, 1)); + } + + @Test + public void checkKisTwoDeprecated() { + int k = 2; + UpdateDoublesSketch qs1 = DoublesSketch.builder().setK(k).build(); + double err = qs1.getNormalizedRankError(false); + assertTrue(err < 1.0); + byte[] arr = qs1.toByteArray(true); //8 + assertEquals(arr.length, DoublesSketch.getCompactSerialiedSizeBytes(k, 0)); + assertEquals(arr.length, qs1.getCurrentCompactSerializedSizeBytes()); + qs1.update(1.0); + arr = qs1.toByteArray(true); //40 + assertEquals(arr.length, DoublesSketch.getCompactSerialiedSizeBytes(k, 1)); + assertEquals(arr.length, qs1.getCurrentCompactSerializedSizeBytes()); + } + + @Test + public void checkPutMemory() { + UpdateDoublesSketch qs1 = DoublesSketch.builder().build(); //k = 128 + for (int i=0; i<1000; i++) { + qs1.update(i); + } + int bytes = qs1.getCurrentUpdatableSerializedSizeBytes(); + WritableMemory dstMem = WritableMemory.writableWrap(new byte[bytes]); + qs1.putMemory(dstMem, false); + Memory srcMem = dstMem; + DoublesSketch qs2 = DoublesSketch.heapify(srcMem); + assertEquals(qs1.getMinItem(), qs2.getMinItem(), 0.0); + assertEquals(qs1.getMaxItem(), qs2.getMaxItem(), 0.0); + } + + @Test(expectedExceptions = SketchesArgumentException.class) + public void checkPutMemoryTooSmall() { + UpdateDoublesSketch qs1 = DoublesSketch.builder().build(); //k = 128 + for (int i=0; i<1000; i++) { + qs1.update(i); + } + int bytes = qs1.getCurrentCompactSerializedSizeBytes(); + WritableMemory dstMem = WritableMemory.writableWrap(new byte[bytes-1]); //too small + qs1.putMemory(dstMem); + } + + //Himanshu's case + @Test + public void testIt() { + java.nio.ByteBuffer bb = java.nio.ByteBuffer.allocate(1<<20).order(ByteOrder.nativeOrder()); + WritableMemory mem = WritableMemory.writableWrap(bb); + + int k = 1024; + DoublesSketch qsk = new DoublesSketchBuilder().setK(k).build(); + DoublesUnion u1 = DoublesUnion.heapify(qsk); + u1.getResult().putMemory(mem); + DoublesUnion u2 = DoublesUnion.heapify(mem); + DoublesSketch qsk2 = u2.getResult(); + assertTrue(qsk2.isEmpty()); + } + + @Test + public void checkEvenlySpacedQuantiles() { + DoublesSketch qsk = buildAndLoadQS(32, 1001); + double[] values = qsk.getQuantiles(11); + for (int i = 0; i= ClassicUtil.computeCombinedBufferItemCapacity(mq1.getK(), mq1.getN())) + && (mq2.getCombinedBufferItemCapacity() + >= ClassicUtil.computeCombinedBufferItemCapacity(mq2.getK(), mq2.getN())) + && (mq1.getBaseBufferCount() == mq2.getBaseBufferCount()) + && (mq1.getBitPattern() == mq2.getBitPattern()) ); + + final boolean b2; + if (mq1.isEmpty()) { + b2 = mq2.isEmpty(); + } else { + b2 = (mq1.getMinItem() == mq2.getMinItem()) && (mq1.getMaxItem() == mq2.getMaxItem()); + } + return b1 && b2; + } + + static UpdateDoublesSketch buildAndLoadQS(int k, int n) { + return buildAndLoadQS(k, n, 0); + } + + static UpdateDoublesSketch buildAndLoadQS(int k, int n, int startV) { + UpdateDoublesSketch qs = DoublesSketch.builder().setK(k).build(); + for (int i=1; i<=n; i++) { + qs.update(startV + i); + } + return qs; + } + + @Test + public void printlnTest() { + println("PRINTING: "+this.getClass().getName()); + print("PRINTING: "+this.getClass().getName() + LS); + } + + /** + * @param s value to print + */ + static void println(String s) { + print(s+LS); + } + + /** + * @param s value to print + */ + static void print(String s) { + //System.err.print(s); //disable here + } + +} From f06c443d1eaeb6897bec30b0da11d460b3ffc8f5 Mon Sep 17 00:00:00 2001 From: Lee Rhodes Date: Sun, 19 Mar 2023 21:06:11 -0700 Subject: [PATCH 4/4] Fixed malformed HTML: accidentally used a "<" in javadocs. --- .../datasketches/quantilescommon/QuantilesDoublesAPI.java | 2 +- .../apache/datasketches/quantilescommon/QuantilesFloatsAPI.java | 2 +- .../datasketches/quantilescommon/QuantilesGenericAPI.java | 2 +- .../org/apache/datasketches/quantilescommon/QuantilesUtil.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesDoublesAPI.java b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesDoublesAPI.java index 5479b2949..e4083567a 100644 --- a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesDoublesAPI.java +++ b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesDoublesAPI.java @@ -253,7 +253,7 @@ default double[] getQuantiles(int numEvenlySpaced) { * @param searchCrit if INCLUSIVE, the given ranks include all quantiles ≤ the quantile directly corresponding to * each rank. * @return an array of quantiles that are evenly spaced by their ranks. - * @throws IllegalArgumentException if sketch is empty or if numEvenlySpaced < 1. + * @throws IllegalArgumentException if sketch is empty or if numEvenlySpaced is less than 1. * @see org.apache.datasketches.quantilescommon.QuantileSearchCriteria */ double[] getQuantiles(int numEvenlySpaced, QuantileSearchCriteria searchCrit); diff --git a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesFloatsAPI.java b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesFloatsAPI.java index 528a9212b..91ecfff45 100644 --- a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesFloatsAPI.java +++ b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesFloatsAPI.java @@ -252,7 +252,7 @@ default float[] getQuantiles(int numEvenlySpaced) { * @param searchCrit if INCLUSIVE, the given ranks include all quantiles ≤ the quantile directly corresponding to * each rank. * @return an array of quantiles that are evenly spaced by their ranks. - * @throws IllegalArgumentException if sketch is empty or if numEvenlySpaced < 1. + * @throws IllegalArgumentException if sketch is empty or if numEvenlySpaced is less than 1. * @see org.apache.datasketches.quantilescommon.QuantileSearchCriteria */ float[] getQuantiles(int numEvenlySpaced, QuantileSearchCriteria searchCrit); diff --git a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesGenericAPI.java b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesGenericAPI.java index 20150870b..b6d15b603 100644 --- a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesGenericAPI.java +++ b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesGenericAPI.java @@ -253,7 +253,7 @@ default T[] getQuantiles(int numEvenlySpaced) { * @param searchCrit if INCLUSIVE, the given ranks include all quantiles ≤ the quantile directly corresponding to * each rank. * @return an array of quantiles that are evenly spaced by their ranks. - * @throws IllegalArgumentException if sketch is empty or if numEvenlySpaced < 1. + * @throws IllegalArgumentException if sketch is empty or if numEvenlySpaced is less than 1. * @see org.apache.datasketches.quantilescommon.QuantileSearchCriteria */ T[] getQuantiles(int numEvenlySpaced, QuantileSearchCriteria searchCrit); diff --git a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesUtil.java b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesUtil.java index a57cb8e42..dc4692004 100644 --- a/src/main/java/org/apache/datasketches/quantilescommon/QuantilesUtil.java +++ b/src/main/java/org/apache/datasketches/quantilescommon/QuantilesUtil.java @@ -88,7 +88,7 @@ public static final void checkFloatsSplitPointsOrder(final float[] values) { * @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 num < 1. + * @throws IllegalArgumentException if num is less than 1. */ public static double[] evenlySpaced(final double value1, final double value2, final int num) { if (num < 1) {