• 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.15
/src/main/java/org/apache/datasketches/kll/KllDoublesSketch.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.kll;
21

22
import static java.lang.Math.max;
23
import static java.lang.Math.min;
24
import static org.apache.datasketches.common.ByteArrayUtil.putDoubleLE;
25
import static org.apache.datasketches.kll.KllSketch.SketchStructure.UPDATABLE;
26
import static org.apache.datasketches.kll.KllSketch.SketchType.DOUBLES_SKETCH;
27

28
import java.util.Arrays;
29
import java.util.Objects;
30

31
import org.apache.datasketches.common.ArrayOfItemsSerDe;
32
import org.apache.datasketches.common.SketchesArgumentException;
33
import org.apache.datasketches.common.SuppressFBWarnings;
34
import org.apache.datasketches.kll.KllDirectDoublesSketch.KllDirectCompactDoublesSketch;
35
import org.apache.datasketches.memory.DefaultMemoryRequestServer;
36
import org.apache.datasketches.memory.Memory;
37
import org.apache.datasketches.memory.MemoryRequestServer;
38
import org.apache.datasketches.memory.WritableMemory;
39
import org.apache.datasketches.quantilescommon.DoublesSketchSortedView;
40
import org.apache.datasketches.quantilescommon.QuantileSearchCriteria;
41
import org.apache.datasketches.quantilescommon.QuantilesDoublesAPI;
42
import org.apache.datasketches.quantilescommon.QuantilesDoublesSketchIterator;
43

44
/**
45
 * This variation of the KllSketch implements primitive doubles.
46
 *
47
 * @see org.apache.datasketches.kll.KllSketch
48
 */
49
public abstract class KllDoublesSketch extends KllSketch implements QuantilesDoublesAPI {
1✔
50
  private DoublesSketchSortedView doublesSV = null;
1✔
51
  final static int ITEM_BYTES = Double.BYTES;
52

53
  KllDoublesSketch(
54
      final SketchStructure sketchStructure) {
55
    super(SketchType.DOUBLES_SKETCH, sketchStructure);
1✔
56
  }
1✔
57

58
  //Factories for new heap instances.
59

60
  /**
61
   * Create a new heap instance of this sketch with the default <em>k = 200</em>.
62
   * The default <em>k</em> = 200 results in a normalized rank error of about
63
   * 1.65%. Larger K will have smaller error but the sketch will be larger (and slower).
64
   * @return new KllDoublesSketch on the Java heap.
65
   */
66
  public static KllDoublesSketch newHeapInstance() {
67
    return newHeapInstance(DEFAULT_K);
1✔
68
  }
69

70
  /**
71
   * Create a new heap instance of this sketch with a given parameter <em>k</em>.
72
   * <em>k</em> can be between 8, inclusive, and 65535, inclusive.
73
   * The default <em>k</em> = 200 results in a normalized rank error of about
74
   * 1.65%. Larger K will have smaller error but the sketch will be larger (and slower).
75
   * @param k parameter that controls size of the sketch and accuracy of estimates.
76
   * @return new KllDoublesSketch on the Java heap.
77
   */
78
  public static KllDoublesSketch newHeapInstance(final int k) {
79
    return new KllHeapDoublesSketch(k, DEFAULT_M);
1✔
80
  }
81

82
  //Factories for new direct instances.
83

84
  /**
85
   * Create a new direct updatable instance of this sketch with the default <em>k</em>.
86
   * The default <em>k</em> = 200 results in a normalized rank error of about
87
   * 1.65%. Larger <em>k</em> will have smaller error but the sketch will be larger (and slower).
88
   * @param dstMem the given destination WritableMemory object for use by the sketch
89
   * @param memReqSvr the given MemoryRequestServer to request a larger WritableMemory
90
   * @return a new direct instance of this sketch
91
   */
92
  public static KllDoublesSketch newDirectInstance(
93
      final WritableMemory dstMem,
94
      final MemoryRequestServer memReqSvr) {
95
    return newDirectInstance(DEFAULT_K, dstMem, memReqSvr);
1✔
96
  }
97

98
  /**
99
   * Create a new direct updatable instance of this sketch with a given <em>k</em>.
100
   * @param k parameter that controls size of the sketch and accuracy of estimates.
101
   * @param dstMem the given destination WritableMemory object for use by the sketch
102
   * @param memReqSvr the given MemoryRequestServer to request a larger WritableMemory
103
   * @return a new direct instance of this sketch
104
   */
105
  public static KllDoublesSketch newDirectInstance(
106
      final int k,
107
      final WritableMemory dstMem,
108
      final MemoryRequestServer memReqSvr) {
109
    Objects.requireNonNull(dstMem, "Parameter 'dstMem' must not be null");
1✔
110
    Objects.requireNonNull(memReqSvr, "Parameter 'memReqSvr' must not be null");
1✔
111
    return KllDirectDoublesSketch.newDirectUpdatableInstance(k, DEFAULT_M, dstMem, memReqSvr);
1✔
112
  }
113

114
  //Factory to create an heap instance from a Memory image
115

116
  /**
117
   * Factory heapify takes a compact sketch image in Memory and instantiates an on-heap sketch.
118
   * The resulting sketch will not retain any link to the source Memory.
119
   * @param srcMem a compact Memory image of a sketch serialized by this sketch.
120
   * <a href="{@docRoot}/resources/dictionary.html#mem">See Memory</a>
121
   * @return a heap-based sketch based on the given Memory.
122
   */
123
  public static KllDoublesSketch heapify(final Memory srcMem) {
124
    Objects.requireNonNull(srcMem, "Parameter 'srcMem' must not be null");
1✔
125
    return KllHeapDoublesSketch.heapifyImpl(srcMem);
1✔
126
  }
127

128
  //Factory to wrap a Read-Only Memory
129

130
  /**
131
   * Wrap a sketch around the given read only compact source Memory containing sketch data
132
   * that originated from this sketch.
133
   * @param srcMem the read only source Memory
134
   * @return instance of this sketch
135
   */
136
  public static KllDoublesSketch wrap(final Memory srcMem) {
137
    Objects.requireNonNull(srcMem, "Parameter 'srcMem' must not be null");
1✔
138
    final KllMemoryValidate memVal = new KllMemoryValidate(srcMem, DOUBLES_SKETCH, null);
1✔
139
    if (memVal.sketchStructure == UPDATABLE) {
1✔
140
      final MemoryRequestServer memReqSvr = new DefaultMemoryRequestServer(); //dummy
1✔
141
      return new KllDirectDoublesSketch(memVal.sketchStructure, (WritableMemory)srcMem, memReqSvr, memVal);
1✔
142
    } else {
143
      return new KllDirectCompactDoublesSketch(memVal.sketchStructure, srcMem, memVal);
1✔
144
    }
145
  }
146

147
  //Factory to wrap a WritableMemory image
148

149
  /**
150
   * Wrap a sketch around the given source Writable Memory containing sketch data
151
   * that originated from this sketch.
152
   * @param srcMem a WritableMemory that contains data.
153
   * @param memReqSvr the given MemoryRequestServer to request a larger WritableMemory
154
   * @return instance of this sketch
155
   */
156
  public static KllDoublesSketch writableWrap(
157
      final WritableMemory srcMem,
158
      final MemoryRequestServer memReqSvr) {
159
    Objects.requireNonNull(srcMem, "Parameter 'srcMem' must not be null");
1✔
160
    Objects.requireNonNull(memReqSvr, "Parameter 'memReqSvr' must not be null");
1✔
161
    final KllMemoryValidate memVal = new KllMemoryValidate(srcMem, DOUBLES_SKETCH);
1✔
162
    if (memVal.sketchStructure == UPDATABLE) {
1✔
163
      return new KllDirectDoublesSketch(UPDATABLE, srcMem, memReqSvr, memVal);
1✔
164
    } else {
165
      return new KllDirectCompactDoublesSketch(memVal.sketchStructure, srcMem, memVal);
1✔
166
    }
167
  }
168

169
  //END of Constructors
170

171
  @Override
172
  public double[] getCDF(final double[] splitPoints, final QuantileSearchCriteria searchCrit) {
173
    if (isEmpty()) { throw new SketchesArgumentException(EMPTY_MSG); }
1✔
174
    refreshSortedView();
1✔
175
    return doublesSV.getCDF(splitPoints, searchCrit);
1✔
176
  }
177

178
  @Override
179
  public double[] getPMF(final double[] splitPoints, final QuantileSearchCriteria searchCrit) {
180
    if (isEmpty()) { throw new SketchesArgumentException(EMPTY_MSG); }
1✔
181
    refreshSortedView();
1✔
182
    return doublesSV.getPMF(splitPoints, searchCrit);
1✔
183
  }
184

185
  @Override
186
  public double getQuantile(final double rank, final QuantileSearchCriteria searchCrit) {
187
    if (isEmpty()) { throw new SketchesArgumentException(EMPTY_MSG); }
1✔
188
    refreshSortedView();
1✔
189
    return doublesSV.getQuantile(rank, searchCrit);
1✔
190
  }
191

192
  @Override
193
  public double[] getQuantiles(final double[] ranks, final QuantileSearchCriteria searchCrit) {
194
    if (isEmpty()) { throw new SketchesArgumentException(EMPTY_MSG); }
1✔
195
    refreshSortedView();
1✔
196
    final int len = ranks.length;
1✔
197
    final double[] quantiles = new double[len];
1✔
198
    for (int i = 0; i < len; i++) {
1✔
199
      quantiles[i] = doublesSV.getQuantile(ranks[i], searchCrit);
1✔
200
    }
201
    return quantiles;
1✔
202
  }
203

204
  /**
205
   * {@inheritDoc}
206
   * The approximate probability that the true quantile is within the confidence interval
207
   * specified by the upper and lower quantile bounds for this sketch is 0.99.
208
   */
209
  @Override
210
  public double getQuantileLowerBound(final double rank) {
211
    return getQuantile(max(0, rank - KllHelper.getNormalizedRankError(getMinK(), false)));
1✔
212
  }
213

214
  /**
215
   * {@inheritDoc}
216
   * The approximate probability that the true quantile is within the confidence interval
217
   * specified by the upper and lower quantile bounds for this sketch is 0.99.
218
   */
219
  @Override
220
  public double getQuantileUpperBound(final double rank) {
221
    return getQuantile(min(1.0, rank + KllHelper.getNormalizedRankError(getMinK(), false)));
1✔
222
  }
223

224
  @Override
225
  public double getRank(final double quantile, final QuantileSearchCriteria searchCrit) {
226
    if (isEmpty()) { throw new SketchesArgumentException(EMPTY_MSG); }
1✔
227
    refreshSortedView();
1✔
228
    return doublesSV.getRank(quantile, searchCrit);
1✔
229
  }
230

231
  /**
232
   * {@inheritDoc}
233
   * The approximate probability that the true rank is within the confidence interval
234
   * specified by the upper and lower rank bounds for this sketch is 0.99.
235
   */
236
  @Override
237
  public double getRankLowerBound(final double rank) {
238
    return max(0.0, rank - KllHelper.getNormalizedRankError(getMinK(), false));
1✔
239
  }
240

241
  /**
242
   * {@inheritDoc}
243
   * The approximate probability that the true rank is within the confidence interval
244
   * specified by the upper and lower rank bounds for this sketch is 0.99.
245
   */
246
  @Override
247
  public double getRankUpperBound(final double rank) {
248
    return min(1.0, rank + KllHelper.getNormalizedRankError(getMinK(), false));
1✔
249
  }
250

251
  @Override
252
  public double[] getRanks(final double[] quantiles, final QuantileSearchCriteria searchCrit) {
253
    if (isEmpty()) { throw new SketchesArgumentException(EMPTY_MSG); }
1✔
254
    refreshSortedView();
1✔
255
    final int len = quantiles.length;
1✔
256
    final double[] ranks = new double[len];
1✔
257
    for (int i = 0; i < len; i++) {
1✔
258
      ranks[i] = doublesSV.getRank(quantiles[i], searchCrit);
1✔
259
    }
260
    return ranks;
1✔
261
  }
262

263
  @Override
264
  public QuantilesDoublesSketchIterator iterator() {
265
    return new KllDoublesSketchIterator(
1✔
266
        getDoubleItemsArray(), getLevelsArray(SketchStructure.UPDATABLE), getNumLevels());
1✔
267
  }
268

269
  @Override
270
  public final void merge(final KllSketch other) {
271
    if (readOnly || sketchStructure != UPDATABLE) { throw new SketchesArgumentException(TGT_IS_READ_ONLY_MSG); }
1✔
272
    if (this == other) { throw new SketchesArgumentException(SELF_MERGE_MSG); }
1✔
273
    final KllDoublesSketch othDblSk = (KllDoublesSketch)other;
1✔
274
    if (othDblSk.isEmpty()) { return; }
1✔
275
    KllDoublesHelper.mergeDoubleImpl(this, othDblSk);
1✔
276
    doublesSV = null;
1✔
277
  }
1✔
278

279
  /**
280
   * {@inheritDoc}
281
   * <p>The parameter <i>k</i> will not change.</p>
282
   */
283
  @Override
284
  public final void reset() {
285
    if (readOnly) { throw new SketchesArgumentException(TGT_IS_READ_ONLY_MSG); }
1✔
286
    final int k = getK();
1✔
287
    setN(0);
1✔
288
    setMinK(k);
1✔
289
    setNumLevels(1);
1✔
290
    setLevelZeroSorted(false);
1✔
291
    setLevelsArray(new int[] {k, k});
1✔
292
    setMinItem(Double.NaN);
1✔
293
    setMaxItem(Double.NaN);
1✔
294
    setDoubleItemsArray(new double[k]);
1✔
295
    doublesSV = null;
1✔
296
  }
1✔
297

298
  @Override
299
  public byte[] toByteArray() {
300
    return KllHelper.toByteArray(this, false);
1✔
301
  }
302

303
  @Override
304
  public String toString(final boolean withLevels, final boolean withLevelsAndItems) {
305
    KllSketch sketch = this;
1✔
306
    if (withLevelsAndItems && sketchStructure != UPDATABLE) {
1✔
307
      final Memory mem = getWritableMemory();
1✔
308
      assert mem != null;
1✔
309
      sketch = KllDoublesSketch.heapify(getWritableMemory());
1✔
310
    }
311
    return KllHelper.toStringImpl(sketch, withLevels, withLevelsAndItems, getSerDe());
1✔
312
  }
313

314
  //SINGLE UPDATE
315

316
  @Override
317
  public void update(final double item) {
318
    if (Double.isNaN(item)) { return; } //ignore
1✔
319
    if (readOnly) { throw new SketchesArgumentException(TGT_IS_READ_ONLY_MSG); }
1✔
320
    updateDouble(this, item);
1✔
321
    doublesSV = null;
1✔
322
  }
1✔
323

324
  //Also Called from KllDoublesHelper::merge
325
  static void updateDouble(final KllDoublesSketch dblSk, final double item) {
326
    dblSk.updateMinMax(item);
1✔
327
    int freeSpace = dblSk.levelsArr[0];
1✔
328
    assert (freeSpace >= 0);
1✔
329
    if (freeSpace == 0) {
1✔
330
      KllDoublesHelper.compressWhileUpdatingSketch(dblSk);
1✔
331
      freeSpace = dblSk.levelsArr[0];
1✔
332
      assert (freeSpace > 0);
1✔
333
    }
334
    dblSk.incN(1);
1✔
335
    dblSk.setLevelZeroSorted(false);
1✔
336
    final int nextPos = freeSpace - 1;
1✔
337
    dblSk.setLevelsArrayAt(0, nextPos);
1✔
338
    dblSk.setDoubleItemsArrayAt(nextPos, item);
1✔
339
  }
1✔
340

341
  /**
342
   * Single update of min and max
343
   * @param item the source item, it must not be a NaN.
344
   */
345
  final void updateMinMax(final double item) {
346
    if (isEmpty() || Double.isNaN(getMinItemInternal())) {
1✔
347
      setMinItem(item);
1✔
348
      setMaxItem(item);
1✔
349
    } else {
350
      setMinItem(min(getMinItemInternal(), item));
1✔
351
      setMaxItem(max(getMaxItemInternal(), item));
1✔
352
    }
353
  }
1✔
354

355
  //WEIGHTED UPDATE
356

357
  /**
358
   * Weighted update. Updates this sketch with the given item the number of times specified by the given integer weight.
359
   * @param item the item to be repeated. NaNs are ignored.
360
   * @param weight the number of times the update of item is to be repeated. It must be &ge; one.
361
   */
362
  public void update(final double item, final long weight) {
363
    if (Double.isNaN(item)) { return; } //ignore
1✔
364
    if (readOnly) { throw new SketchesArgumentException(TGT_IS_READ_ONLY_MSG); }
1✔
365
    if (weight < 1L) { throw new SketchesArgumentException("Weight is less than one."); }
1✔
366
    if (weight == 1L) { updateDouble(this, item); }
1✔
367
    else {
368
      if (weight < levelsArr[0]) {
1✔
369
        for (int i = 0; i < (int)weight; i++) { updateDouble(this, item); }
1✔
370
      } else {
371
        final KllHeapDoublesSketch tmpSk = new KllHeapDoublesSketch(getK(), DEFAULT_M, item, weight);
1✔
372
        merge(tmpSk);
1✔
373
      }
374
    }
375
    doublesSV = null;
1✔
376
  }
1✔
377

378
  // VECTOR UPDATE
379

380
  /**
381
   * Vector update. Updates this sketch with the given array (vector) of items, starting at the items
382
   * offset for a length number of items. This is not supported for direct sketches.
383
   * @param items the vector of items
384
   * @param offset the starting index of the items[] array
385
   * @param length the number of items
386
   */
387
  public void update(final double[] items, final int offset, final int length) {
388
    if (readOnly) { throw new SketchesArgumentException(TGT_IS_READ_ONLY_MSG); }
1✔
389
    if (length == 0) { return; }
1✔
390
    if (!hasNaN(items, offset, length)) {
1✔
391
      updateDouble(items, offset, length); //fast path
1✔
392
      doublesSV = null;
1✔
393
      return;
1✔
394
    }
395
    //has at least one NaN
396
    final int end = offset + length;
×
397
    for (int i = offset; i < end; i++) {
×
398
      final double v = items[i];
×
399
      if (!Double.isNaN(v)) {
×
400
        updateDouble(this, v); //normal path
×
401
        doublesSV = null;
×
402
      }
403
    }
404
  }
×
405

406
  // No NaNs are allowed at this point
407
  private void updateDouble(final double[] srcItems, final int srcOffset, final int length) {
408
    if (isEmpty() || Double.isNaN(getMinItemInternal())) {
1✔
409
      setMinItem(srcItems[srcOffset]); //initialize with a real value
1✔
410
      setMaxItem(srcItems[srcOffset]);
1✔
411
    }
412

413
    int count = 0;
1✔
414
    while (count < length) {
1✔
415
      if (levelsArr[0] == 0) {
1✔
416
        KllDoublesHelper.compressWhileUpdatingSketch(this);
1✔
417
      }
418
      final int spaceNeeded = length - count;
1✔
419
      final int freeSpace = levelsArr[0];
1✔
420
      assert (freeSpace > 0);
1✔
421
      final int numItemsToCopy = min(spaceNeeded, freeSpace);
1✔
422
      final int dstOffset = freeSpace - numItemsToCopy;
1✔
423
      final int localSrcOffset = srcOffset + count;
1✔
424
      setDoubleItemsArrayAt(dstOffset, srcItems, localSrcOffset, numItemsToCopy);
1✔
425
      updateMinMax(srcItems, localSrcOffset, numItemsToCopy);
1✔
426
      count += numItemsToCopy;
1✔
427
      incN(numItemsToCopy);
1✔
428
      setLevelsArrayAt(0, dstOffset);
1✔
429
    }
1✔
430
    setLevelZeroSorted(false);
1✔
431
  }
1✔
432

433
  /**
434
   * Vector update of min and max.
435
   * @param srcItems the input source array of values, no NaNs allowed.
436
   * @param srcOffset the starting offset in srcItems
437
   * @param length the number of items to update min and max
438
   */
439
  private void updateMinMax(final double[] srcItems, final int srcOffset, final int length) {
440
    final int end = srcOffset + length;
1✔
441
    for (int i = srcOffset; i < end; i++) {
1✔
442
      setMinItem(min(getMinItemInternal(), srcItems[i]));
1✔
443
      setMaxItem(max(getMaxItemInternal(), srcItems[i]));
1✔
444
    }
445
  }
1✔
446

447
  // this returns on the first detected NaN.
448
  private static boolean hasNaN(final double[] items, final int offset, final int length) {
449
    final int end = offset + length;
1✔
450
    for (int i = offset; i < end; i++) {
1✔
451
      if (Double.isNaN(items[i])) { return true; }
1✔
452
    }
453
    return false;
1✔
454
  }
455

456
  // END ALL UPDATE METHODS
457

458
  /**
459
   * @return full size of internal items array including empty space at bottom.
460
   */
461
  abstract double[] getDoubleItemsArray();
462

463
  /**
464
   * @return items array of retained items.
465
   */
466
  abstract double[] getDoubleRetainedItemsArray();
467

468
  abstract double getDoubleSingleItem();
469

470
  // Min & Max Methods
471

472
  abstract double getMaxItemInternal();
473

474
  abstract void setMaxItem(double item);
475

476
  abstract double getMinItemInternal();
477

478
  abstract void setMinItem(double item);
479

480
  @Override
481
  abstract byte[] getMinMaxByteArr();
482

483
  @Override
484
  int getMinMaxSizeBytes() {
485
    return Double.BYTES * 2;
1✔
486
  }
487

488
  //END Min & Max Methods
489

490
  @Override
491
  abstract byte[] getRetainedItemsByteArr();
492

493
  @Override
494
  int getRetainedItemsSizeBytes() {
495
    return getNumRetained() * Double.BYTES;
1✔
496
  }
497

498
  @Override
499
  ArrayOfItemsSerDe<?> getSerDe() { return null; }
1✔
500

501
  @Override
502
  final byte[] getSingleItemByteArr() {
503
    final byte[] bytes = new byte[ITEM_BYTES];
1✔
504
    putDoubleLE(bytes, 0, getDoubleSingleItem());
1✔
505
    return bytes;
1✔
506
  }
507

508
  @Override
509
  int getSingleItemSizeBytes() {
510
    return Double.BYTES;
1✔
511
  }
512

513
  @Override
514
  abstract byte[] getTotalItemsByteArr();
515

516
  @Override
517
  int getTotalItemsNumBytes() {
518
    return levelsArr[getNumLevels()] * Double.BYTES;
1✔
519
  }
520

521
  abstract void setDoubleItemsArray(double[] doubleItems);
522

523
  abstract void setDoubleItemsArrayAt(int index, double item);
524

525
  abstract void setDoubleItemsArrayAt(int dstIndex, double[] srcItems, int srcOffset, int length);
526

527
  // SORTED VIEW
528

529
  @Override
530
  @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "OK in this case.")
531
  public DoublesSketchSortedView getSortedView() {
532
    refreshSortedView();
1✔
533
    return doublesSV;
1✔
534
  }
535

536
  private final DoublesSketchSortedView refreshSortedView() {
537
    if (doublesSV == null) {
1✔
538
      final CreateSortedView csv = new CreateSortedView();
1✔
539
      doublesSV = csv.getSV();
1✔
540
    }
541
    return doublesSV;
1✔
542
  }
543

544
  private final class CreateSortedView {
1✔
545
    double[] quantiles;
546
    long[] cumWeights;
547

548
    DoublesSketchSortedView getSV() {
549
      if (isEmpty()) { throw new SketchesArgumentException(EMPTY_MSG); }
1✔
550
      final double[] srcQuantiles = getDoubleItemsArray();
1✔
551
      final int[] srcLevels = levelsArr;
1✔
552
      final int srcNumLevels = getNumLevels();
1✔
553

554
      if (!isLevelZeroSorted()) {
1✔
555
        Arrays.sort(srcQuantiles, srcLevels[0], srcLevels[1]);
1✔
556
        if (!hasMemory()) { setLevelZeroSorted(true); }
1✔
557
        //we don't sort level0 in Memory, only our copy.
558
      }
559
      final int numQuantiles = getNumRetained();
1✔
560
      quantiles = new double[numQuantiles];
1✔
561
      cumWeights = new long[numQuantiles];
1✔
562
      populateFromSketch(srcQuantiles, srcLevels, srcNumLevels, numQuantiles);
1✔
563
      return new DoublesSketchSortedView(
1✔
564
          quantiles, cumWeights, KllDoublesSketch.this);
565
    }
566

567
    private void populateFromSketch(final double[] srcQuantiles, final int[] srcLevels,
568
      final int srcNumLevels, final int numItems) {
569
      final int[] myLevels = new int[srcNumLevels + 1];
1✔
570
      final int offset = srcLevels[0];
1✔
571
      System.arraycopy(srcQuantiles, offset, quantiles, 0, numItems);
1✔
572
      int srcLevel = 0;
1✔
573
      int dstLevel = 0;
1✔
574
      long weight = 1;
1✔
575
      while (srcLevel < srcNumLevels) {
1✔
576
        final int fromIndex = srcLevels[srcLevel] - offset;
1✔
577
        final int toIndex = srcLevels[srcLevel + 1] - offset; // exclusive
1✔
578
        if (fromIndex < toIndex) { // if equal, skip empty level
1✔
579
          Arrays.fill(cumWeights, fromIndex, toIndex, weight);
1✔
580
          myLevels[dstLevel] = fromIndex;
1✔
581
          myLevels[dstLevel + 1] = toIndex;
1✔
582
          dstLevel++;
1✔
583
        }
584
        srcLevel++;
1✔
585
        weight *= 2;
1✔
586
      }
1✔
587
      final int numLevels = dstLevel;
1✔
588
      blockyTandemMergeSort(quantiles, cumWeights, myLevels, numLevels); //create unit weights
1✔
589
      KllHelper.convertToCumulative(cumWeights);
1✔
590
    }
1✔
591
  } //End of class CreateSortedView
592

593
  private static void blockyTandemMergeSort(final double[] quantiles, final long[] weights,
594
      final int[] levels, final int numLevels) {
595
    if (numLevels == 1) { return; }
1✔
596

597
    // duplicate the input in preparation for the "ping-pong" copy reduction strategy.
598
    final double[] quantilesTmp = Arrays.copyOf(quantiles, quantiles.length);
1✔
599
    final long[] weightsTmp = Arrays.copyOf(weights, quantiles.length); // don't need the extra one
1✔
600

601
    blockyTandemMergeSortRecursion(quantilesTmp, weightsTmp, quantiles, weights, levels, 0, numLevels);
1✔
602
  }
1✔
603

604
  private static void blockyTandemMergeSortRecursion(
605
      final double[] quantilesSrc, final long[] weightsSrc,
606
      final double[] quantilesDst, final long[] weightsDst,
607
      final int[] levels, final int startingLevel, final int numLevels) {
608
    if (numLevels == 1) { return; }
1✔
609
    final int numLevels1 = numLevels / 2;
1✔
610
    final int numLevels2 = numLevels - numLevels1;
1✔
611
    assert numLevels1 >= 1;
1✔
612
    assert numLevels2 >= numLevels1;
1✔
613
    final int startingLevel1 = startingLevel;
1✔
614
    final int startingLevel2 = startingLevel + numLevels1;
1✔
615
    // swap roles of src and dst
616
    blockyTandemMergeSortRecursion(
1✔
617
        quantilesDst, weightsDst,
618
        quantilesSrc, weightsSrc,
619
        levels, startingLevel1, numLevels1);
620
    blockyTandemMergeSortRecursion(
1✔
621
        quantilesDst, weightsDst,
622
        quantilesSrc, weightsSrc,
623
        levels, startingLevel2, numLevels2);
624
    tandemMerge(
1✔
625
        quantilesSrc, weightsSrc,
626
        quantilesDst, weightsDst,
627
        levels,
628
        startingLevel1, numLevels1,
629
        startingLevel2, numLevels2);
630
  }
1✔
631

632
  private static void tandemMerge(
633
      final double[] quantilesSrc, final long[] weightsSrc,
634
      final double[] quantilesDst, final long[] weightsDst,
635
      final int[] levelStarts,
636
      final int startingLevel1, final int numLevels1,
637
      final int startingLevel2, final int numLevels2) {
638
    final int fromIndex1 = levelStarts[startingLevel1];
1✔
639
    final int toIndex1 = levelStarts[startingLevel1 + numLevels1]; // exclusive
1✔
640
    final int fromIndex2 = levelStarts[startingLevel2];
1✔
641
    final int toIndex2 = levelStarts[startingLevel2 + numLevels2]; // exclusive
1✔
642
    int iSrc1 = fromIndex1;
1✔
643
    int iSrc2 = fromIndex2;
1✔
644
    int iDst = fromIndex1;
1✔
645

646
    while (iSrc1 < toIndex1 && iSrc2 < toIndex2) {
1✔
647
      if (quantilesSrc[iSrc1] < quantilesSrc[iSrc2]) {
1✔
648
        quantilesDst[iDst] = quantilesSrc[iSrc1];
1✔
649
        weightsDst[iDst] = weightsSrc[iSrc1];
1✔
650
        iSrc1++;
1✔
651
      } else {
652
        quantilesDst[iDst] = quantilesSrc[iSrc2];
1✔
653
        weightsDst[iDst] = weightsSrc[iSrc2];
1✔
654
        iSrc2++;
1✔
655
      }
656
      iDst++;
1✔
657
    }
658
    if (iSrc1 < toIndex1) {
1✔
659
      System.arraycopy(quantilesSrc, iSrc1, quantilesDst, iDst, toIndex1 - iSrc1);
1✔
660
      System.arraycopy(weightsSrc, iSrc1, weightsDst, iDst, toIndex1 - iSrc1);
1✔
661
    } else if (iSrc2 < toIndex2) {
1✔
662
      System.arraycopy(quantilesSrc, iSrc2, quantilesDst, iDst, toIndex2 - iSrc2);
1✔
663
      System.arraycopy(weightsSrc, iSrc2, weightsDst, iDst, toIndex2 - iSrc2);
1✔
664
    }
665
  }
1✔
666

667
  // END SORTED VIEW
668

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

© 2026 Coveralls, Inc