• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

apache / datasketches-java / #306

30 Apr 2024 10:01PM UTC coverage: 97.645% (-0.5%) from 98.139%
#306

push

web-flow
Merge pull request #555 from apache/fix_pom_xml_header

Fix pom xml header

26865 of 27513 relevant lines covered (97.64%)

0.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.2
/src/main/java/org/apache/datasketches/quantiles/ItemsSketch.java
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one
3
 * or more contributor license agreements.  See the NOTICE file
4
 * distributed with this work for additional information
5
 * regarding copyright ownership.  The ASF licenses this file
6
 * to you under the Apache License, Version 2.0 (the
7
 * "License"); you may not use this file except in compliance
8
 * with the License.  You may obtain a copy of the License at
9
 *
10
 *   http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing,
13
 * software distributed under the License is distributed on an
14
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
 * KIND, either express or implied.  See the License for the
16
 * specific language governing permissions and limitations
17
 * under the License.
18
 */
19

20
package org.apache.datasketches.quantiles;
21

22
import static java.lang.Math.max;
23
import static java.lang.Math.min;
24
import static org.apache.datasketches.quantiles.ClassicUtil.MIN_K;
25
import static org.apache.datasketches.quantiles.ClassicUtil.checkFamilyID;
26
import static org.apache.datasketches.quantiles.ClassicUtil.checkK;
27
import static org.apache.datasketches.quantiles.ClassicUtil.checkPreLongsFlagsCap;
28
import static org.apache.datasketches.quantiles.ClassicUtil.computeBaseBufferItems;
29
import static org.apache.datasketches.quantiles.ClassicUtil.computeBitPattern;
30
import static org.apache.datasketches.quantiles.ClassicUtil.computeCombinedBufferItemCapacity;
31
import static org.apache.datasketches.quantiles.ClassicUtil.computeRetainedItems;
32
import static org.apache.datasketches.quantiles.PreambleUtil.COMPACT_FLAG_MASK;
33
import static org.apache.datasketches.quantiles.PreambleUtil.extractFamilyID;
34
import static org.apache.datasketches.quantiles.PreambleUtil.extractFlags;
35
import static org.apache.datasketches.quantiles.PreambleUtil.extractK;
36
import static org.apache.datasketches.quantiles.PreambleUtil.extractN;
37
import static org.apache.datasketches.quantiles.PreambleUtil.extractPreLongs;
38
import static org.apache.datasketches.quantiles.PreambleUtil.extractSerVer;
39

40
import java.lang.reflect.Array;
41
import java.util.Arrays;
42
import java.util.Comparator;
43
import java.util.Objects;
44
import java.util.Random;
45

46
import org.apache.datasketches.common.ArrayOfItemsSerDe;
47
import org.apache.datasketches.common.SketchesArgumentException;
48
import org.apache.datasketches.common.SketchesStateException;
49
import org.apache.datasketches.memory.Memory;
50
import org.apache.datasketches.memory.WritableMemory;
51
import org.apache.datasketches.quantilescommon.GenericPartitionBoundaries;
52
import org.apache.datasketches.quantilescommon.ItemsSketchSortedView;
53
import org.apache.datasketches.quantilescommon.QuantileSearchCriteria;
54
import org.apache.datasketches.quantilescommon.QuantilesAPI;
55
import org.apache.datasketches.quantilescommon.QuantilesGenericAPI;
56
import org.apache.datasketches.quantilescommon.QuantilesGenericSketchIterator;
57

58
/**
59
 * This is an implementation of the Low Discrepancy Mergeable Quantiles Sketch, using generic items,
60
 * described in section 3.2 of the journal version of the paper "Mergeable Summaries"
61
 * by Agarwal, Cormode, Huang, Phillips, Wei, and Yi:
62
 *
63
 * <p>Reference: <a href="http://dblp.org/rec/html/journals/tods/AgarwalCHPWY13"></a></p>
64
 *
65
 * <p>A <i>k</i> of 128 produces a normalized, rank error of about 1.7%.
66
 * For example, the median returned from getQuantile(0.5) will be between the actual quantiles
67
 * from the hypothetically sorted array of input quantiles at normalized ranks of 0.483 and 0.517, with
68
 * a confidence of about 99%.</p>
69
 *
70
 * <p>The size of an ItemsSketch is very dependent on the size of the generic Items input into the sketch,
71
 * so there is no comparable size table as there is for the DoublesSketch.</p>
72
 *
73
 * @see QuantilesAPI
74
 *
75
 * @param <T> The sketch data type
76
 */
