• 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

96.5
/src/main/java/org/apache/datasketches/req/ReqSketch.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.req;
21

22
import static org.apache.datasketches.common.Util.LS;
23
import static org.apache.datasketches.quantilescommon.QuantileSearchCriteria.INCLUSIVE;
24

25
import java.util.ArrayList;
26
import java.util.List;
27
import java.util.Random;
28

29
import org.apache.datasketches.common.SketchesArgumentException;
30
import org.apache.datasketches.memory.Memory;
31
import org.apache.datasketches.quantilescommon.FloatsSketchSortedView;
32
import org.apache.datasketches.quantilescommon.QuantileSearchCriteria;
33
import org.apache.datasketches.quantilescommon.QuantilesAPI;
34
import org.apache.datasketches.quantilescommon.QuantilesFloatsSketchIterator;
35

36
/**
37
 * This Relative Error Quantiles Sketch is the Java implementation based on the paper
38
 * "Relative Error Streaming Quantiles" by Graham Cormode, Zohar Karnin, Edo Liberty,
39
 * Justin Thaler, Pavel Veselý, and loosely derived from a Python prototype written by Pavel Veselý.
40
 *
41
 * <p>Reference: https://arxiv.org/abs/2004.01668</p>
42
 *
43
 * <p>This implementation differs from the algorithm described in the paper in the following:</p>
44
 *
45
 * <ul>
46
 * <li>The algorithm requires no upper bound on the stream length.
47
 * Instead, each relative-compactor counts the number of compaction operations performed
48
 * so far (via variable state). Initially, the relative-compactor starts with INIT_NUMBER_OF_SECTIONS.
49
 * Each time the number of compactions (variable state) exceeds 2^{numSections - 1}, we double
50
 * numSections. Note that after merging the sketch with another one variable state may not correspond
51
 * to the number of compactions performed at a particular level, however, since the state variable
52
 * never exceeds the number of compactions, the guarantees of the sketch remain valid.</li>
53
 *
54
 * <li>The size of each section (variable k and sectionSize in the code and parameter k in
55
 * the paper) is initialized with a number set by the user via variable k.
56
 * When the number of sections doubles, we decrease sectionSize by a factor of sqrt(2).
57
 * This is applied at each level separately. Thus, when we double the number of sections, the
58
 * nominal compactor size increases by a factor of approx. sqrt(2) (+/- rounding).</li>
59
 *
60
 * <li>The merge operation here does not perform "special compactions", which are used in the paper
61
 * to allow for a tight mathematical analysis of the sketch.</li>
62
 * </ul>
63
 *
64
 * <p>This implementation provides a number of capabilities not discussed in the paper or provided
65
 * in the Python prototype.</p>
66
 *
67
 * <ul><li>The Python prototype only implemented high accuracy for low ranks. This implementation
68
 * provides the user with the ability to choose either high rank accuracy or low rank accuracy at
69
 * the time of sketch construction.</li>
70
 * <li>The Python prototype only implemented a comparison criterion of "INCLUSIVE". This implementation
71
 * allows the user to switch back and forth between the "INCLUSIVE" criterion and the "EXCLUSIVE" criterion.</li>
72
 * <li>This implementation provides extensive debug visibility into the operation of the sketch with
73
 * two levels of detail output. This is not only useful for debugging, but is a powerful tool to
74
 * help users understand how the sketch works.</li>
75
 * </ul>
76
 *
77
 * @see QuantilesAPI
78
 *
79
 * @author Edo Liberty
80
 * @author Pavel Vesely
81
 * @author Lee Rhodes
82
 */
83
public final class ReqSketch extends BaseReqSketch {
1✔
84

85
  static class CompactorReturn {
1✔
86
    int deltaRetItems;
87
    int deltaNomSize;
88
  }
89

90
  //static finals
91
  static final byte MIN_K = 4;
92
  static final byte NOM_CAP_MULT = 2;
93

94
  //finals
95
  private final int k; //default is 12 (1% @ 95% Confidence)
96
  private final boolean hra; //default is true
97
  //state variables
98
  private long totalN = 0;
1✔
99
  private float minItem = Float.NaN;
1✔
100
  private float maxItem = Float.NaN;
1✔
101
  //computed from compactors
102
  private int retItems = 0; //number of retained items in the sketch
1✔
103
  private int maxNomSize = 0; //sum of nominal capacities of all compactors
1✔
104
  //Objects
105
  private FloatsSketchSortedView reqSV = null;
1✔
106
  private List<ReqCompactor> compactors = new ArrayList<>();
1✔
107
  private ReqDebug reqDebug = null; //user config, default: null, can be set after construction.
1✔
108

109
  private final CompactorReturn cReturn = new CompactorReturn(); //used in compress()
1✔
110

111
  private final Random rand;
112

113
  /**
114
   * Construct from elements. After sketch is constructed, retItems and maxNomSize must be computed.
115
   * Used by ReqSerDe.
116
   */
117
  ReqSketch(final int k, final boolean hra, final long totalN, final float minItem,
118
      final float maxItem, final List<ReqCompactor> compactors) {
1✔
119
    checkK(k);
1✔
120
    this.k = k;
1✔
121
    this.hra = hra;
1✔
122
    this.totalN = totalN;
1✔
123
    this.minItem = minItem;
1✔
124
    this.maxItem = maxItem;
1✔
125
    this.compactors = compactors;
1✔
126
    this.rand = new Random();
1✔
127
  }
1✔
128

129
  /**
130
   * Normal Constructor used by ReqSketchBuilder.
131
   * @param k Controls the size and error of the sketch. It must be even and in the range
132
   * [4, 1024].
133
   * The default number 12 roughly corresponds to 1% relative error guarantee at 95% confidence.
134
   * @param highRankAccuracy if true, the default, the high ranks are prioritized for better
135
   * accuracy. Otherwise the low ranks are prioritized for better accuracy.
136
   * @param reqDebug the debug handler. It may be null.
137
   */
138
  ReqSketch(final int k, final boolean highRankAccuracy, final ReqDebug reqDebug) {
1✔
139
    checkK(k);
1✔
140
    this.k = k;
1✔
141
    this.hra = highRankAccuracy;
1✔
142
    this.reqDebug = reqDebug;
1✔
143
    this.rand = (reqDebug == null) ? new Random() : new Random(1);
1✔
144
    grow();
1✔
145
  }
1✔
146

147
  /**
148
   * Copy Constructor.  Only used in test.
149
   * @param other the other sketch to be deep copied into this one.
150
   */
151
  ReqSketch(final ReqSketch other) {
1✔
152
    this.k = other.k;
1✔
153
    this.hra = other.hra;
1✔
154
    this.totalN = other.totalN;
1✔
155
    this.retItems = other.retItems;
1✔
156
    this.maxNomSize = other.maxNomSize;
1✔
157
    this.minItem = other.minItem;
1✔
158
    this.maxItem = other.maxItem;
1✔
159
    this.reqDebug = other.reqDebug;
1✔
160
    this.reqSV = null;
1✔
161
    this.rand = (reqDebug == null) ? new Random() : new Random(1);
1✔
162

163
    for (int i = 0; i < other.getNumLevels(); i++) {
1✔
164
      compactors.add(new ReqCompactor(other.compactors.get(i)));
1✔
165
    }
166
  }
1✔
167

168
  /**
169
   * Returns a new ReqSketchBuilder
170
   * @return a new ReqSketchBuilder
171
   */
172
  public static final ReqSketchBuilder builder() {
173
    return new ReqSketchBuilder();
1✔
174
  }
175

176
  /**
177
   * Returns an ReqSketch on the heap from a Memory image of the sketch.
178
   * @param mem The Memory object holding a valid image of an ReqSketch
179
   * @return an ReqSketch on the heap from a Memory image of the sketch.
180
   */
181
  public static ReqSketch heapify(final Memory mem) {
182
    return ReqSerDe.heapify(mem);
1✔
183
  }
184

185
  @Override
186
  public int getK() {
187
    return k;
1✔
188
  }
189

190
  /**
191
   * This checks the given float array to make sure that it contains only finite numbers
192
   * and is monotonically increasing.
193
   * @param splits the given array
194
   */
195
  static void validateSplits(final float[] splits) {
196
    final int len = splits.length;
1✔
197
    for (int i = 0; i < len; i++) {
1✔
198
      final float v = splits[i];
1✔
199
      if (!Float.isFinite(v)) {
1✔
200
        throw new SketchesArgumentException("Numbers must be finite");
×
201
      }
202
      if (i < len - 1 && v >= splits[i + 1]) {
1✔
203
        throw new SketchesArgumentException(
1✔
204
          "Numbers must be unique and monotonically increasing");
205
      }
206
    }
207
  }
1✔
208

209
  @Override
210
  public double[] getCDF(final float[] splitPoints, final QuantileSearchCriteria searchCrit) {
211
    if (isEmpty()) { throw new IllegalArgumentException(QuantilesAPI.EMPTY_MSG); }
1✔
212
    refreshSortedView();
1✔
213
    return reqSV.getCDF(splitPoints, searchCrit);
1✔
214
  }
215

216
  @Override
217
  public boolean getHighRankAccuracyMode() {
218
    return hra;
1✔
219
  }
220

221
  @Override
222
  public float getMaxItem() {
223
    if (isEmpty()) { throw new IllegalArgumentException(QuantilesAPI.EMPTY_MSG); }
1✔
224
    return maxItem;
1✔
225
  }
226

227
  @Override
228
  public float getMinItem() {
229
    if (isEmpty()) { throw new IllegalArgumentException(QuantilesAPI.EMPTY_MSG); }
1✔
230
    return minItem;
1✔
231
  }
232

233
  @Override
234
  public long getN() {
235
    return totalN;
1✔
236
  }
237

238
  @Override
239
  /**
240
   * This is an unsupported operation for this sketch
241
   */
242
  public double getNormalizedRankError(final boolean pmf) {
243
    throw new UnsupportedOperationException(UNSUPPORTED_MSG);
×
244

245
  }
246

247
  @Override
248
  public double[] getPMF(final float[] splitPoints, final QuantileSearchCriteria searchCrit) {
249
    if (isEmpty()) { throw new IllegalArgumentException(QuantilesAPI.EMPTY_MSG); }
1✔
250
    refreshSortedView();
1✔
251
    return reqSV.getPMF(splitPoints, searchCrit);
1✔
252
  }
253

254
  @Override
255
  public float getQuantile(final double normRank, final QuantileSearchCriteria searchCrit) {
256
    if (isEmpty()) { throw new IllegalArgumentException(QuantilesAPI.EMPTY_MSG); }
1✔
257
    if (normRank < 0 || normRank > 1.0) {
1✔
258
      throw new SketchesArgumentException(
1✔
259
        "Normalized rank must be in the range [0.0, 1.0]: " + normRank);
260
    }
261
    refreshSortedView();
1✔
262
    return reqSV.getQuantile(normRank, searchCrit);
1✔
263
  }
264

265
  @Override
266
  public float[] getQuantiles(final double[] normRanks, final QuantileSearchCriteria searchCrit) {
267
    if (isEmpty()) { throw new IllegalArgumentException(QuantilesAPI.EMPTY_MSG); }
1✔
268
    refreshSortedView();
1✔
269
    final int len = normRanks.length;
1✔
270
    final float[] qArr = new float[len];
1✔
271
    for (int i = 0; i < len; i++) {
1✔
272
      qArr[i] = reqSV.getQuantile(normRanks[i], searchCrit);
1✔
273
    }
274
    return qArr;
1✔
275
  }
276

277
  /**
278
   * {@inheritDoc}
279
   * The approximate probability that the true quantile is within the confidence interval
280
   * specified by the upper and lower quantile bounds for this sketch is 0.95.
281
   */
282
  @Override
283
  public float getQuantileLowerBound(final double rank) {
284
    return getQuantile(getRankLowerBound(rank, 2), INCLUSIVE);
×
285
  }
286

287
  @Override
288
  public float getQuantileLowerBound(final double rank, final int numStdDev) {
289
    return getQuantile(getRankLowerBound(rank, numStdDev), INCLUSIVE);
×
290
  }
291

292
  /**
293
   * {@inheritDoc}
294
   * The approximate probability that the true quantile is within the confidence interval
295
   * specified by the upper and lower quantile bounds for this sketch is 0.95.
296
   */
297
  @Override
298
  public float getQuantileUpperBound(final double rank) {
299
    return getQuantile(getRankUpperBound(rank, 2), INCLUSIVE);
×
300
  }
301

302
  @Override
303
  public float getQuantileUpperBound(final double rank, final int numStdDev) {
304
    return getQuantile(getRankUpperBound(rank, numStdDev), INCLUSIVE);
×
305
  }
306

307
  @Override
308
  public double getRank(final float quantile, final QuantileSearchCriteria searchCrit) {
309
    if (isEmpty()) { throw new IllegalArgumentException(QuantilesAPI.EMPTY_MSG); }
1✔
310
    refreshSortedView();
1✔
311
    return reqSV.getRank(quantile, searchCrit);
1✔
312
  }
313

314
  /**
315
   * {@inheritDoc}
316
   * The approximate probability that the true rank is within the confidence interval
317
   * specified by the upper and lower rank bounds for this sketch is 0.95.
318
   */
319
  @Override
320
  public double getRankLowerBound(final double rank) {
321
    return getRankLB(k, getNumLevels(), rank, 2, hra, getN());
×
322
  }
323

324
  @Override
325
  public double getRankLowerBound(final double rank, final int numStdDev) {
326
    return getRankLB(k, getNumLevels(), rank, numStdDev, hra, getN());
1✔
327
  }
328

329
  @Override
330
  public double[] getRanks(final float[] quantiles, final QuantileSearchCriteria searchCrit) {
331
    if (isEmpty()) { throw new IllegalArgumentException(QuantilesAPI.EMPTY_MSG); }
1✔
332
    refreshSortedView();
1✔
333
    final int numQuantiles = quantiles.length;
1✔
334
    final double[] retArr = new double[numQuantiles];
1✔
335
    for (int i = 0; i < numQuantiles; i++) {
1✔
336
      retArr[i] = reqSV.getRank(quantiles[i], searchCrit); //already normalized
1✔
337
    }
338
    return retArr;
1✔
339
  }
340

341
  /**
342
   * {@inheritDoc}
343
   * The approximate probability that the true rank is within the confidence interval
344
   * specified by the upper and lower rank bounds for this sketch is 0.95.
345
   */
346
  @Override
347
  public double getRankUpperBound(final double rank) {
348
    return getRankUB(k, getNumLevels(), rank, 2, hra, getN());
×
349
  }
350

351
  @Override
352
  public double getRankUpperBound(final double rank, final int numStdDev) {
353
    return getRankUB(k, getNumLevels(), rank, numStdDev, hra, getN());
1✔
354
  }
355

356
  @Override
357
  public int getNumRetained() { return retItems; }
1✔
358

359
  @Override
360
  public int getSerializedSizeBytes() {
361
    final ReqSerDe.SerDeFormat serDeFormat = ReqSerDe.getSerFormat(this);
1✔
362
    return ReqSerDe.getSerBytes(this, serDeFormat);
1✔
363
  }
364

365
  @Override
366
  public boolean isEmpty() {
367
    return totalN == 0;
1✔
368
  }
369

370
  @Override
371
  public boolean isEstimationMode() {
372
    return getNumLevels() > 1;
1✔
373
  }
374

375
  @Override
376
  public QuantilesFloatsSketchIterator iterator() {
377
    return new ReqSketchIterator(this);
1✔
378
  }
379

380
  @Override
381
  public ReqSketch merge(final ReqSketch other) {
382
    if (other == null || other.isEmpty()) { return this; }
1✔
383
    if (other.hra != hra) {
1✔
384
      throw new SketchesArgumentException(
×
385
          "Both sketches must have the same HighRankAccuracy setting.");
386
    }
387
    totalN += other.totalN;
1✔
388
    //update min, max items, n
389
    if (Float.isNaN(minItem) || other.minItem < minItem) { minItem = other.minItem; }
1✔
390
    if (Float.isNaN(maxItem) || other.maxItem > maxItem) { maxItem = other.maxItem; }
1✔
391
    //Grow until self has at least as many compactors as other
392
    while (getNumLevels() < other.getNumLevels()) { grow(); }
1✔
393
    //Merge the items in all height compactors
394
    for (int i = 0; i < other.getNumLevels(); i++) {
1✔
395
      compactors.get(i).merge(other.compactors.get(i));
1✔
396
    }
397
    maxNomSize = computeMaxNomSize();
1✔
398
    retItems = computeTotalRetainedItems();
1✔
399
    if (retItems >= maxNomSize) {
1✔
400
      compress();
1✔
401
    }
402
    assert retItems < maxNomSize;
1✔
403
    reqSV = null;
1✔
404
    return this;
1✔
405
  }
406

407
  @Override
408
  public void reset() {
409
    totalN = 0;
1✔
410
    retItems = 0;
1✔
411
    maxNomSize = 0;
1✔
412
    minItem = Float.NaN;
1✔
413
    maxItem = Float.NaN;
1✔
414
    reqSV = null;
1✔
415
    compactors = new ArrayList<>();
1✔
416
    grow();
1✔
417
  }
1✔
418

419
  @Override
420
  public byte[] toByteArray() {
421
    return ReqSerDe.toByteArray(this);
1✔
422
  }
423

424
  @Override
425
  public String toString() {
426
    final StringBuilder sb = new StringBuilder();
1✔
427
    sb.append("**********Relative Error Quantiles Sketch Summary**********").append(LS);
1✔
428
    sb.append("  K               : " + k).append(LS);
1✔
429
    sb.append("  N               : " + totalN).append(LS);
1✔
430
    sb.append("  Retained Items  : " + retItems).append(LS);
1✔
431
    sb.append("  Min Item        : " + minItem).append(LS);
1✔
432
    sb.append("  Max Item        : " + maxItem).append(LS);
1✔
433
    sb.append("  Estimation Mode : " + isEstimationMode()).append(LS);
1✔
434
    sb.append("  High Rank Acc   : " + hra).append(LS);
1✔
435
    sb.append("  Levels          : " + compactors.size()).append(LS);
1✔
436
    sb.append("************************End Summary************************").append(LS);
1✔
437
    return sb.toString();
1✔
438
  }
439

440
  @Override
441
  public void update(final float item) {
442
    if (Float.isNaN(item)) { return; }
1✔
443
    if (isEmpty()) {
1✔
444
      minItem = item;
1✔
445
      maxItem = item;
1✔
446
    } else {
447
      if (item < minItem) { minItem = item; }
1✔
448
      if (item > maxItem) { maxItem = item; }
1✔
449
    }
450
    final FloatBuffer buf = compactors.get(0).getBuffer();
1✔
451
    buf.append(item);
1✔
452
    retItems++;
1✔
453
    totalN++;
1✔
454
    if (retItems >= maxNomSize) {
1✔
455
      buf.sort();
1✔
456
      compress();
1✔
457
    }
458
    reqSV = null;
1✔
459
  }
1✔
460

461
  @Override
462
  public String viewCompactorDetail(final String fmt, final boolean allData) {
463
    final StringBuilder sb = new StringBuilder();
1✔
464
    sb.append("*********Relative Error Quantiles Compactor Detail*********").append(LS);
1✔
465
    sb.append("Compactor Detail: Ret Items: ").append(getNumRetained())
1✔
466
      .append("  N: ").append(getN());
1✔
467
    sb.append(LS);
1✔
468
    for (int i = 0; i < getNumLevels(); i++) {
1✔
469
      final ReqCompactor c = compactors.get(i);
1✔
470
      sb.append(c.toListPrefix()).append(LS);
1✔
471
      if (allData) { sb.append(c.getBuffer().toHorizList(fmt, 20)).append(LS); }
1✔
472
    }
473
    sb.append("************************End Detail*************************").append(LS);
1✔
474
    return sb.toString();
1✔
475
  }
476

477
  /**
478
   * Computes a new bound for determining when to compress the sketch.
479
   */
480
  int computeMaxNomSize() {
481
    int cap = 0;
1✔
482
    for (final ReqCompactor c : compactors) { cap += c.getNomCapacity(); }
1✔
483
    return cap;
1✔
484
  }
485

486
  /**
487
   * Computes the retained Items for the sketch.
488
   */
489
  int computeTotalRetainedItems() {
490
    int count = 0;
1✔
491
    for (final ReqCompactor c : compactors) {
1✔
492
      count += c.getBuffer().getCount();
1✔
493
    }
1✔
494
    return count;
1✔
495
  }
496

497
  List<ReqCompactor> getCompactors() {
498
    return compactors;
1✔
499
  }
500

501
  int getMaxNomSize() {
502
    return maxNomSize;
1✔
503
  }
504

505
  /**
506
   * Gets the number of levels of compactors in the sketch.
507
   * @return the number of levels of compactors in the sketch.
508
   */
509
  int getNumLevels() {
510
    return compactors.size();
1✔
511
  }
512

513
  void setMaxNomSize(final int maxNomSize) {
514
    this.maxNomSize = maxNomSize;
1✔
515
  }
1✔
516

517
  void setRetainedItems(final int retItems) {
518
    this.retItems = retItems;
1✔
519
  }
1✔
520

521
  private static void checkK(final int k) {
522
    if ((k & 1) > 0 || k < 4 || k > 1024) {
1✔
523
      throw new SketchesArgumentException(
1✔
524
          "<i>K</i> must be even and in the range [4, 1024]: " + k );
525
    }
526
  }
1✔
527

528
  private void compress() {
529
    if (reqDebug != null) { reqDebug.emitStartCompress(); }
1✔
530
    for (int h = 0; h < compactors.size(); h++) {
1✔
531
      final ReqCompactor c = compactors.get(h);
1✔
532
      final int compRetItems = c.getBuffer().getCount();
1✔
533
      final int compNomCap = c.getNomCapacity();
1✔
534

535
      if (compRetItems >= compNomCap) {
1✔
536
        if (h + 1 >= getNumLevels()) { //at the top?
1✔
537
          if (reqDebug != null) { reqDebug.emitMustAddCompactor(); }
1✔
538
          grow(); //add a level, increases maxNomSize
1✔
539
        }
540
        final FloatBuffer promoted = c.compact(cReturn, this.rand);
1✔
541
        compactors.get(h + 1).getBuffer().mergeSortIn(promoted);
1✔
542
        retItems += cReturn.deltaRetItems;
1✔
543
        maxNomSize += cReturn.deltaNomSize;
1✔
544
        //we specifically decided not to do lazy compression.
545
      }
546
    }
547
    reqSV = null;
1✔
548
    if (reqDebug != null) { reqDebug.emitCompressDone(); }
1✔
549
  }
1✔
550

551
  private void grow() {
552
    final byte lgWeight = (byte)getNumLevels();
1✔
553
    if (lgWeight == 0 && reqDebug != null) { reqDebug.emitStart(this); }
1✔
554
    compactors.add(new ReqCompactor(lgWeight, hra, k, reqDebug));
1✔
555
    maxNomSize = computeMaxNomSize();
1✔
556
    if (reqDebug != null) { reqDebug.emitNewCompactor(lgWeight); }
1✔
557
  }
1✔
558

559
  // SORTED VIEW
560

561
  @Override
562
  public FloatsSketchSortedView getSortedView() {
563
    refreshSortedView();
1✔
564
    return reqSV;
1✔
565
  }
566

567
  private final FloatsSketchSortedView refreshSortedView() {
568
    if (reqSV == null) {
1✔
569
      final CreateSortedView csv = new CreateSortedView();
1✔
570
      reqSV = csv.getSV();
1✔
571
    }
572
    return reqSV;
1✔
573
  }
574

575
  private final class CreateSortedView {
1✔
576
    float[] quantiles;
577
    long[] cumWeights;
578

579
    FloatsSketchSortedView getSV() {
580
      if (isEmpty()) { throw new SketchesArgumentException(EMPTY_MSG); }
1✔
581
      //build the SV arrays
582
      final List<ReqCompactor> compactors = getCompactors();
1✔
583
      final int numComp = compactors.size();
1✔
584
      final int totalQuantiles = getNumRetained();
1✔
585
      quantiles = new float[totalQuantiles]; //could have zero entries
1✔
586
      cumWeights = new long[totalQuantiles];
1✔
587
      int count = 0;
1✔
588
      for (int i = 0; i < numComp; i++) {
1✔
589
        final ReqCompactor c = compactors.get(i);
1✔
590
        final FloatBuffer bufIn = c.getBuffer();
1✔
591
        final long bufWeight = 1 << c.getLgWeight();
1✔
592
        final int bufInLen = bufIn.getCount();
1✔
593
        mergeSortIn(bufIn, bufWeight, count, getHighRankAccuracyMode());
1✔
594
        count += bufInLen;
1✔
595
      }
596
      createCumulativeNativeRanks();
1✔
597
      return new FloatsSketchSortedView(quantiles, cumWeights, ReqSketch.this);
1✔
598
    }
599

600
    /**
601
     * Specially modified version of FloatBuffer.mergeSortIn(). Here spaceAtBottom is always false and
602
     * the ultimate array size has already been set.  However, this must simultaneously deal with
603
     * sorting the base FloatBuffer as well.
604
     *
605
     * @param bufIn given FloatBuffer. If not sorted it will be sorted here.
606
     * @param bufWeight associated weight of input FloatBuffer
607
     * @param count tracks number of items inserted into the class arrays
608
     */
609
    private void mergeSortIn(final FloatBuffer bufIn, final long bufWeight, final int count, final boolean hra) {
610
      if (!bufIn.isSorted()) { bufIn.sort(); }
1✔
611
      final float[] arrIn = bufIn.getArray(); //may be larger than its item count.
1✔
612
      final int bufInLen = bufIn.getCount();
1✔
613
      final int totLen = count + bufInLen;
1✔
614
      int i = count - 1;
1✔
615
      int j = bufInLen - 1;
1✔
616
      int h = hra ? bufIn.getCapacity() - 1 : bufInLen - 1;
1✔
617
      for (int k = totLen; k-- > 0; ) {
1✔
618
        if (i >= 0 && j >= 0) { //both valid
1✔
619
          if (quantiles[i] >= arrIn[h]) {
1✔
620
            quantiles[k] = quantiles[i];
1✔
621
            cumWeights[k] = cumWeights[i--]; //not yet natRanks, just individual wts
1✔
622
          } else {
623
            quantiles[k] = arrIn[h--]; j--;
1✔
624
            cumWeights[k] = bufWeight;
1✔
625
          }
626
        } else if (i >= 0) { //i is valid
1✔
627
          quantiles[k] = quantiles[i];
1✔
628
          cumWeights[k] = cumWeights[i--];
1✔
629
        } else if (j >= 0) { //j is valid
1✔
630
          quantiles[k] = arrIn[h--]; j--;
1✔
631
          cumWeights[k] = bufWeight;
1✔
632
        } else {
633
          break;
634
        }
635
      }
636
    }
1✔
637

638
    private void createCumulativeNativeRanks() {
639
      final int len = quantiles.length;
1✔
640
      for (int i = 1; i < len; i++) {
1✔
641
        cumWeights[i] +=  cumWeights[i - 1];
1✔
642
      }
643
      if (totalN > 0) {
1✔
644
        assert cumWeights[len - 1] == totalN;
1✔
645
      }
646
    }
1✔
647

648
  } //End CreateSortedView
649

650
}
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