77
public final class ItemsSketch<T> implements QuantilesGenericAPI<T> {
78
  final Class<T> clazz;
79
  private final Comparator<? super T> comparator_;
80
  final int k_;
81
  long n_;
82
  T maxItem_; //The largest item ever seen in the stream.
83
  T minItem_; //The smallest item ever seen in the stream.
84

85
  /**
86
   * In the on-heap version, this equals combinedBuffer_.length.
87
   * May differ in later versions that grow space more aggressively.
88
   * Also, in the off-heap version, combinedBuffer_ won't even be a java array,
89
   * so it won't know its own length.
90
   */
91
  int combinedBufferItemCapacity_;
92

93
  /**
94
   * Number of items currently in base buffer.
95
   *
96
   * <p>Count = N % (2 * K)</p>
97
   */
98
  int baseBufferCount_;
99

100
  /**
101
   * Active levels expressed as a bit pattern.
102
   *
103
   * <p>Pattern = N / (2 * K)</p>
104
   */
105
  long bitPattern_;
106

107
  /**
108
   * This single array contains the base buffer plus all levels some of which may not be used.
109
   * A level is of size K and is either full and sorted, or not used. A "not used" buffer may have
110
   * garbage. Whether a level buffer used or not is indicated by the bitPattern_.
111
   * The base buffer has length 2*K but might not be full and isn't necessarily sorted.
112
   * The base buffer precedes the level buffers.
113
   *
114
   * <p>The levels arrays require quite a bit of explanation, which we defer until later.</p>
115
   */
116
  Object[] combinedBuffer_;
117

118
  ItemsSketchSortedView<T> classicQisSV = null;
1✔
119

120
  /**
121
   * Setting the seed makes the results of the sketch deterministic if the input items are
122
   * received in exactly the same order. This is only useful when performing test comparisons,
123
   * otherwise, it is not recommended.
124
   */
125
  public static final Random rand = new Random();
1✔
126

127
  private ItemsSketch(
128
      final int k,
129
      final Class<T> clazz,
130
      final Comparator<? super T> comparator) {
1✔
131
    Objects.requireNonNull(clazz, "Class<T> must not be null.");
1✔
132
    Objects.requireNonNull(comparator, "Comparator must not be null.");
1✔
133
    checkK(k);
1✔
134
    k_ = k;
1✔
135
    this.clazz = clazz;
1✔
136
    comparator_ = comparator;
1✔
137
  }
1✔
138

139
  /**
140
   * Obtains a new instance of an ItemsSketch using the DEFAULT_K.
141
   * @param <T> The sketch data type
142
   * @param clazz the given class of T
143
   * @param comparator to compare items
144
   * @return an ItemSketch&lt;T&gt;.
145
   */
146
  public static <T> ItemsSketch<T> getInstance(
147
      final Class<T> clazz,
148
      final Comparator<? super T> comparator) {
149
    return getInstance(clazz, PreambleUtil.DEFAULT_K, comparator);
1✔
150
  }
151

152
  /**
153
   * Obtains a new instance of an ItemsSketch.
154
   * @param clazz the given class of T
155
   * @param k Parameter that controls space usage of sketch and accuracy of estimates.
156
   * Must be greater than 2 and less than 65536 and a power of 2.
157
   * @param comparator to compare items
158
   * @param <T> The sketch data type
159
   * @return an ItemSketch&lt;T&gt;.
160
   */
161
  public static <T> ItemsSketch<T> getInstance(
162
      final Class<T> clazz,
163
      final int k,
164
      final Comparator<? super T> comparator) {
165
    final ItemsSketch<T> qs = new ItemsSketch<>(k, clazz, comparator);
1✔
166
    final int bufAlloc = 2 * Math.min(MIN_K, k); //the min is important
1✔
167
    qs.n_ = 0;
1✔
168
    qs.combinedBufferItemCapacity_ = bufAlloc;
1✔
169
    qs.combinedBuffer_ = new Object[bufAlloc];
1✔
170
    qs.baseBufferCount_ = 0;
1✔
171
    qs.bitPattern_ = 0;
1✔
172
    qs.minItem_ = null;
1✔
173
    qs.maxItem_ = null;
1✔
174
    return qs;
1✔
175
  }
176

177
  /**
178
   * Heapifies the given srcMem, which must be a Memory image of a ItemsSketch
179
   * @param clazz the given class of T
180
   * @param srcMem a Memory image of a sketch.
181
   * <a href="{@docRoot}/resources/dictionary.html#mem">See Memory</a>
182
   * @param comparator to compare items
183
   * @param serDe an instance of ArrayOfItemsSerDe
184
   * @param <T> The sketch data type
185
   * @return a ItemSketch&lt;T&gt; on the Java heap.
186
   */
187
  public static <T> ItemsSketch<T> getInstance(
188
      final Class<T> clazz,
189
      final Memory srcMem,
190
      final Comparator<? super T> comparator,
191
      final ArrayOfItemsSerDe<T> serDe) {
192
    final long memCapBytes = srcMem.getCapacity();
1✔
193
    if (memCapBytes < 8) {
1✔
194
      throw new SketchesArgumentException("Memory too small: " + memCapBytes);
1✔
195
    }
196

197
    final int preambleLongs = extractPreLongs(srcMem);
1✔
198
    final int serVer = extractSerVer(srcMem);
1✔
199
    final int familyID = extractFamilyID(srcMem);
1✔
200
    final int flags = extractFlags(srcMem);
1✔
201
    final int k = extractK(srcMem);
1✔
202

203
    ItemsUtil.checkItemsSerVer(serVer);
1✔
204

205
    if (serVer == 3 && (flags & COMPACT_FLAG_MASK) == 0) {
1✔
206
      throw new SketchesArgumentException("Non-compact Memory images are not supported.");
1✔
207
    }
208

209
    final boolean empty = checkPreLongsFlagsCap(preambleLongs, flags, memCapBytes);
1✔
210
    checkFamilyID(familyID);
1✔
211
    final ItemsSketch<T> sk = getInstance(clazz, k, comparator); //checks k
1✔
212
    if (empty) { return sk; }
1✔
213

214
    //Not empty, must have valid preamble + min, max
215
    final long n = extractN(srcMem);
1✔
216

217
    //can't check memory capacity here, not enough information
218
    final int extra = 2; //for min, max
1✔
219
    final int numMemItems = computeRetainedItems(k, n) + extra;
1✔
220

221
    //set class members
222
    sk.n_ = n;
1✔
223
    sk.combinedBufferItemCapacity_ = computeCombinedBufferItemCapacity(k, n);
1✔
224
    sk.baseBufferCount_ = computeBaseBufferItems(k, n);
1✔
225
    sk.bitPattern_ = computeBitPattern(k, n);
1✔
226
    sk.combinedBuffer_ = new Object[sk.combinedBufferItemCapacity_];
1✔
227

228
    final int srcMemItemsOffsetBytes = preambleLongs * Long.BYTES;
1✔
229
    final Memory mReg = srcMem.region(srcMemItemsOffsetBytes,
1✔
230
        srcMem.getCapacity() - srcMemItemsOffsetBytes);
1✔
231
    final T[] itemsArray = serDe.deserializeFromMemory(mReg, 0, numMemItems);
1✔
232
    sk.itemsArrayToCombinedBuffer(itemsArray);
1✔
233
    return sk;
1✔
234
  }
235

236
  /**
237
   * Returns a copy of the given sketch
238
   * @param <T> The sketch data type
239
   * @param sketch the given sketch
240
   * @return a copy of the given sketch
241
   */
242
  static <T> ItemsSketch<T> copy(final ItemsSketch<T> sketch) {
243
    final ItemsSketch<T> qsCopy = ItemsSketch.getInstance(sketch.clazz, sketch.k_, sketch.comparator_);
1✔
244
    qsCopy.n_ = sketch.n_;
1✔
245
    qsCopy.minItem_ = sketch.isEmpty() ? null : sketch.getMinItem();
1✔
246
    qsCopy.maxItem_ = sketch.isEmpty() ? null : sketch.getMaxItem();
1✔
247
    qsCopy.combinedBufferItemCapacity_ = sketch.getCombinedBufferAllocatedCount();
1✔
248
    qsCopy.baseBufferCount_ = sketch.getBaseBufferCount();
1✔
249
    qsCopy.bitPattern_ = sketch.getBitPattern();
1✔
250
    final Object[] combBuf = sketch.getCombinedBuffer();
1✔
251
    qsCopy.combinedBuffer_ = Arrays.copyOf(combBuf, combBuf.length);
1✔
252
    return qsCopy;
1✔
253
  }
254

255
  //END of Constructors
256

257
  @Override
258
  public double[] getCDF(final T[] splitPoints, final QuantileSearchCriteria searchCrit) {
259
    if (isEmpty()) { throw new IllegalArgumentException(QuantilesAPI.EMPTY_MSG); }
1✔
260
    refreshSortedView();
1✔
261
    return classicQisSV.getCDF(splitPoints, searchCrit);
1✔
262
  }
263

264
  @Override
265
  public Class<T> getClassOfT() { return clazz; }
1✔
266

267
  @Override
268
  public Comparator<? super T> getComparator() {
269
    return comparator_;
1✔
270
  }
271

272
  @Override
273
  public T getMaxItem() {
274
    if (isEmpty()) { throw new IllegalArgumentException(QuantilesAPI.EMPTY_MSG); }
1✔
275
    return maxItem_;
1✔
276
  }
277

278
  @Override
279
  public T getMinItem() {
280
    if (isEmpty()) { throw new IllegalArgumentException(QuantilesAPI.EMPTY_MSG); }
1✔
281
    return minItem_;
1✔
282
  }
283

284
  @Override
285
  public GenericPartitionBoundaries<T> getPartitionBoundariesFromNumParts(
286
      final int numEquallySizedParts,
287
      final QuantileSearchCriteria searchCrit) {
288
    if (isEmpty()) { throw new IllegalArgumentException(QuantilesAPI.EMPTY_MSG); }
1✔
289
    refreshSortedView();
1✔
290
    return classicQisSV.getPartitionBoundariesFromNumParts(numEquallySizedParts, searchCrit);
1✔
291
  }
292

293
  @Override
294
  public GenericPartitionBoundaries<T> getPartitionBoundariesFromPartSize(
295
      final long nominalPartSizeItems,
296
      final QuantileSearchCriteria searchCrit) {
297
    if (isEmpty()) { throw new IllegalArgumentException(QuantilesAPI.EMPTY_MSG); }
×
298
    refreshSortedView();
×
299
    return classicQisSV.getPartitionBoundariesFromPartSize(nominalPartSizeItems, searchCrit);
×
300
  }
301

302
  @Override
303
  public double[] getPMF(final T[] splitPoints, final QuantileSearchCriteria searchCrit) {
304
  if (isEmpty()) { throw new IllegalArgumentException(QuantilesAPI.EMPTY_MSG); }
1✔
305
    refreshSortedView();
1✔
306
    return classicQisSV.getPMF(splitPoints, searchCrit);
1✔
307
  }
308

309
  @Override
310
  public T getQuantile(final double rank, final QuantileSearchCriteria searchCrit) {
311
    if (isEmpty()) { throw new IllegalArgumentException(QuantilesAPI.EMPTY_MSG); }
1✔
312
    refreshSortedView();
1✔
313
    return classicQisSV.getQuantile(rank, searchCrit);
1✔
314
  }
315

316
  @Override
317
  public T getQuantileLowerBound(final double rank) {
318
    if (isEmpty()) { throw new IllegalArgumentException(QuantilesAPI.EMPTY_MSG); }
1✔
319
    return getQuantile(max(0, rank - getNormalizedRankError(k_, false)));
1✔
320
  }
321

322
  @Override
323
  public T getQuantileUpperBound(final double rank) {
324
    if (isEmpty()) { throw new IllegalArgumentException(QuantilesAPI.EMPTY_MSG); }
1✔
325
    return getQuantile(min(1.0, rank + getNormalizedRankError(k_, false)));
1✔
326
  }
327

328
  @Override
329
  public T[] getQuantiles(final double[] ranks, final QuantileSearchCriteria searchCrit) {
330
    if (isEmpty()) { throw new IllegalArgumentException(QuantilesAPI.EMPTY_MSG); }
1✔
331
    refreshSortedView();
1✔
332
    return classicQisSV.getQuantiles(ranks, searchCrit);
1✔
333
  }
334

335
  @Override
336
  public double getRank(final T quantile, final QuantileSearchCriteria searchCrit) {
337
    if (isEmpty()) { throw new IllegalArgumentException(QuantilesAPI.EMPTY_MSG); }
1✔
338
    refreshSortedView();
1✔
339
    return classicQisSV.getRank(quantile, searchCrit);
1✔
340
  }
341

342
  @Override
343
  public double getRankLowerBound(final double rank) {
344
    return max(0.0, rank - getNormalizedRankError(k_, false));
×
345
  }
346

347
  @Override
348
  public double getRankUpperBound(final double rank) {
349
    return min(1.0, rank + getNormalizedRankError(k_, false));
×
350
  }
351

352
  @Override
353
  public double[] getRanks(final T[] quantiles, final QuantileSearchCriteria searchCrit) {
354
    if (isEmpty()) { throw new IllegalArgumentException(QuantilesAPI.EMPTY_MSG); }
1✔
355
    refreshSortedView();
1✔
356
    final int len = quantiles.length;
1✔
357
    final double[] ranks = new double[len];
1✔
358
    for (int i = 0; i < len; i++) {
1✔
359
      ranks[i] = classicQisSV.getRank(quantiles[i], searchCrit);
1✔
360
    }
361
    return ranks;
1✔
362
  }
363

364
  @Override
365
  public QuantilesGenericSketchIterator<T> iterator() {
366
    return new ItemsSketchIterator<>(this, bitPattern_);
1✔
367
  }
368

369
  @Override
370
  public int getK() {
371
    return k_;
1✔
372
  }
373

374
  @Override
375
  public long getN() {
376
    return n_;
1✔
377
  }
378

379
  @Override
380
  public double getNormalizedRankError(final boolean pmf) {
381
    return getNormalizedRankError(k_, pmf);
1✔
382
  }
383

384
  /**
385
   * Gets the normalized rank error given k and pmf.
386
   * Static method version of the {@link #getNormalizedRankError(boolean)}.
387
   * @param k the configuration parameter
388
   * @param pmf if true, returns the "double-sided" normalized rank error for the getPMF() function.
389
   * Otherwise, it is the "single-sided" normalized rank error for all the other queries.
390
   * @return if pmf is true, the normalized rank error for the getPMF() function.
391
   * Otherwise, it is the "single-sided" normalized rank error for all the other queries.
392
   */
393
  public static double getNormalizedRankError(final int k, final boolean pmf) {
394
    return ClassicUtil.getNormalizedRankError(k, pmf);
1✔
395
  }
396

397
  /**
398
   * Gets the approximate <em>k</em> to use given epsilon, the normalized rank error.
399
   * @param epsilon the normalized rank error between zero and one.
400
   * @param pmf if true, this function returns <em>k</em> assuming the input epsilon
401
   * is the desired "double-sided" epsilon for the getPMF() function. Otherwise, this function
402
   * returns <em>k</em> assuming the input epsilon is the desired "single-sided"
403
   * epsilon for all the other queries.
404
   * @return <i>k</i> given epsilon.
405
   */
406
  public static int getKFromEpsilon(final double epsilon, final boolean pmf) {
407
    return ClassicUtil.getKFromEpsilon(epsilon, pmf);
1✔
408
  }
409

410
  @Override
411
  public boolean hasMemory() {
412
    return false;
1✔
413
  }
414

415
  @Override
416
  public boolean isEmpty() {
417
   return getN() == 0;
1✔
418
  }
419

420
  @Override
421
  public boolean isDirect() {
422
    return false;
1✔
423
  }
424

425
  @Override
426
  public boolean isEstimationMode() {
427
    return getN() >= 2L * k_;
1✔
428
  }
429

430
  @Override
431
  public boolean isReadOnly() {
432
    return false;
1✔
433
  }
434

435
  @Override
436
  public void reset() {
437
    n_ = 0;
1✔
438
    combinedBufferItemCapacity_ = 2 * Math.min(MIN_K, k_); //the min is important
1✔
439
    combinedBuffer_ = new Object[combinedBufferItemCapacity_];
1✔
440
    baseBufferCount_ = 0;
1✔
441
    bitPattern_ = 0;
1✔
442
    minItem_ = null;
1✔
443
    maxItem_ = null;
1✔
444
    classicQisSV = null;
1✔
445
  }
1✔
446

447
  /**
448
   * Serialize this sketch to a byte array form.
449
   * @param serDe an instance of ArrayOfItemsSerDe
450
   * @return byte array of this sketch
451
   */
452
  public byte[] toByteArray(final ArrayOfItemsSerDe<T> serDe) {
453
    return toByteArray(false, serDe);
1✔
454
  }
455

456
  /**
457
   * Serialize this sketch to a byte array form.
458
   * @param ordered if true the base buffer will be ordered (default == false).
459
   * @param serDe an instance of ArrayOfItemsSerDe
460
   * @return this sketch in a byte array form.
461
   */
462
  public byte[] toByteArray(final boolean ordered, final ArrayOfItemsSerDe<T> serDe) {
463
    return ItemsByteArrayImpl.toByteArray(this, ordered, serDe);
1✔
464
  }
465

466
  /**
467
   * Returns human readable summary information about this sketch.
468
   * Used for debugging.
469
   */
470
  @Override
471
  public String toString() {
472
    return toString(false, false);
1✔
473
  }
474

475
  /**
476
   * Returns human readable summary information about this sketch.
477
   * Used for debugging.
478
   * @param withLevels if true includes sketch levels array summary information
479
   * @param withLevelsAndItems if true include detail of levels array and items array together
480
   * @return human readable summary information about this sketch.
481
   */
482
  public String toString(final boolean withLevels, final boolean withLevelsAndItems) {
483
    return ItemsUtil.toString(withLevels, withLevelsAndItems, this);
1✔
484
  }
485

486
  /**
487
   * Returns a human readable string of the preamble of a byte array image of an ItemsSketch.
488
   * Used for debugging.
489
   * @param byteArr the given byte array
490
   * @return a human readable string of the preamble of a byte array image of an ItemsSketch.
491
   */
492
  public static String toString(final byte[] byteArr) {
493
    return PreambleUtil.toString(byteArr, false);
1✔
494
  }
495

496
  /**
497
   * Returns a human readable string of the preamble of a Memory image of an ItemsSketch.
498
   * Used for debugging.
499
   * @param mem the given Memory
500
   * @return a human readable string of the preamble of a Memory image of an ItemsSketch.
501
   */
502
  public static String toString(final Memory mem) {
503
    return PreambleUtil.toString(mem, false);
1✔
504
  }
505

506
  /**
507
   * From an existing sketch, this creates a new sketch that can have a smaller K.
508
   * The original sketch is not modified.
509
   *
510
   * @param newK the new K that must be smaller than current K.
511
   * It is required that this.getK() = newK * 2^(nonnegative integer).
512
   * @return the new sketch.
513
   */
514
  public ItemsSketch<T> downSample(final int newK) {
515
    final ItemsSketch<T> newSketch = ItemsSketch.getInstance(clazz, newK, comparator_);
1✔
516
    ItemsMergeImpl.downSamplingMergeInto(this, newSketch);
1✔
517
    return newSketch;
1✔
518
  }
519

520
  @Override
521
  public int getNumRetained() {
522
    return computeRetainedItems(getK(), getN());
1✔
523
  }
524

525
  /**
526
   * Puts the current sketch into the given Memory if there is sufficient space.
527
   * Otherwise, throws an error.
528
   *
529
   * @param dstMem the given memory.
530
   * @param serDe an instance of ArrayOfItemsSerDe
531
   */
532
  public void putMemory(final WritableMemory dstMem, final ArrayOfItemsSerDe<T> serDe) {
533
    final byte[] byteArr = toByteArray(serDe);
1✔
534
    final long memCap = dstMem.getCapacity();
1✔
535
    if (memCap < byteArr.length) {
1✔
536
      throw new SketchesArgumentException(
1✔
537
          "Destination Memory not large enough: " + memCap + " < " + byteArr.length);
538
    }
539
    dstMem.putByteArray(0, byteArr, 0, byteArr.length);
1✔
540
  }
1✔
541

542
  @Override
543
  public void update(final T item) {
544
    // this method only uses the base buffer part of the combined buffer
545

546
    if (item == null) { return; }
1✔
547
    if (maxItem_ == null || comparator_.compare(item, maxItem_) > 0) { maxItem_ = item; }
1✔
548
    if (minItem_ == null || comparator_.compare(item, minItem_) < 0) { minItem_ = item; }
1✔
549

550
    if (baseBufferCount_ + 1 > combinedBufferItemCapacity_) {
1✔
551
      ItemsSketch.growBaseBuffer(this);
1✔
552
    }
553
    combinedBuffer_[baseBufferCount_++] = item;
1✔
554
    n_++;
1✔
555
    if (baseBufferCount_ == 2 * k_) {
1✔
556
      ItemsUtil.processFullBaseBuffer(this);
1✔
557
    }
558
    classicQisSV = null;
1✔
559
  }
1✔
560

561
  // Restricted
562

563
  /**
564
   * Returns the base buffer count
565
   * @return the base buffer count
566
   */
567
  int getBaseBufferCount() {
568
    return baseBufferCount_;
1✔
569
  }
570

571
  /**
572
   * Returns the allocated count for the combined base buffer
573
   * @return the allocated count for the combined base buffer
574
   */
575
  int getCombinedBufferAllocatedCount() {
576
    return combinedBufferItemCapacity_;
1✔
577
  }
578

579
  /**
580
   * Returns the bit pattern for valid log levels
581
   * @return the bit pattern for valid log levels
582
   */
583
  long getBitPattern() {
584
    return bitPattern_;
1✔
585
  }
586

587
  /**
588
   * Returns the combined buffer reference
589
   * @return the combined buffer reference
590
   */
591
  Object[] getCombinedBuffer() {
592
    return combinedBuffer_;
1✔
593
  }
594

595
  /**
596
   * Loads the Combined Buffer, min and max from the given items array.
597
   * The Combined Buffer is always in non-compact form and must be pre-allocated.
598
   * @param itemsArray the given items array
599
   */
600
  private void itemsArrayToCombinedBuffer(final T[] itemsArray) {
601
    final int extra = 2; // space for min and max items
1✔
602

603
    //Load min, max
604
    minItem_ = itemsArray[0];
1✔
605
    maxItem_ = itemsArray[1];
1✔
606

607
    //Load base buffer
608
    System.arraycopy(itemsArray, extra, combinedBuffer_, 0, baseBufferCount_);
1✔
609

610
    //Load levels
611
    long bits = bitPattern_;
1✔
612
    if (bits > 0) {
1✔
613
      int index = extra + baseBufferCount_;
1✔
614
      for (int level = 0; bits != 0L; level++, bits >>>= 1) {
1✔
615
        if ((bits & 1L) > 0L) {
1✔
616
          System.arraycopy(itemsArray, index, combinedBuffer_, (2 + level) * k_, k_);
1✔
617
          index += k_;
1✔
618
        }
619
      }
620
    }
621
  }
1✔
622

623
  private static <T> void growBaseBuffer(final ItemsSketch<T> sketch) {
624
    final Object[] baseBuffer = sketch.getCombinedBuffer();
1✔
625
    final int oldSize = sketch.getCombinedBufferAllocatedCount();
1✔
626
    final int k = sketch.getK();
1✔
627
    assert oldSize < 2 * k;
1✔
628
    final int newSize = Math.max(Math.min(2 * k, 2 * oldSize), 1);
1✔
629
    sketch.combinedBufferItemCapacity_ = newSize;
1✔
630
    sketch.combinedBuffer_ = Arrays.copyOf(baseBuffer, newSize);
1✔
631
  }
1✔
632

633
  //************SORTED VIEW****************************
634

635
  @Override
636
  public ItemsSketchSortedView<T> getSortedView() {
637
    return refreshSortedView();
1✔
638
  }
639

640
  private final ItemsSketchSortedView<T> refreshSortedView() {
641
    return (classicQisSV == null) ? (classicQisSV = getSV(this)) : classicQisSV;
1✔
642
  }
643

644
  @SuppressWarnings({"unchecked"})
645
  private static <T> ItemsSketchSortedView<T> getSV(final ItemsSketch<T> sk) {
646
    final long totalN = sk.getN();
1✔
647
    if (sk.isEmpty() || (totalN == 0)) { throw new SketchesArgumentException(EMPTY_MSG); }
1✔
648
    final int k = sk.getK();
1✔
649
    final int numQuantiles = sk.getNumRetained();
1✔
650
    final T[] svQuantiles = (T[]) Array.newInstance(sk.clazz, numQuantiles);
1✔
651
    final long[] svCumWeights = new long[numQuantiles];
1✔
652
    final Comparator<? super T> comparator = sk.comparator_;
1✔
653

654
    final T[] combinedBuffer = (T[]) sk.getCombinedBuffer();
1✔
655
    final int baseBufferCount = sk.getBaseBufferCount();
1✔
656

657
    // Populate from ItemsSketch:
658
    // copy over the "levels" and then the base buffer, all with appropriate weights
659
    populateFromItemsSketch(k, totalN, sk.getBitPattern(), combinedBuffer, baseBufferCount,
1✔
660
        numQuantiles, svQuantiles, svCumWeights, sk.getComparator());
1✔
661

662
    // Sort the first "numSamples" slots of the two arrays in tandem,
663
    // taking advantage of the already sorted blocks of length k
664
    ItemsMergeImpl.blockyTandemMergeSort(svQuantiles, svCumWeights, numQuantiles, k, comparator);
1✔
665

666
    if (convertToCumulative(svCumWeights) != totalN) {
1✔
667
      throw new SketchesStateException("Sorted View is misconfigured. TotalN does not match cumWeights.");
×
668
    }
669

670
    return new ItemsSketchSortedView<>(svQuantiles, svCumWeights, sk);
1✔
671

672
  }
673

674
  private final static <T> void populateFromItemsSketch(
675
      final int k, final long totalN, final long bitPattern, final T[] combinedBuffer,
676
      final int baseBufferCount, final int numQuantiles, final T[] svQuantiles, final long[] svCumWeights,
677
      final Comparator<? super T> comparator) {
678

679
    long weight = 1;
1✔
680
    int index = 0;
1✔
681
    long bits = bitPattern;
1✔
682
    assert bits == (totalN / (2L * k)); // internal consistency check
1✔
683
    for (int lvl = 0; bits != 0L; lvl++, bits >>>= 1) {
1✔
684
      weight <<= 1; // X2
1✔
685
      if ((bits & 1L) > 0L) {
1✔
686
        final int offset = (2 + lvl) * k;
1✔
687
        for (int i = 0; i < k; i++) {
1✔
688
          svQuantiles[index] = combinedBuffer[i + offset];
1✔
689
          svCumWeights[index] = weight;
1✔
690
          index++;
1✔
691
        }
692
      }
693
    }
694

695
    weight = 1; //NOT a mistake! We just copied the highest level; now we need to copy the base buffer
1✔
696
    final int startOfBaseBufferBlock = index;
1✔
697

698
    // Copy BaseBuffer over, along with weight = 1
699
    for (int i = 0; i < baseBufferCount; i++) {
1✔
700
      svQuantiles[index] = combinedBuffer[i];
1✔
701
      svCumWeights[index] = weight;
1✔
702
      index++;
1✔
703
    }
704
    assert index == numQuantiles;
1✔
705

706
    // Must sort the items that came from the base buffer.
707
    // Don't need to sort the corresponding weights because they are all the same.
708
    Arrays.sort(svQuantiles, startOfBaseBufferBlock, numQuantiles, comparator);
1✔
709
  }
1✔
710

711
  /**
712
   * Convert the individual weights into cumulative weights.
713
   * An array of {1,1,1,1} becomes {1,2,3,4}
714
   * @param array of actual weights from the sketch, none of the weights may be zero
715
   * @return total weight
716
   */
717
  private static long convertToCumulative(final long[] array) {
718
    long subtotal = 0;
1✔
719
    for (int i = 0; i < array.length; i++) {
1✔
720
      final long newSubtotal = subtotal + array[i];
1✔
721
      subtotal = array[i] = newSubtotal;
1✔
722
    }
723
    return subtotal;
1✔
724
  }
725

726
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc