1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.numbers.examples.jmh.arrays;
19
20 import java.util.Arrays;
21 import java.util.BitSet;
22
23 /**
24 * A K<sup>th</sup> selector implementation to pick up the K<sup>th</sup> ordered element
25 * from a data array containing the input numbers. Uses a partial sort of the data.
26 *
27 * <p>Note: The search may use a cache of pivots. A pivot is a K<sup>th</sup> index that
28 * corresponds to a value correctly positioned within the equivalent fully sorted data array.
29 * Each step of the algorithm partitions the array between a lower and upper bound into
30 * values above or below a value chosen from the interval. The index of this value after the
31 * partition is stored as a pivot. A subsequent search into the array can use the pivots to
32 * quickly bracket the interval for the next target index.
33 *
34 * <p>The maximum supported cache size is 2^30 - 1. This ensures that any valid index i
35 * into the cache can be increased for the next level using 2 * i + 2 without overflow.
36 *
37 * <p>The method for choosing the value within the interval to pivot around is specified by
38 * the {@link PivotingStrategy}. Ideally this should provide a guess of the middle (median)
39 * of the interval. The value must be within the interval, thus using for example the
40 * mean of the end values is not an option. The default uses a guess for the median from
41 * 3 values that are likely to be representative of the range of values.
42 *
43 * <p>This implementation provides the option to return the (K+1) ordered element along with
44 * the K<sup>th</sup>. This uses the position of K and the most recent upper bound on the
45 * bracket known to contain values greater than the K<sup>th</sup>. This prevents using a
46 * second search into the array for the (K+1) element.
47 *
48 * @since 1.2
49 */
50 class KthSelector {
51 /** Empty pivots array. */
52 static final int[] NO_PIVOTS = {};
53 /** Minimum selection size for insertion sort rather than selection.
54 * Dual-pivot quicksort used 27 in the original paper. */
55 private static final int MIN_SELECT_SIZE = 17;
56
57 /** A {@link PivotingStrategy} used for pivoting. */
58 private final PivotingStrategy pivotingStrategy;
59
60 /** Minimum selection size for insertion sort rather than selection. */
61 private final int minSelectSize;
62
63 /**
64 * Constructor with default {@link PivotingStrategy#MEDIAN_OF_3 median of 3} pivoting
65 * strategy.
66 */
67 KthSelector() {
68 this(PivotingStrategy.MEDIAN_OF_3);
69 }
70
71 /**
72 * Constructor with specified pivoting strategy.
73 *
74 * @param pivotingStrategy Pivoting strategy to use.
75 */
76 KthSelector(PivotingStrategy pivotingStrategy) {
77 this(pivotingStrategy, MIN_SELECT_SIZE);
78 }
79
80 /**
81 * Constructor with specified pivoting strategy and select size.
82 *
83 * @param pivotingStrategy Pivoting strategy to use.
84 * @param minSelectSize Minimum selection size for insertion sort rather than selection.
85 */
86 KthSelector(PivotingStrategy pivotingStrategy, int minSelectSize) {
87 this.pivotingStrategy = pivotingStrategy;
88 this.minSelectSize = minSelectSize;
89 }
90
91 /**
92 * Select K<sup>th</sup> value in the array. Optionally select the next value after K.
93 *
94 * <p>Note: If K+1 is requested this method assumes it is a valid index into the array.
95 *
96 * <p>Uses a single-pivot partition method.
97 *
98 * @param data Data array to use to find out the K<sup>th</sup> value.
99 * @param k Index whose value in the array is of interest.
100 * @param kp1 K+1<sup>th</sup> value (if not null)
101 * @return K<sup>th</sup> value
102 */
103 double selectSP(double[] data, int k, double[] kp1) {
104 int begin = 0;
105 int end = data.length;
106 while (end - begin > minSelectSize) {
107 // Select a pivot and partition data array around it
108 final int pivot = partitionSP(data, begin, end,
109 pivotingStrategy.pivotIndex(data, begin, end - 1, k));
110 if (k == pivot) {
111 // The pivot was exactly the element we wanted
112 return finalSelection(data, k, kp1, end);
113 } else if (k < pivot) {
114 // The element is in the left partition
115 end = pivot;
116 } else {
117 // The element is in the right partition
118 begin = pivot + 1;
119 }
120 }
121 sortRange(data, begin, end);
122 if (kp1 != null) {
123 // Either end == data.length and k+1 is sorted; or
124 // end == pivot where data[k] <= data[pivot] <= data[pivot+j] for all j
125 kp1[0] = data[k + 1];
126 }
127 return data[k];
128 }
129
130 /**
131 * Select K<sup>th</sup> value in the array. Optionally select the next value after K.
132 *
133 * <p>Note: If K+1 is requested this method assumes it is a valid index into the array.
134 *
135 * <p>Uses a single-pivot partition method with special handling of NaN and signed zeros.
136 * Correction of signed zeros requires a sweep across the entire range.
137 *
138 * @param data Data array to use to find out the K<sup>th</sup> value.
139 * @param k Index whose value in the array is of interest.
140 * @param kp1 K+1<sup>th</sup> value (if not null)
141 * @return K<sup>th</sup> value
142 */
143 double selectSPN(double[] data, int k, double[] kp1) {
144 // Handle NaN
145 final int length = sortNaN(data);
146 if (k >= length) {
147 if (kp1 != null) {
148 kp1[0] = Double.NaN;
149 }
150 return Double.NaN;
151 }
152
153 int begin = 0;
154 int end = length;
155 while (end - begin > minSelectSize) {
156 // Select a pivot and partition data array around it
157 final int pivot = partitionSPN(data, begin, end,
158 pivotingStrategy.pivotIndex(data, begin, end - 1, k));
159 if (k == pivot) {
160 // The pivot was exactly the element we wanted
161 if (data[k] == 0) {
162 orderSignedZeros(data, 0, length);
163 }
164 return finalSelection(data, k, kp1, end);
165 } else if (k < pivot) {
166 // The element is in the left partition
167 end = pivot;
168 } else {
169 // The element is in the right partition
170 begin = pivot + 1;
171 }
172 }
173 insertionSort(data, begin, end, begin != 0);
174 if (data[k] == 0) {
175 orderSignedZeros(data, 0, length);
176 }
177 if (kp1 != null) {
178 // Either end == data.length and k+1 is sorted; or
179 // end == pivot where data[k] <= data[pivot] <= data[pivot+j] for all j
180 kp1[0] = data[k + 1];
181 }
182 return data[k];
183 }
184
185 /**
186 * Select K<sup>th</sup> value in the array. Optionally select the next value after K.
187 *
188 * <p>Note: If K+1 is requested this method assumes it is a valid index into the array.
189 *
190 * <p>Uses a single-pivot partition method with a heap cache.
191 *
192 * <p>This method can be used for repeat calls to identify K<sup>th</sup> values in
193 * the same array by caching locations of pivots. Maximum supported heap size is 2^30 - 1.
194 *
195 * @param data Data array to use to find out the K<sup>th</sup> value.
196 * @param pivotsHeap Cached pivots heap that can be used for efficient estimation.
197 * @param k Index whose value in the array is of interest.
198 * @param kp1 K+1<sup>th</sup> value (if not null)
199 * @return K<sup>th</sup> value
200 */
201 double selectSPH(double[] data, int[] pivotsHeap, int k, double[] kp1) {
202 final int heapLength = pivotsHeap.length;
203 if (heapLength == 0) {
204 // No pivots
205 return selectSP(data, k, kp1);
206 }
207 int begin = 0;
208 int end = data.length;
209 int node = 0;
210 while (end - begin > minSelectSize) {
211 int pivot;
212
213 if (node < heapLength && pivotsHeap[node] >= 0) {
214 // The pivot has already been found in a previous call
215 // and the array has already been partitioned around it
216 pivot = pivotsHeap[node];
217 } else {
218 // Select a pivot and partition data array around it
219 pivot = partitionSP(data, begin, end,
220 pivotingStrategy.pivotIndex(data, begin, end - 1, k));
221 if (node < heapLength) {
222 pivotsHeap[node] = pivot;
223 }
224 }
225
226 if (k == pivot) {
227 // The pivot was exactly the element we wanted
228 return finalSelection(data, k, kp1, end);
229 } else if (k < pivot) {
230 // The element is in the left partition
231 end = pivot;
232 if (node < heapLength) {
233 node = Math.min((node << 1) + 1, heapLength);
234 }
235 } else {
236 // The element is in the right partition
237 begin = pivot + 1;
238 if (node < heapLength) {
239 node = Math.min((node << 1) + 2, heapLength);
240 }
241 }
242 }
243 sortRange(data, begin, end);
244 if (kp1 != null) {
245 // Either end == data.length and k+1 is sorted; or
246 // end == pivot where data[k] <= data[pivot] <= data[pivot+j] for all j
247 kp1[0] = data[k + 1];
248 }
249 return data[k];
250 }
251
252 /**
253 * Select K<sup>th</sup> value in the array. Optionally select the next value after K.
254 *
255 * <p>Note: If K+1 is requested this method assumes it is a valid index into the array
256 * (i.e. K is not the last index in the array).
257 *
258 * @param data Data array to use to find out the K<sup>th</sup> value.
259 * @param k Index whose value in the array is of interest.
260 * @param kp1 K+1<sup>th</sup> value (if not null)
261 * @param end Upper bound (exclusive) of the interval containing K.
262 * This should be either a pivot point {@code data[k] <= data[end]} or the length
263 * of the data array.
264 * @return K<sup>th</sup> value
265 */
266 private static double finalSelection(double[] data, int k, double[] kp1, int end) {
267 if (kp1 != null) {
268 // After partitioning all elements above k are greater than or equal to k.
269 // Find the minimum of the elements above.
270 // Set the k+1 limit as either a pivot or the end of the data.
271 final int limit = Math.min(end, data.length - 1);
272 double min = data[k + 1];
273 for (int i = k + 2; i <= limit; i++) {
274 if (DoubleMath.lessThan(data[i], min)) {
275 min = data[i];
276 }
277 }
278 kp1[0] = min;
279 }
280 return data[k];
281 }
282
283 /**
284 * Partition the array such that indices {@code k} correspond to their correctly
285 * sorted value in the equivalent fully sorted array. For all indices {@code k}
286 * and any index {@code i}:
287 *
288 * <pre>{@code
289 * data[i < k] <= data[k] <= data[k < i]
290 * }</pre>
291 *
292 * <p>Uses a single-pivot partition method.
293 *
294 * @param data Values.
295 * @param k Indices.
296 */
297 void partitionSP(double[] data, int... k) {
298 final int n = k.length;
299 if (n <= 1) {
300 if (n == 1) {
301 selectSP(data, k[0], null);
302 }
303 return;
304 }
305 // Multiple pivots
306 final int length = data.length;
307 final BitSet pivots = new BitSet(length);
308
309 for (int i = 0; i < n; i++) {
310 final int kk = k[i];
311 if (pivots.get(kk)) {
312 // Already sorted
313 continue;
314 }
315 int begin;
316 int end;
317 if (i == 0) {
318 begin = 0;
319 end = length;
320 } else {
321 // Start inclusive
322 begin = pivots.previousSetBit(kk) + 1;
323 end = pivots.nextSetBit(kk + 1);
324 if (end < 0) {
325 end = length;
326 }
327 }
328 partitionSP(data, begin, end, pivots, kk);
329 }
330 }
331
332 /**
333 * Partition around the K<sup>th</sup> value in the array.
334 *
335 * <p>This method can be used for repeat calls to identify K<sup>th</sup> values in
336 * the same array by caching locations of pivots (correctly sorted indices).
337 *
338 * <p>Uses a single-pivot partition method.
339 *
340 * @param data Data array to use to find out the K<sup>th</sup> value.
341 * @param begin Lower bound (inclusive).
342 * @param end Upper bound (exclusive).
343 * @param pivots Cache of pivot points.
344 * @param k Index whose value in the array is of interest.
345 */
346 private void partitionSP(double[] data, int begin, int end, BitSet pivots, int k) {
347 // Find the unsorted range containing k
348 while (end - begin > minSelectSize) {
349 // Select a value and partition data array around it
350 final int pivot = partitionSP(data, begin, end,
351 pivotingStrategy.pivotIndex(data, begin, end - 1, k));
352 pivots.set(pivot);
353 if (k == pivot) {
354 // The pivot was exactly the element we wanted
355 return;
356 } else if (k < pivot) {
357 // The element is in the left partition
358 end = pivot;
359 } else {
360 // The element is in the right partition
361 begin = pivot + 1;
362 }
363 }
364 setPivots(begin, end, pivots);
365 sortRange(data, begin, end);
366 }
367
368 /**
369 * Partition an array slice around a pivot. Partitioning exchanges array elements such
370 * that all elements smaller than pivot are before it and all elements larger than
371 * pivot are after it.
372 *
373 * <p>Uses a single-pivot partition method.
374 *
375 * @param data Data array.
376 * @param begin Lower bound (inclusive).
377 * @param end Upper bound (exclusive).
378 * @param pivot Initial index of the pivot.
379 * @return index of the pivot after partition
380 */
381 private static int partitionSP(double[] data, int begin, int end, int pivot) {
382 final double value = data[pivot];
383 data[pivot] = data[begin];
384
385 int i = begin + 1;
386 int j = end - 1;
387 while (i < j) {
388 while (i < j && DoubleMath.greaterThan(data[j], value)) {
389 --j;
390 }
391 while (i < j && DoubleMath.lessThan(data[i], value)) {
392 ++i;
393 }
394 if (i < j) {
395 final double tmp = data[i];
396 data[i++] = data[j];
397 data[j--] = tmp;
398 }
399 }
400
401 if (i >= end || DoubleMath.greaterThan(data[i], value)) {
402 --i;
403 }
404 data[begin] = data[i];
405 data[i] = value;
406 return i;
407 }
408
409 /**
410 * Partition the array such that indices {@code k} correspond to their correctly
411 * sorted value in the equivalent fully sorted array. For all indices {@code k}
412 * and any index {@code i}:
413 *
414 * <pre>{@code
415 * data[i < k] <= data[k] <= data[k < i]
416 * }</pre>
417 *
418 * <p>Uses a single-pivot partition method.
419 *
420 * @param data Values.
421 * @param k Indices.
422 */
423 void partitionSPN(double[] data, int... k) {
424 final int n = k.length;
425 if (n <= 1) {
426 if (n == 1) {
427 selectSPN(data, k[0], null);
428 }
429 return;
430 }
431 // Multiple pivots
432
433 // Handle NaN
434 final int length = sortNaN(data);
435 if (length < 1) {
436 return;
437 }
438
439 final BitSet pivots = new BitSet(length);
440
441 // Flag any pivots that are zero
442 boolean zeros = false;
443 for (int i = 0; i < n; i++) {
444 final int kk = k[i];
445 if (kk >= length || pivots.get(kk)) {
446 // Already sorted
447 continue;
448 }
449 int begin;
450 int end;
451 if (i == 0) {
452 begin = 0;
453 end = length;
454 } else {
455 // Start inclusive
456 begin = pivots.previousSetBit(kk) + 1;
457 end = pivots.nextSetBit(kk + 1);
458 if (end < 0) {
459 end = length;
460 }
461 }
462 partitionSPN(data, begin, end, pivots, kk);
463 zeros = zeros || data[kk] == 0;
464 }
465
466 // Handle signed zeros
467 if (zeros) {
468 orderSignedZeros(data, 0, length);
469 }
470 }
471
472 /**
473 * Partition around the K<sup>th</sup> value in the array.
474 *
475 * <p>This method can be used for repeat calls to identify K<sup>th</sup> values in
476 * the same array by caching locations of pivots (correctly sorted indices).
477 *
478 * <p>Note: Requires that the range contains no NaN values. Does not partition
479 * around signed zeros.
480 *
481 * <p>Uses a single-pivot partition method.
482 *
483 * @param data Data array to use to find out the K<sup>th</sup> value.
484 * @param begin Lower bound (inclusive).
485 * @param end Upper bound (exclusive).
486 * @param pivots Cache of pivot points.
487 * @param k Index whose value in the array is of interest.
488 */
489 private void partitionSPN(double[] data, int begin, int end, BitSet pivots, int k) {
490 // Find the unsorted range containing k
491 while (end - begin > minSelectSize) {
492 // Select a value and partition data array around it
493 final int pivot = partitionSPN(data, begin, end,
494 pivotingStrategy.pivotIndex(data, begin, end - 1, k));
495 pivots.set(pivot);
496 if (k == pivot) {
497 // The pivot was exactly the element we wanted
498 return;
499 } else if (k < pivot) {
500 // The element is in the left partition
501 end = pivot;
502 } else {
503 // The element is in the right partition
504 begin = pivot + 1;
505 }
506 }
507 setPivots(begin, end, pivots);
508 sortRange(data, begin, end);
509 }
510
511 /**
512 * Partition an array slice around a pivot. Partitioning exchanges array elements such
513 * that all elements smaller than pivot are before it and all elements larger than
514 * pivot are after it.
515 *
516 * <p>Note: Requires that the range contains no NaN values. Does not partition
517 * around signed zeros.
518 *
519 * <p>Uses a single-pivot partition method.
520 *
521 * @param data Data array.
522 * @param begin Lower bound (inclusive).
523 * @param end Upper bound (exclusive).
524 * @param pivot Initial index of the pivot.
525 * @return index of the pivot after partition
526 */
527 private static int partitionSPN(double[] data, int begin, int end, int pivot) {
528 final double value = data[pivot];
529 data[pivot] = data[begin];
530
531 int i = begin + 1;
532 int j = end - 1;
533 while (i < j) {
534 while (i < j && data[j] > value) {
535 --j;
536 }
537 while (i < j && data[i] < value) {
538 ++i;
539 }
540 if (i < j) {
541 final double tmp = data[i];
542 data[i++] = data[j];
543 data[j--] = tmp;
544 }
545 }
546
547 if (i >= end || data[i] > value) {
548 --i;
549 }
550 data[begin] = data[i];
551 data[i] = value;
552 return i;
553 }
554
555 /**
556 * Sort an array range.
557 *
558 * @param data Data array.
559 * @param begin Lower bound (inclusive).
560 * @param end Upper bound (exclusive).
561 */
562 private static void sortRange(double[] data, int begin, int end) {
563 Arrays.sort(data, begin, end);
564 }
565
566 /**
567 * Sorts an array using an insertion sort.
568 *
569 * <p>Note: Requires that the range contains no NaN values. It does not respect the
570 * order of signed zeros.
571 *
572 * <p>This method is fast up to approximately 40 - 80 values.
573 *
574 * <p>The {@code internal} flag indicates that the value at {@code data[begin - 1]}
575 * is sorted.
576 *
577 * @param data Data array.
578 * @param begin Lower bound (inclusive).
579 * @param end Upper bound (exclusive).
580 * @param internal Internal flag.
581 */
582 private static void insertionSort(double[] data, int begin, int end, boolean internal) {
583 Sorting.sort(data, begin, end - 1, internal);
584 }
585
586 /**
587 * Move NaN values to the end of the array.
588 * This allows all other values to be compared using {@code <, ==, >} operators (with
589 * the exception of signed zeros).
590 *
591 * @param data Values.
592 * @return end of non-NaN values
593 */
594 private static int sortNaN(double[] data) {
595 int end = data.length;
596 // Avoid unnecessary moves
597 while (--end > 0) {
598 if (!Double.isNaN(data[end])) {
599 break;
600 }
601 }
602 end++;
603 for (int i = end; i > 0;) {
604 final double v = data[--i];
605 if (Double.isNaN(v)) {
606 data[i] = data[--end];
607 data[end] = v;
608 }
609 }
610 return end;
611 }
612
613 /**
614 * Detect and fix the sort order of signed zeros. Assumes the data may have been
615 * partially ordered around zero.
616 *
617 * <p>Searches for zeros if {@code data[begin] <= 0} and {@code data[end - 1] >= 0}.
618 * If zeros are discovered in the range then they are assumed to be continuous.
619 *
620 * @param data Values.
621 * @param begin Lower bound (inclusive).
622 * @param end Upper bound (exclusive).
623 */
624 private static void fixSignedZeros(double[] data, int begin, int end) {
625 int j;
626 if (data[begin] <= 0 && data[end - 1] >= 0) {
627 int i = begin;
628 while (data[i] < 0) {
629 i++;
630 }
631 j = end - 1;
632 while (data[j] > 0) {
633 j--;
634 }
635 sortZero(data, i, j + 1);
636 }
637 }
638
639 /**
640 * Count the number of signed zeros in the range and order them to be correctly
641 * sorted. This checks all values in the range. It does not assume zeros are continuous.
642 *
643 * @param data Values.
644 * @param begin Lower bound (inclusive).
645 * @param end Upper bound (exclusive).
646 */
647 private static void orderSignedZeros(double[] data, int begin, int end) {
648 int c = countSignedZeros(data, begin, end);
649 if (c != 0) {
650 int i = begin - 1;
651 while (++i < end) {
652 if (data[i] == 0) {
653 data[i] = -0.0;
654 if (--c == 0) {
655 break;
656 }
657 }
658 }
659 while (++i < end) {
660 if (data[i] == 0) {
661 data[i] = 0.0;
662 }
663 }
664 }
665 }
666
667 /**
668 * Count the number of signed zeros (-0.0).
669 *
670 * @param data Values.
671 * @param begin Lower bound (inclusive).
672 * @param end Upper bound (exclusive).
673 * @return the count
674 */
675 static int countSignedZeros(double[] data, int begin, int end) {
676 // Count negative zeros
677 int c = 0;
678 for (int i = begin; i < end; i++) {
679 if (data[i] == 0 && Double.doubleToRawLongBits(data[i]) < 0) {
680 c++;
681 }
682 }
683 return c;
684 }
685
686 /**
687 * Sort a range of all zero values.
688 * This orders -0.0 before 0.0.
689 *
690 * @param data Values.
691 * @param begin Lower bound (inclusive).
692 * @param end Upper bound (exclusive).
693 */
694 static void sortZero(double[] data, int begin, int end) {
695 // Count negative zeros
696 int c = 0;
697 for (int i = begin; i < end; i++) {
698 if (Double.doubleToRawLongBits(data[i]) < 0) {
699 c++;
700 }
701 }
702 // Replace
703 if (c != 0) {
704 int i = begin;
705 while (c-- > 0) {
706 data[i++] = -0.0;
707 }
708 while (i < end) {
709 data[i++] = 0.0;
710 }
711 }
712 }
713
714 /**
715 * Sets the pivots.
716 *
717 * @param from Start (inclusive)
718 * @param to End (exclusive)
719 * @param pivots the pivots
720 */
721 private static void setPivots(int from, int to, BitSet pivots) {
722 if (from + 1 == to) {
723 pivots.set(from);
724 } else {
725 pivots.set(from, to);
726 }
727 }
728
729 /**
730 * Partition the array such that indices {@code k} correspond to their correctly
731 * sorted value in the equivalent fully sorted array. For all indices {@code k}
732 * and any index {@code i}:
733 *
734 * <pre>{@code
735 * data[i < k] <= data[k] <= data[k < i]
736 * }</pre>
737 *
738 * <p>Uses a Bentley-McIlroy quicksort partition method by Sedgewick.
739 *
740 * @param data Values.
741 * @param k Indices.
742 */
743 void partitionSBM(double[] data, int... k) {
744 final int n = k.length;
745 if (n < 1) {
746 return;
747 }
748
749 // Handle NaN
750 final int length = sortNaN(data);
751 if (length < 1) {
752 return;
753 }
754
755 if (n == 1) {
756 if (k[0] < length) {
757 partitionSBM(data, 0, length, k[0]);
758 }
759 return;
760 }
761 // Special case for partition around adjacent indices (for interpolation)
762 if (n == 2 && k[0] + 1 == k[1]) {
763 if (k[0] < length) {
764 final int p = partitionSBM(data, 0, length, k[0]);
765 if (p > k[1]) {
766 partitionMin(data, k[1], p);
767 }
768 }
769 return;
770 }
771
772 // To partition all k requires not moving any pivot k after it has been
773 // processed. This is supported using two strategies:
774 //
775 // 1. Processing k in sorted order:
776 // (k1, end), (k2, end), (k3, end), ... , k1 <= k2 <= k3
777 // This can reorder each region during processing without destroying sorted k.
778 //
779 // 2. Processing unique k and visiting array regions only once:
780 // Pre-process the pivots to make them unique and store the entire sorted
781 // region between the end pivots (k1, kn) in a BitSet type structure:
782 // |k1|......|k2|....|p|k3|k4|pppp|......|kn|
783 // k can be processed in any order, e.g. k3. We use already sorted regions
784 // |p| to bracket the search for each k, and skip k that are already sorted (k4).
785 // Worst case storage cost is Order(N / 64).
786 // The advantage is never visiting any part of the array twice. If the pivots
787 // saturate the entire range then performance degrades to the speed of
788 // the sort of the entire array.
789
790 // Multiple pivots
791 final BitSet pivots = new BitSet(length);
792
793 for (int i = 0; i < n; i++) {
794 final int kk = k[i];
795 if (kk >= length || pivots.get(kk)) {
796 // Already sorted
797 continue;
798 }
799 int begin;
800 int end;
801 if (i == 0) {
802 begin = 0;
803 end = length;
804 } else {
805 // Start inclusive
806 begin = pivots.previousSetBit(kk) + 1;
807 end = pivots.nextSetBit(kk + 1);
808 if (end < 0) {
809 end = length;
810 }
811 }
812 partitionSBM(data, begin, end, pivots, kk);
813 }
814 }
815
816 /**
817 * Move the minimum value to the start of the range.
818 *
819 * <p>Note: Requires that the range contains no NaN values.
820 * Does not respect the ordering of signed zeros.
821 *
822 * @param data Data array to use to find out the K<sup>th</sup> value.
823 * @param begin Lower bound (inclusive).
824 * @param end Upper bound (exclusive).
825 */
826 static void partitionMin(double[] data, int begin, int end) {
827 int i = begin;
828 double min = data[i];
829 int j = i;
830 while (++i < end) {
831 if (data[i] < min) {
832 min = data[i];
833 j = i;
834 }
835 }
836 //swap(data, begin, j)
837 data[j] = data[begin];
838 data[begin] = min;
839 }
840
841 /**
842 * Move the maximum value to the end of the range.
843 *
844 * <p>Note: Requires that the range contains no NaN values.
845 * Does not respect the ordering of signed zeros.
846 *
847 * @param data Data array to use to find out the K<sup>th</sup> value.
848 * @param begin Lower bound (inclusive).
849 * @param end Upper bound (exclusive).
850 */
851 static void partitionMax(double[] data, int begin, int end) {
852 int i = end - 1;
853 double max = data[i];
854 int j = i;
855 while (--i >= begin) {
856 if (data[i] > max) {
857 max = data[i];
858 j = i;
859 }
860 }
861 //swap(data, end - 1, j)
862 data[j] = data[end - 1];
863 data[end - 1] = max;
864 }
865
866 /**
867 * Partition around the K<sup>th</sup> value in the array.
868 *
869 * <p>This method can be used for repeat calls to identify K<sup>th</sup> values in
870 * the same array by caching locations of pivots (correctly sorted indices).
871 *
872 * <p>Note: Requires that the range contains no NaN values.
873 *
874 * <p>Uses a Bentley-McIlroy quicksort partition method by Sedgewick.
875 *
876 * @param data Data array to use to find out the K<sup>th</sup> value.
877 * @param begin Lower bound (inclusive).
878 * @param end Upper bound (exclusive).
879 * @param pivots Cache of pivot points.
880 * @param k Index whose value in the array is of interest.
881 */
882 private void partitionSBM(double[] data, int begin, int end, BitSet pivots, int k) {
883 // Find the unsorted range containing k
884 final int[] upper = {0};
885 while (end - begin > minSelectSize) {
886 // Select a value and partition data array around it
887 final int from = partitionSBM(data, begin, end,
888 pivotingStrategy.pivotIndex(data, begin, end - 1, k), upper);
889 final int to = upper[0];
890 setPivots(from, to, pivots);
891 if (k >= to) {
892 // The element is in the right partition
893 begin = to;
894 } else if (k < from) {
895 // The element is in the left partition
896 end = from;
897 } else {
898 // The range contains the element we wanted
899 return;
900 }
901 }
902 setPivots(begin, end, pivots);
903 insertionSort(data, begin, end, begin != 0);
904 fixSignedZeros(data, begin, end);
905 }
906
907 /**
908 * Partition an array slice around a pivot. Partitioning exchanges array elements such
909 * that all elements smaller than pivot are before it and all elements larger than
910 * pivot are after it.
911 *
912 * <p>Note: Requires that the range contains no NaN values.
913 *
914 * <p>Uses a Bentley-McIlroy quicksort partition method by Sedgewick.
915 *
916 * @param data Data array.
917 * @param begin Lower bound (inclusive).
918 * @param end Upper bound (exclusive).
919 * @param pivot Initial index of the pivot.
920 * @param upper Upper bound (exclusive) of the pivot range.
921 * @return Lower bound (inclusive) of the pivot range.
922 */
923 private static int partitionSBM(double[] data, int begin, int end, int pivot, int[] upper) {
924 // Single-pivot Bentley-McIlroy quicksort handling equal keys (Sedgewick's algorithm).
925 //
926 // Partition data using pivot P into less-than, greater-than or equal.
927 // P is placed at the end to act as a sentinal.
928 // k traverses the unknown region ??? and values moved if equal (l) or greater (g):
929 //
930 // left p i j q right
931 // | ==P | <P | ??? | >P | ==P |P|
932 //
933 // At the end P and additional equal values are swapped back to the centre.
934 //
935 // | <P | ==P | >P |
936 //
937 // Adapted from Sedgewick "Quicksort is optimal"
938 // https://sedgewick.io/wp-content/themes/sedgewick/talks/2002QuicksortIsOptimal.pdf
939 //
940 // The algorithm has been changed so that:
941 // - A pivot point must be provided.
942 // - An edge case where the search meets in the middle is handled.
943 // - Equal value data is not swapped to the end. Since the value is fixed then
944 // only the less than / greater than value must be moved from the end inwards.
945 // The end is then assumed to be the equal value. This would not work with
946 // object references. Equivalent swap calls are commented.
947 // - Added a fast-forward over initial range containing the pivot.
948
949 final int l = begin;
950 final int r = end - 1;
951
952 int p = l;
953 int q = r;
954
955 // Use the pivot index to set the upper sentinal value
956 final double v = data[pivot];
957 data[pivot] = data[r];
958 data[r] = v;
959
960 // Special case: count signed zeros
961 int c = 0;
962 if (v == 0) {
963 c = countSignedZeros(data, begin, end);
964 }
965
966 // Fast-forward over equal regions to reduce swaps
967 while (data[p] == v) {
968 if (++p == q) {
969 // Edge-case: constant value
970 if (c != 0) {
971 sortZero(data, begin, end);
972 }
973 upper[0] = end;
974 return begin;
975 }
976 }
977 // Cannot overrun as the prior scan using p stopped before the end
978 while (data[q - 1] == v) {
979 q--;
980 }
981
982 int i = p - 1;
983 int j = q;
984
985 for (;;) {
986 do {
987 ++i;
988 } while (data[i] < v);
989 while (v < data[--j]) {
990 // Stop at l (not i) allows scan loops to be independent
991 if (j == l) {
992 break;
993 }
994 }
995 if (i >= j) {
996 // Edge-case if search met on an internal pivot value
997 // (not at the greater equal region, i.e. i < q).
998 // Move this to the lower-equal region.
999 if (i == j && v == data[i]) {
1000 //swap(data, i++, p++)
1001 //data[i++] = data[p++];
1002 data[i++] = data[p];
1003 data[p++] = v;
1004 }
1005 break;
1006 }
1007 //swap(data, i, j)
1008 final double vj = data[i];
1009 final double vi = data[j];
1010 data[i] = vi;
1011 data[j] = vj;
1012 if (vi == v) {
1013 //swap(data, i, p++)
1014 //data[i] = data[p++];
1015 data[i] = data[p];
1016 data[p++] = v;
1017 }
1018 if (vj == v) {
1019 //swap(data, j, --q)
1020 data[j] = data[--q];
1021 data[q] = v;
1022 }
1023 }
1024 // i is at the end (exclusive) of the less-than region
1025
1026 // Place pivot value in centre
1027 //swap(data, r, i)
1028 data[r] = data[i];
1029 data[i] = v;
1030
1031 // Move equal regions to the centre.
1032 // Set the pivot range [j, i) and move this outward for equal values.
1033 j = i++;
1034
1035 // less-equal:
1036 // for (int k = l; k < p; k++):
1037 // swap(data, k, --j)
1038 // greater-equal:
1039 // for (int k = r; k-- > q; i++) {
1040 // swap(data, k, i)
1041
1042 // Move the minimum of less-equal or less-than
1043 int move = Math.min(p - l, j - p);
1044 final int lower = j - (p - l);
1045 for (int k = l; move-- > 0; k++) {
1046 data[k] = data[--j];
1047 data[j] = v;
1048 }
1049 // Move the minimum of greater-equal or greater-than
1050 move = Math.min(r - q, q - i);
1051 upper[0] = i + (r - q);
1052 for (int k = r; move-- > 0; i++) {
1053 data[--k] = data[i];
1054 data[i] = v;
1055 }
1056
1057 // Special case: fixed signed zeros
1058 if (c != 0) {
1059 p = lower;
1060 while (c-- > 0) {
1061 data[p++] = -0.0;
1062 }
1063 while (p < upper[0]) {
1064 data[p++] = 0.0;
1065 }
1066 }
1067
1068 return lower;
1069 }
1070
1071 /**
1072 * Partition around the K<sup>th</sup> value in the array.
1073 *
1074 * <p>This method can be used for repeat calls to identify K<sup>th</sup> values in
1075 * the same array by caching locations of pivots (correctly sorted indices).
1076 *
1077 * <p>Note: Requires that the range contains no NaN values.
1078 *
1079 * <p>Uses a Bentley-McIlroy quicksort partition method by Sedgewick.
1080 *
1081 * <p>Returns the last known pivot location adjacent to K.
1082 * If {@code p <= k} the range [p, min{k+2, data.length}) is sorted.
1083 * If {@code p > k} then p is a pivot.
1084 *
1085 * @param data Data array to use to find out the K<sup>th</sup> value.
1086 * @param begin Lower bound (inclusive).
1087 * @param end Upper bound (exclusive).
1088 * @param k Index whose value in the array is of interest.
1089 * @return the bound index
1090 */
1091 private int partitionSBM(double[] data, int begin, int end, int k) {
1092 // Find the unsorted range containing k
1093 final int[] upper = {0};
1094 while (end - begin > minSelectSize) {
1095 // Select a value and partition data array around it
1096 final int from = partitionSBM(data, begin, end,
1097 pivotingStrategy.pivotIndex(data, begin, end - 1, k), upper);
1098 final int to = upper[0];
1099 if (k >= to) {
1100 // The element is in the right partition
1101 begin = to;
1102 } else if (k < from) {
1103 // The element is in the left partition
1104 end = from;
1105 } else {
1106 // The range contains the element we wanted
1107 return end;
1108 }
1109 }
1110 insertionSort(data, begin, end, begin != 0);
1111 fixSignedZeros(data, begin, end);
1112 // Either end == data.length and k+1 is sorted; or
1113 // end == pivot and k+1 is sorted
1114 return begin;
1115 }
1116
1117 /**
1118 * Partition the array such that indices {@code k} correspond to their correctly
1119 * sorted value in the equivalent fully sorted array. For all indices {@code k}
1120 * and any index {@code i}:
1121 *
1122 * <pre>{@code
1123 * data[i < k] <= data[k] <= data[k < i]
1124 * }</pre>
1125 *
1126 * <p>Uses a Bentley-McIlroy quicksort partition method.
1127 *
1128 * @param data Values.
1129 * @param k Indices.
1130 */
1131 void partitionBM(double[] data, int... k) {
1132 final int n = k.length;
1133 if (n < 1) {
1134 return;
1135 }
1136
1137 // Handle NaN
1138 final int length = sortNaN(data);
1139 if (length < 1) {
1140 return;
1141 }
1142
1143 if (n == 1) {
1144 partitionBM(data, 0, length, k[0]);
1145 return;
1146 }
1147 // Special case for partition around adjacent indices (for interpolation)
1148 if (n == 2 && k[0] + 1 == k[1]) {
1149 final int p = partitionBM(data, 0, length, k[0]);
1150 if (p > k[1]) {
1151 partitionMin(data, k[1], p);
1152 }
1153 return;
1154 }
1155
1156 // Multiple pivots
1157 final BitSet pivots = new BitSet(length);
1158
1159 for (int i = 0; i < n; i++) {
1160 final int kk = k[i];
1161 if (kk >= length || pivots.get(kk)) {
1162 // Already sorted
1163 continue;
1164 }
1165 int begin;
1166 int end;
1167 if (i == 0) {
1168 begin = 0;
1169 end = length;
1170 } else {
1171 // Start inclusive
1172 begin = pivots.previousSetBit(kk) + 1;
1173 end = pivots.nextSetBit(kk + 1);
1174 if (end < 0) {
1175 end = length;
1176 }
1177 }
1178 partitionBM(data, begin, end, pivots, kk);
1179 }
1180 }
1181
1182 /**
1183 * Partition around the K<sup>th</sup> value in the array.
1184 *
1185 * <p>This method can be used for repeat calls to identify K<sup>th</sup> values in
1186 * the same array by caching locations of pivots (correctly sorted indices).
1187 *
1188 * <p>Note: Requires that the range contains no NaN values.
1189 *
1190 * <p>Uses a Bentley-McIlroy quicksort partition method.
1191 *
1192 * @param data Data array to use to find out the K<sup>th</sup> value.
1193 * @param begin Lower bound (inclusive).
1194 * @param end Upper bound (exclusive).
1195 * @param pivots Cache of pivot points.
1196 * @param k Index whose value in the array is of interest.
1197 */
1198 private void partitionBM(double[] data, int begin, int end, BitSet pivots, int k) {
1199 // Find the unsorted range containing k
1200 final int[] upper = {0};
1201 while (end - begin > minSelectSize) {
1202 // Select a value and partition data array around it
1203 final int from = partitionBM(data, begin, end,
1204 pivotingStrategy.pivotIndex(data, begin, end - 1, k), upper);
1205 final int to = upper[0];
1206 setPivots(from, to, pivots);
1207 if (k >= to) {
1208 // The element is in the right partition
1209 begin = to;
1210 } else if (k < from) {
1211 // The element is in the left partition
1212 end = from;
1213 } else {
1214 // The range contains the element we wanted
1215 return;
1216 }
1217 }
1218 setPivots(begin, end, pivots);
1219 insertionSort(data, begin, end, begin != 0);
1220 fixSignedZeros(data, begin, end);
1221 }
1222
1223 /**
1224 * Partition an array slice around a pivot. Partitioning exchanges array elements such
1225 * that all elements smaller than pivot are before it and all elements larger than
1226 * pivot are after it.
1227 *
1228 * <p>Note: Requires that the range contains no NaN values.
1229 *
1230 * <p>Uses a Bentley-McIlroy quicksort partition method.
1231 *
1232 * @param data Data array.
1233 * @param begin Lower bound (inclusive).
1234 * @param end Upper bound (exclusive).
1235 * @param pivot Initial index of the pivot.
1236 * @param upper Upper bound (exclusive) of the pivot range.
1237 * @return Lower bound (inclusive) of the pivot range.
1238 */
1239 private static int partitionBM(double[] data, int begin, int end, int pivot, int[] upper) {
1240 // Partition method handling equal keys, Bentley-McIlroy quicksort.
1241 //
1242 // Adapted from program 7 in Bentley-McIlroy (1993)
1243 // Engineering a sort function
1244 // SOFTWARE—PRACTICE AND EXPERIENCE, VOL.23(11), 1249–1265
1245 //
1246 // 3-way partition of the data using a pivot value into
1247 // less-than, equal or greater-than.
1248 //
1249 // First partition data into 4 reqions by scanning the unknown region from
1250 // left (i) and right (j) and moving equal values to the ends:
1251 // i-> <-j end
1252 // l p | | q r|
1253 // | equal | less | unknown | greater | equal ||
1254 //
1255 // <-j end
1256 // l p i q r|
1257 // | equal | less | greater | equal ||
1258 //
1259 // Then the equal values are copied from the ends to the centre:
1260 // | less | equal | greater |
1261
1262 final int l = begin;
1263 final int r = end - 1;
1264
1265 int i = l;
1266 int j = r;
1267 int p = l;
1268 int q = r;
1269
1270 final double v = data[pivot];
1271
1272 // Special case: count signed zeros
1273 int c = 0;
1274 if (v == 0) {
1275 c = countSignedZeros(data, begin, end);
1276 }
1277
1278 for (;;) {
1279 while (i <= j && data[i] <= v) {
1280 if (data[i] == v) {
1281 //swap(data, i, p++)
1282 data[i] = data[p];
1283 data[p++] = v;
1284 }
1285 i++;
1286 }
1287 while (j >= i && data[j] >= v) {
1288 if (v == data[j]) {
1289 //swap(data, j, q--)
1290 data[j] = data[q];
1291 data[q--] = v;
1292 }
1293 j--;
1294 }
1295 if (i > j) {
1296 break;
1297 }
1298 swap(data, i++, j--);
1299 }
1300
1301 // Move equal regions to the centre.
1302 int s = Math.min(p - l, i - p);
1303 for (int k = l; s > 0; k++, s--) {
1304 //swap(data, k, i - s)
1305 data[k] = data[i - s];
1306 data[i - s] = v;
1307 }
1308 s = Math.min(q - j, r - q);
1309 for (int k = i; s > 0; k++, s--) {
1310 //swap(data, end - s, k)
1311 data[end - s] = data[k];
1312 data[k] = v;
1313 }
1314
1315 // Set output range
1316 i = i - p + l;
1317 j = j - q + end;
1318 upper[0] = j;
1319
1320 // Special case: fixed signed zeros
1321 if (c != 0) {
1322 p = i;
1323 while (c-- > 0) {
1324 data[p++] = -0.0;
1325 }
1326 while (p < j) {
1327 data[p++] = 0.0;
1328 }
1329 }
1330
1331 return i;
1332 }
1333
1334 /**
1335 * Partition around the K<sup>th</sup> value in the array.
1336 *
1337 * <p>This method can be used for repeat calls to identify K<sup>th</sup> values in
1338 * the same array by caching locations of pivots (correctly sorted indices).
1339 *
1340 * <p>Note: Requires that the range contains no NaN values.
1341 *
1342 * <p>Uses a Bentley-McIlroy quicksort partition method.
1343 *
1344 * <p>Returns the last known pivot location adjacent to K.
1345 * If {@code p <= k} the range [p, min{k+2, data.length}) is sorted.
1346 * If {@code p > k} then p is a pivot.
1347 *
1348 * @param data Data array to use to find out the K<sup>th</sup> value.
1349 * @param begin Lower bound (inclusive).
1350 * @param end Upper bound (exclusive).
1351 * @param k Index whose value in the array is of interest.
1352 * @return the bound index
1353 */
1354 private int partitionBM(double[] data, int begin, int end, int k) {
1355 // Find the unsorted range containing k
1356 final int[] upper = {0};
1357 while (end - begin > minSelectSize) {
1358 // Select a value and partition data array around it
1359 final int from = partitionBM(data, begin, end,
1360 pivotingStrategy.pivotIndex(data, begin, end - 1, k), upper);
1361 final int to = upper[0];
1362 if (k >= to) {
1363 // The element is in the right partition
1364 begin = to;
1365 } else if (k < from) {
1366 // The element is in the left partition
1367 end = from;
1368 } else {
1369 // The range contains the element we wanted
1370 return end;
1371 }
1372 }
1373 insertionSort(data, begin, end, begin != 0);
1374 fixSignedZeros(data, begin, end);
1375 // Either end == data.length and k+1 is sorted; or
1376 // end == pivot and k+1 is sorted
1377 return begin;
1378 }
1379
1380 /**
1381 * Partition the array such that indices {@code k} correspond to their correctly
1382 * sorted value in the equivalent fully sorted array. For all indices {@code k}
1383 * and any index {@code i}:
1384 *
1385 * <pre>{@code
1386 * data[i < k] <= data[k] <= data[k < i]
1387 * }</pre>
1388 *
1389 * <p>Uses a dual-pivot quicksort method by Vladimir Yaroslavskiy.
1390 *
1391 * @param data Values.
1392 * @param k Indices.
1393 */
1394 void partitionDP(double[] data, int... k) {
1395 final int n = k.length;
1396 if (n < 1) {
1397 return;
1398 }
1399
1400 // Handle NaN
1401 final int length = sortNaN(data);
1402 if (length < 1) {
1403 return;
1404 }
1405
1406 if (n == 1) {
1407 partitionDP(data, 0, length, (BitSet) null, k[0]);
1408 return;
1409 }
1410 // Special case for partition around adjacent indices (for interpolation)
1411 if (n == 2 && k[0] + 1 == k[1]) {
1412 final int p = partitionDP(data, 0, length, (BitSet) null, k[0]);
1413 if (p > k[1]) {
1414 partitionMin(data, k[1], p);
1415 }
1416 return;
1417 }
1418
1419 // Multiple pivots
1420 final BitSet pivots = new BitSet(length);
1421
1422 for (int i = 0; i < n; i++) {
1423 final int kk = k[i];
1424 if (kk >= length || pivots.get(kk)) {
1425 // Already sorted
1426 continue;
1427 }
1428 int begin;
1429 int end;
1430 if (i == 0) {
1431 begin = 0;
1432 end = length;
1433 } else {
1434 // Start inclusive
1435 begin = pivots.previousSetBit(kk) + 1;
1436 end = pivots.nextSetBit(kk + 1);
1437 if (end < 0) {
1438 end = length;
1439 }
1440 }
1441 partitionDP(data, begin, end, pivots, kk);
1442 }
1443 }
1444
1445 /**
1446 * Partition around the K<sup>th</sup> value in the array.
1447 *
1448 * <p>This method can be used for repeat calls to identify K<sup>th</sup> values in
1449 * the same array by caching locations of pivots (correctly sorted indices).
1450 *
1451 * <p>Note: Requires that the range contains no NaN values.
1452 *
1453 * <p>Uses a dual-pivot quicksort method by Vladimir Yaroslavskiy.
1454 *
1455 * <p>Returns the pivot location adjacent to K to signal if K+1 is sorted.
1456 * If {@code p <= k} the range [p, min{k+2, data.length}) is sorted.
1457 * If {@code p > k} then p is a pivot.
1458 *
1459 * @param data Data array to use to find out the K<sup>th</sup> value.
1460 * @param begin Lower bound (inclusive).
1461 * @param end Upper bound (exclusive).
1462 * @param pivots Cache of pivot points.
1463 * @param k Index whose value in the array is of interest.
1464 * @return the bound index
1465 */
1466 private int partitionDP(double[] data, int begin, int end, BitSet pivots, int k) {
1467 // Find the unsorted range containing k
1468 final int[] bounds = new int[4];
1469 int div = 3;
1470 while (end - begin > minSelectSize) {
1471 div = partitionDP(data, begin, end, bounds, div);
1472 final int k0 = bounds[0];
1473 final int k1 = bounds[1];
1474 final int k2 = bounds[2];
1475 final int k3 = bounds[3];
1476 // sorted in [k0, k1) and (k2, k3]
1477 if (pivots != null) {
1478 setPivots(k0, k1, pivots);
1479 setPivots(k2 + 1, k3 + 1, pivots);
1480 }
1481 if (k > k3) {
1482 // The element is in the right partition
1483 begin = k3 + 1;
1484 } else if (k < k0) {
1485 // The element is in the left partition
1486 end = k0;
1487 } else if (k >= k1 && k <= k2) {
1488 // Internal unsorted region
1489 begin = k1;
1490 end = k2 + 1;
1491 } else {
1492 // The sorted ranges contain the element we wanted.
1493 // Return a pivot (k0; k2+1) to signal if k+1 is sorted.
1494 if (k + 1 < k1) {
1495 return k0;
1496 }
1497 if (k + 1 < k3) {
1498 return k2 + 1;
1499 }
1500 return end;
1501 }
1502 }
1503 if (pivots != null) {
1504 setPivots(begin, end, pivots);
1505 }
1506 insertionSort(data, begin, end, begin != 0);
1507 fixSignedZeros(data, begin, end);
1508 // Either end == data.length and k+1 is sorted; or
1509 // end == pivot and k+1 is sorted
1510 return begin;
1511 }
1512
1513 /**
1514 * Partition an array slice around a pivot. Partitioning exchanges array elements such
1515 * that all elements smaller than pivot are before it and all elements larger than
1516 * pivot are after it.
1517 *
1518 * <p>Note: Requires that the range contains no NaN values. If the range contains
1519 * signed zeros and one is chosen as a pivot point the sort order of zeros is correct.
1520 *
1521 * <p>This method returns 4 points: the lower and upper pivots and bounds for
1522 * the internal range of unsorted values.
1523 * <ul>
1524 * <li>k0: lower pivot point: {@code a[k] < a[k0]} for {@code k < k0}.
1525 * <li>k1: the start (inclusive) of the unsorted range: {@code k0 < k1}.
1526 * <li>k2: the end (inclusive) of the unsorted range: {@code k2 <= k3}.
1527 * <li>k3: upper pivot point: {@code a[k3] < a[k]} for {@code k3 < k}.
1528 * </ul>
1529 *
1530 * <p>Bounds are set so {@code [k0, k1)} and {@code (k2, k3]} are fully sorted.
1531 * When the range {@code [k0, k3]} contains fully sorted elements
1532 * the result is set to {@code k1 = k3+1} and {@code k2 = k3}.
1533 *
1534 * <p>Uses a dual-pivot quicksort method by Vladimir Yaroslavskiy.
1535 *
1536 * @param a Data array.
1537 * @param left Lower bound (inclusive).
1538 * @param end Upper bound (exclusive).
1539 * @param bounds Points [k0, k1, k2, k3].
1540 * @param div Divisor for the range to pick medians.
1541 * @return div
1542 */
1543 private int partitionDP(double[] a, int left, int end, int[] bounds, int div) {
1544 // Dual-pivot quicksort method by Vladimir Yaroslavskiy.
1545 //
1546 // Partition data using pivots P1 and P2 into less-than, greater-than or between.
1547 // Pivot values P1 & P2 are placed at the end. If P1 < P2, P2 acts as a sentinal.
1548 // k traverses the unknown region ??? and values moved if less (l) or greater (g):
1549 //
1550 // left l k g right
1551 // |P1| <P1 | P1<= & <= P2 | ??? | >P2 |P2|
1552 //
1553 // At the end pivots are swapped back to behind the l and g pointers.
1554 //
1555 // | <P1 |P1| P1<= & <= P2 |P2| >P2 |
1556 //
1557 // Adapted from Yaroslavskiy
1558 // http://codeblab.com/wp-content/uploads/2009/09/DualPivotQuicksort.pdf
1559 //
1560 // Modified to allow partial sorting (partitioning):
1561 // - Ignore insertion sort for tiny array (handled by calling code)
1562 // - Ignore recursive calls for a full sort (handled by calling code)
1563 // - Check for equal elements if a pivot is a signed zero
1564 // - Fix signed zeros within the region between pivots
1565 // - Change to fast-forward over initial ascending / descending runs
1566 // - Change to a single-pivot partition method if the pivots are equal
1567
1568 final int right = end - 1;
1569 final int len = right - left;
1570
1571 // Find pivots:
1572
1573 // Original method: Guess medians using 1/3 and 2/3 of range
1574 final int third = len / div;
1575 int m1 = left + third;
1576 int m2 = right - third;
1577 if (m1 <= left) {
1578 m1 = left + 1;
1579 }
1580 if (m2 >= right) {
1581 m2 = right - 1;
1582 }
1583 if (a[m1] < a[m2]) {
1584 swap(a, m1, left);
1585 swap(a, m2, right);
1586 } else {
1587 swap(a, m1, right);
1588 swap(a, m2, left);
1589 }
1590 // pivots
1591 final double pivot1 = a[left];
1592 final double pivot2 = a[right];
1593
1594 // Single pivot sort
1595 if (pivot1 == pivot2) {
1596 final int lower = partitionSBM(a, left, end, m1, bounds);
1597 final int upper = bounds[0];
1598 // Set dual pivot range
1599 bounds[0] = lower;
1600 bounds[3] = upper - 1;
1601 // Fully sorted internally
1602 bounds[1] = upper;
1603 bounds[2] = upper - 1;
1604 return div;
1605 }
1606
1607 // Special case: Handle signed zeros
1608 int c = 0;
1609 if (pivot1 == 0 || pivot2 == 0) {
1610 c = countSignedZeros(a, left, end);
1611 }
1612
1613 // pointers
1614 int less = left + 1;
1615 int great = right - 1;
1616
1617 // Fast-forward ascending / descending runs to reduce swaps
1618 while (a[less] < pivot1) {
1619 less++;
1620 }
1621 while (a[great] > pivot2) {
1622 great--;
1623 }
1624
1625 // sorting
1626 SORTING:
1627 for (int k = less; k <= great; k++) {
1628 final double v = a[k];
1629 if (v < pivot1) {
1630 //swap(a, k, less++)
1631 a[k] = a[less];
1632 a[less] = v;
1633 less++;
1634 } else if (v > pivot2) {
1635 // Original
1636 //while (k < great && a[great] > pivot2) {
1637 // great--;
1638 //}
1639 while (a[great] > pivot2) {
1640 if (great-- == k) {
1641 // Done
1642 break SORTING;
1643 }
1644 }
1645 // swap(a, k, great--)
1646 // if (a[k] < pivot1)
1647 // swap(a, k, less++)
1648 final double w = a[great];
1649 a[great] = v;
1650 great--;
1651 // a[k] = w
1652 if (w < pivot1) {
1653 a[k] = a[less];
1654 a[less] = w;
1655 less++;
1656 } else {
1657 a[k] = w;
1658 }
1659 }
1660 }
1661 // swaps
1662 final int dist = great - less;
1663 // Original paper: If middle partition (dist) is less than 13
1664 // then increase 'div' by 1. This means that the two outer partitions
1665 // contained most of the data and choosing medians should take
1666 // values closer to the edge. The middle will be sorted by quicksort.
1667 // 13 = 27 / 2 where 27 is the threshold for quicksort.
1668 if (dist < (minSelectSize >>> 1)) {
1669 div++;
1670 }
1671 //swap(a, less - 1, left)
1672 //swap(a, great + 1, right)
1673 a[left] = a[less - 1];
1674 a[less - 1] = pivot1;
1675 a[right] = a[great + 1];
1676 a[great + 1] = pivot2;
1677
1678 // unsorted in [less, great]
1679
1680 // Set the pivots
1681 bounds[0] = less - 1;
1682 bounds[3] = great + 1;
1683 //partitionDP(a, left, less - 2, div)
1684 //partitionDP(a, great + 2, right, div)
1685
1686 // equal elements
1687 // Original paper: If middle partition (dist) is bigger
1688 // than (length - 13) then check for equal elements, i.e.
1689 // if the middle was very large there may be many repeated elements.
1690 // 13 = 27 / 2 where 27 is the threshold for quicksort.
1691 // We always do this if the pivots are signed zeros.
1692 if ((dist > len - (minSelectSize >>> 1) || c != 0) && pivot1 != pivot2) {
1693 // Fast-forward to reduce swaps
1694 while (a[less] == pivot1) {
1695 less++;
1696 }
1697 while (a[great] == pivot2) {
1698 great--;
1699 }
1700 // This copies the logic in the sorting loop using == comparisons
1701 EQUAL:
1702 for (int k = less; k <= great; k++) {
1703 final double v = a[k];
1704 if (v == pivot1) {
1705 //swap(a, k, less++)
1706 a[k] = a[less];
1707 a[less] = v;
1708 less++;
1709 } else if (v == pivot2) {
1710 while (a[great] == pivot2) {
1711 if (great-- == k) {
1712 // Done
1713 break EQUAL;
1714 }
1715 }
1716 final double w = a[great];
1717 a[great] = v;
1718 great--;
1719 if (w == pivot1) {
1720 a[k] = a[less];
1721 a[less] = w;
1722 less++;
1723 } else {
1724 a[k] = w;
1725 }
1726 }
1727 }
1728 }
1729 // unsorted in [less, great]
1730 if (pivot1 < pivot2 && less < great) {
1731 //partitionDP(a, less, great, div)
1732 bounds[1] = less;
1733 bounds[2] = great;
1734 } else {
1735 // Fully sorted
1736 bounds[1] = bounds[3] + 1;
1737 bounds[2] = bounds[3];
1738 }
1739
1740 // Fix signed zeros
1741 if (c != 0) {
1742 int i;
1743 if (pivot1 == 0) {
1744 i = bounds[0];
1745 while (c-- > 0) {
1746 a[i++] = -0.0;
1747 }
1748 while (i < end && a[i] == 0) {
1749 a[i++] = 0.0;
1750 }
1751 } else {
1752 i = bounds[3];
1753 while (a[i] == 0) {
1754 a[i--] = 0.0;
1755 if (i == left) {
1756 break;
1757 }
1758 }
1759 while (c-- > 0) {
1760 a[++i] = -0.0;
1761 }
1762 }
1763 }
1764
1765 return div;
1766 }
1767
1768 /**
1769 * Partition the array such that indices {@code k} correspond to their correctly
1770 * sorted value in the equivalent fully sorted array. For all indices {@code k}
1771 * and any index {@code i}:
1772 *
1773 * <pre>{@code
1774 * data[i < k] <= data[k] <= data[k < i]
1775 * }</pre>
1776 *
1777 * <p>Uses a dual-pivot quicksort method by Vladimir Yaroslavskiy.
1778 *
1779 * @param data Values.
1780 * @param k Indices.
1781 */
1782 void partitionDP5(double[] data, int... k) {
1783 final int n = k.length;
1784 if (n < 1) {
1785 return;
1786 }
1787
1788 // Handle NaN
1789 final int length = sortNaN(data);
1790 if (length < 1) {
1791 return;
1792 }
1793
1794 if (n == 1) {
1795 partitionDP5(data, 0, length, (BitSet) null, k[0]);
1796 return;
1797 }
1798 // Special case for partition around adjacent indices (for interpolation)
1799 if (n == 2 && k[0] + 1 == k[1]) {
1800 final int p = partitionDP5(data, 0, length, (BitSet) null, k[0]);
1801 if (p > k[1]) {
1802 partitionMin(data, k[1], p);
1803 }
1804 return;
1805 }
1806
1807 // Multiple pivots
1808 final BitSet pivots = new BitSet(length);
1809
1810 for (int i = 0; i < n; i++) {
1811 final int kk = k[i];
1812 if (kk >= length || pivots.get(kk)) {
1813 // Already sorted
1814 continue;
1815 }
1816 int begin;
1817 int end;
1818 if (i == 0) {
1819 begin = 0;
1820 end = length;
1821 } else {
1822 // Start inclusive
1823 begin = pivots.previousSetBit(kk) + 1;
1824 end = pivots.nextSetBit(kk + 1);
1825 if (end < 0) {
1826 end = length;
1827 }
1828 }
1829 partitionDP5(data, begin, end, pivots, kk);
1830 }
1831 }
1832
1833 /**
1834 * Partition around the K<sup>th</sup> value in the array.
1835 *
1836 * <p>This method can be used for repeat calls to identify K<sup>th</sup> values in
1837 * the same array by caching locations of pivots (correctly sorted indices).
1838 *
1839 * <p>Note: Requires that the range contains no NaN values.
1840 *
1841 * <p>Uses a dual-pivot quicksort method by Vladimir Yaroslavskiy.
1842 *
1843 * <p>Returns the pivot location adjacent to K to signal if K+1 is sorted.
1844 * If {@code p <= k} the range [p, min{k+2, data.length}) is sorted.
1845 * If {@code p > k} then p is a pivot.
1846 *
1847 * @param data Data array to use to find out the K<sup>th</sup> value.
1848 * @param begin Lower bound (inclusive).
1849 * @param end Upper bound (exclusive).
1850 * @param pivots Cache of pivot points.
1851 * @param k Index whose value in the array is of interest.
1852 * @return the bound index
1853 */
1854 private int partitionDP5(double[] data, int begin, int end, BitSet pivots, int k) {
1855 // Find the unsorted range containing k
1856 final int[] bounds = new int[4];
1857 while (end - begin > minSelectSize) {
1858 partitionDP5(data, begin, end, bounds);
1859 final int k0 = bounds[0];
1860 final int k1 = bounds[1];
1861 final int k2 = bounds[2];
1862 final int k3 = bounds[3];
1863 // sorted in [k0, k1) and (k2, k3]
1864 if (pivots != null) {
1865 setPivots(k0, k1, pivots);
1866 setPivots(k2 + 1, k3 + 1, pivots);
1867 }
1868 if (k > k3) {
1869 // The element is in the right partition
1870 begin = k3 + 1;
1871 } else if (k < k0) {
1872 // The element is in the left partition
1873 end = k0;
1874 } else if (k >= k1 && k <= k2) {
1875 // Internal unsorted region
1876 begin = k1;
1877 end = k2 + 1;
1878 } else {
1879 // The sorted ranges contain the element we wanted.
1880 // Return a pivot (k0; k2+1) to signal if k+1 is sorted.
1881 if (k + 1 < k1) {
1882 return k0;
1883 }
1884 if (k + 1 < k3) {
1885 return k2 + 1;
1886 }
1887 return end;
1888 }
1889 }
1890 if (pivots != null) {
1891 setPivots(begin, end, pivots);
1892 }
1893 insertionSort(data, begin, end, begin != 0);
1894 fixSignedZeros(data, begin, end);
1895 // Either end == data.length and k+1 is sorted; or
1896 // end == pivot and k+1 is sorted
1897 return begin;
1898 }
1899
1900 /**
1901 * Partition an array slice around a pivot. Partitioning exchanges array elements such
1902 * that all elements smaller than pivot are before it and all elements larger than
1903 * pivot are after it.
1904 *
1905 * <p>Note: Requires that the range contains no NaN values. If the range contains
1906 * signed zeros and one is chosen as a pivot point the sort order of zeros is correct.
1907 *
1908 * <p>This method returns 4 points: the lower and upper pivots and bounds for
1909 * the internal range of unsorted values.
1910 * <ul>
1911 * <li>k0: lower pivot point: {@code a[k] < a[k0]} for {@code k < k0}.
1912 * <li>k1: the start (inclusive) of the unsorted range: {@code k0 < k1}.
1913 * <li>k2: the end (inclusive) of the unsorted range: {@code k2 <= k3}.
1914 * <li>k3: upper pivot point: {@code a[k3] < a[k]} for {@code k3 < k}.
1915 * </ul>
1916 *
1917 * <p>Bounds are set so {@code [k0, k1)} and {@code (k2, k3]} are fully sorted.
1918 * When the range {@code [k0, k3]} contains fully sorted elements
1919 * the result is set to {@code k1 = k3+1} and {@code k2 = k3}.
1920 *
1921 * <p>Uses a dual-pivot quicksort method by Vladimir Yaroslavskiy.
1922 *
1923 * @param a Data array.
1924 * @param left Lower bound (inclusive).
1925 * @param end Upper bound (exclusive).
1926 * @param bounds Points [k0, k1, k2, k3].
1927 */
1928 private static void partitionDP5(double[] a, int left, int end, int[] bounds) {
1929 // Dual-pivot quicksort method by Vladimir Yaroslavskiy.
1930 //
1931 // Adapted from:
1932 //
1933 // http://codeblab.com/wp-content/uploads/2009/09/DualPivotQuicksort.pdf
1934 //
1935 // Modified to allow partial sorting (partitioning):
1936 // - Choose a pivot using 5 sorted points from the range.
1937 // - Ignore insertion sort for tiny array (handled by calling code)
1938 // - Ignore recursive calls for a full sort (handled by calling code)
1939 // - Check for equal elements if a pivot is a signed zero
1940 // - Fix signed zeros within the region between pivots
1941 // - Change to fast-forward over initial ascending / descending runs
1942 // - Change to a single-pivot partition method if the pivots are equal
1943
1944 final int right = end - 1;
1945 final int len = right - left;
1946
1947 // Find pivots:
1948
1949 // Original method: Guess medians using 1/3 and 2/3 of range.
1950 // Here we sort 5 points and choose 2 and 4 as the pivots: 1/6, 1/3, 1/2, 2/3, 5/6
1951 // 1/6 ~ 1/8 + 1/32. Ensure the value is above zero to choose different points!
1952 // This is safe is len >= 4.
1953 final int sixth = 1 + (len >>> 3) + (len >>> 5);
1954 final int p3 = left + (len >>> 1);
1955 final int p2 = p3 - sixth;
1956 final int p1 = p2 - sixth;
1957 final int p4 = p3 + sixth;
1958 final int p5 = p4 + sixth;
1959 Sorting.sort5(a, p1, p2, p3, p4, p5);
1960
1961 // For testing
1962 //p2 = DualPivotingStrategy.SORT_5.pivotIndex(a, left, end - 1, bounds);
1963 //p4 = bounds[0];
1964
1965 final double pivot1 = a[p2];
1966 final double pivot2 = a[p4];
1967
1968 // Add property to control this switch so we can benchmark not using it.
1969
1970 if (pivot1 == pivot2) {
1971 // pivots == median !
1972 // Switch to a single pivot sort around the estimated median
1973 final int lower = partitionSBM(a, left, end, p3, bounds);
1974 final int upper = bounds[0];
1975 // Set dual pivot range
1976 bounds[0] = lower;
1977 bounds[3] = upper - 1;
1978 // No unsorted internal region
1979 bounds[1] = upper;
1980 bounds[2] = upper - 1;
1981 return;
1982 }
1983
1984 // Special case: Handle signed zeros
1985 int c = 0;
1986 if (pivot1 == 0 || pivot2 == 0) {
1987 c = countSignedZeros(a, left, end);
1988 }
1989
1990 // Move ends to the pivot locations.
1991 // After sorting the final pivot locations are overwritten.
1992 a[p2] = a[left];
1993 a[p4] = a[right];
1994 // It is assumed
1995 //a[left] = pivot1
1996 //a[right] = pivot2
1997
1998 // pointers
1999 int less = left + 1;
2000 int great = right - 1;
2001
2002 // Fast-forward ascending / descending runs to reduce swaps
2003 while (a[less] < pivot1) {
2004 less++;
2005 }
2006 while (a[great] > pivot2) {
2007 great--;
2008 }
2009
2010 // sorting
2011 SORTING:
2012 for (int k = less; k <= great; k++) {
2013 final double v = a[k];
2014 if (v < pivot1) {
2015 //swap(a, k, less++)
2016 a[k] = a[less];
2017 a[less] = v;
2018 less++;
2019 } else if (v > pivot2) {
2020 // Original
2021 //while (k < great && a[great] > pivot2) {
2022 // great--;
2023 //}
2024 while (a[great] > pivot2) {
2025 if (great-- == k) {
2026 // Done
2027 break SORTING;
2028 }
2029 }
2030 // swap(a, k, great--)
2031 // if (a[k] < pivot1)
2032 // swap(a, k, less++)
2033 final double w = a[great];
2034 a[great] = v;
2035 great--;
2036 // a[k] = w
2037 if (w < pivot1) {
2038 a[k] = a[less];
2039 a[less] = w;
2040 less++;
2041 } else {
2042 a[k] = w;
2043 }
2044 }
2045 }
2046 // swaps
2047 //swap(a, less - 1, left)
2048 //swap(a, great + 1, right)
2049 a[left] = a[less - 1];
2050 a[less - 1] = pivot1;
2051 a[right] = a[great + 1];
2052 a[great + 1] = pivot2;
2053
2054 // unsorted in [less, great]
2055
2056 // Set the pivots
2057 bounds[0] = less - 1;
2058 bounds[3] = great + 1;
2059 //partitionDP5(a, left, less - 2)
2060 //partitionDP5(a, great + 2, right)
2061
2062 // equal elements
2063 // Original paper: If middle partition (dist) is bigger
2064 // than (length - 13) then check for equal elements, i.e.
2065 // if the middle was very large there may be many repeated elements.
2066 // 13 = 27 / 2 where 27 is the threshold for quicksort.
2067
2068 // Look for equal elements if the centre is more than 2/3 the length
2069 // We always do this if the pivots are signed zeros.
2070 if ((less < p1 && great > p5 || c != 0) && pivot1 != pivot2) {
2071
2072 // Fast-forward to reduce swaps
2073 while (a[less] == pivot1) {
2074 less++;
2075 }
2076 while (a[great] == pivot2) {
2077 great--;
2078 }
2079
2080 // This copies the logic in the sorting loop using == comparisons
2081 EQUAL:
2082 for (int k = less; k <= great; k++) {
2083 final double v = a[k];
2084 if (v == pivot1) {
2085 //swap(a, k, less++)
2086 a[k] = a[less];
2087 a[less] = v;
2088 less++;
2089 } else if (v == pivot2) {
2090 while (a[great] == pivot2) {
2091 if (great-- == k) {
2092 // Done
2093 break EQUAL;
2094 }
2095 }
2096 final double w = a[great];
2097 a[great] = v;
2098 great--;
2099 if (w == pivot1) {
2100 a[k] = a[less];
2101 a[less] = w;
2102 less++;
2103 } else {
2104 a[k] = w;
2105 }
2106 }
2107 }
2108 }
2109 // unsorted in [less, great]
2110 if (pivot1 < pivot2 && less < great) {
2111 //partitionDP5(a, less, great)
2112 bounds[1] = less;
2113 bounds[2] = great;
2114 } else {
2115 // Fully sorted
2116 bounds[1] = bounds[3] + 1;
2117 bounds[2] = bounds[3];
2118 }
2119
2120 // Fix signed zeros
2121 if (c != 0) {
2122 int i;
2123 if (pivot1 == 0) {
2124 i = bounds[0];
2125 while (c-- > 0) {
2126 a[i++] = -0.0;
2127 }
2128 while (i < end && a[i] == 0) {
2129 a[i++] = 0.0;
2130 }
2131 } else {
2132 i = bounds[3];
2133 while (a[i] == 0) {
2134 a[i--] = 0.0;
2135 if (i == left) {
2136 break;
2137 }
2138 }
2139 while (c-- > 0) {
2140 a[++i] = -0.0;
2141 }
2142 }
2143 }
2144 }
2145
2146 /**
2147 * Swaps the two specified elements in the array.
2148 *
2149 * @param array Array.
2150 * @param i First index.
2151 * @param j Second index.
2152 */
2153 private static void swap(double[] array, int i, int j) {
2154 final double tmp = array[i];
2155 array[i] = array[j];
2156 array[j] = tmp;
2157 }
2158
2159 /**
2160 * Sort the data.
2161 *
2162 * <p>Uses a single-pivot quicksort partition method.
2163 *
2164 * @param data Values.
2165 */
2166 void sortSP(double[] data) {
2167 sortSP(data, 0, data.length);
2168 }
2169
2170 /**
2171 * Sort the data.
2172 *
2173 * @param data Values.
2174 * @param begin Lower bound (inclusive).
2175 * @param end Upper bound (exclusive).
2176 */
2177 private void sortSP(double[] data, int begin, int end) {
2178 if (end - begin <= 1) {
2179 return;
2180 }
2181 final int i = partitionSP(data, begin, end,
2182 pivotingStrategy.pivotIndex(data, begin, end - 1, begin));
2183 sortSP(data, begin, i);
2184 sortSP(data, i + 1, end);
2185 }
2186
2187 /**
2188 * Sort the data.
2189 *
2190 * <p>Uses a Bentley-McIlroy quicksort partition method.
2191 *
2192 * @param data Values.
2193 */
2194 void sortSBM(double[] data) {
2195 sortSBM(data, 0, sortNaN(data));
2196 }
2197
2198 /**
2199 * Sort the data. Requires no NaN values in the range.
2200 *
2201 * @param data Values.
2202 * @param begin Lower bound (inclusive).
2203 * @param end Upper bound (exclusive).
2204 */
2205 private void sortSBM(double[] data, int begin, int end) {
2206 if (end - begin <= minSelectSize) {
2207 insertionSort(data, begin, end, begin != 0);
2208 if (begin < end) {
2209 fixSignedZeros(data, begin, end);
2210 }
2211 return;
2212 }
2213 final int[] to = {0};
2214 final int from = partitionSBM(data, begin, end,
2215 pivotingStrategy.pivotIndex(data, begin, end - 1, begin), to);
2216 sortSBM(data, begin, from);
2217 sortSBM(data, to[0], end);
2218 }
2219
2220 /**
2221 * Sort the data.
2222 *
2223 * <p>Uses a Bentley-McIlroy quicksort partition method.
2224 *
2225 * @param data Values.
2226 */
2227 void sortBM(double[] data) {
2228 sortBM(data, 0, sortNaN(data));
2229 }
2230
2231 /**
2232 * Sort the data. Requires no NaN values in the range.
2233 *
2234 * @param data Values.
2235 * @param begin Lower bound (inclusive).
2236 * @param end Upper bound (exclusive).
2237 */
2238 private void sortBM(double[] data, int begin, int end) {
2239 if (end - begin <= minSelectSize) {
2240 insertionSort(data, begin, end, begin != 0);
2241 if (begin < end) {
2242 fixSignedZeros(data, begin, end);
2243 }
2244 return;
2245 }
2246 final int[] to = {0};
2247 final int from = partitionBM(data, begin, end,
2248 pivotingStrategy.pivotIndex(data, begin, end - 1, begin), to);
2249 sortBM(data, begin, from);
2250 sortBM(data, to[0], end);
2251 }
2252
2253 /**
2254 * Sort the data.
2255 *
2256 * <p>Uses a dual-pivot quicksort method by Vladimir Yaroslavskiy.
2257 *
2258 * @param data Values.
2259 */
2260 void sortDP(double[] data) {
2261 sortDP(data, 0, sortNaN(data), 3);
2262 }
2263
2264 /**
2265 * Sort the data. Requires no NaN values in the range.
2266 *
2267 * @param data Values.
2268 * @param begin Lower bound (inclusive).
2269 * @param end Upper bound (exclusive).
2270 * @param div Divisor for the range to pick medians.
2271 */
2272 private void sortDP(double[] data, int begin, int end, int div) {
2273 if (end - begin <= minSelectSize) {
2274 insertionSort(data, begin, end, begin != 0);
2275 if (begin < end) {
2276 fixSignedZeros(data, begin, end);
2277 }
2278 return;
2279 }
2280 final int[] bounds = new int[4];
2281 div = partitionDP(data, begin, end, bounds, div);
2282 final int k0 = bounds[0];
2283 final int k1 = bounds[1];
2284 final int k2 = bounds[2];
2285 final int k3 = bounds[3];
2286 sortDP(data, begin, k0, div);
2287 sortDP(data, k3 + 1, end, div);
2288 sortDP(data, k1, k2 + 1, div);
2289 }
2290
2291 /**
2292 * Sort the data.
2293 *
2294 * <p>Uses a dual-pivot quicksort method by Vladimir Yaroslavskiy optimised
2295 * to choose the pivots using 5 sorted points.
2296 *
2297 * @param data Values.
2298 */
2299 void sortDP5(double[] data) {
2300 sortDP5(data, 0, sortNaN(data));
2301 }
2302
2303 /**
2304 * Sort the data. Requires no NaN values in the range.
2305 *
2306 * @param data Values.
2307 * @param begin Lower bound (inclusive).
2308 * @param end Upper bound (exclusive).
2309 */
2310 private void sortDP5(double[] data, int begin, int end) {
2311 if (end - begin <= minSelectSize) {
2312 insertionSort(data, begin, end, begin != 0);
2313 if (begin < end) {
2314 fixSignedZeros(data, begin, end);
2315 }
2316 return;
2317 }
2318 final int[] bounds = new int[4];
2319 partitionDP5(data, begin, end, bounds);
2320 final int k0 = bounds[0];
2321 final int k1 = bounds[1];
2322 final int k2 = bounds[2];
2323 final int k3 = bounds[3];
2324 sortDP5(data, begin, k0);
2325 sortDP5(data, k3 + 1, end);
2326 sortDP5(data, k1, k2 + 1);
2327 }
2328
2329 /**
2330 * Creates the pivots heap for a data array of the specified {@code length}.
2331 * If the array is too small to use the pivots heap then an empty array is returned.
2332 *
2333 * @param length Length.
2334 * @return the pivots heap
2335 */
2336 static int[] createPivotsHeap(int length) {
2337 if (length <= MIN_SELECT_SIZE) {
2338 return NO_PIVOTS;
2339 }
2340 // Size should be x^2 - 1, where x is the layers in the heap.
2341 // Do not create more pivots than the array length. When partitions are small
2342 // the pivots are no longer used so this does not have to contain all indices.
2343 // Default size in Commons Math Percentile class was 1023 (10 layers).
2344 final int n = nextPow2(length >>> 1);
2345 final int[] pivotsHeap = new int[Math.min(n, 1 << 10) - 1];
2346 Arrays.fill(pivotsHeap, -1);
2347 return pivotsHeap;
2348 }
2349
2350 /**
2351 * Returns the closest power-of-two number greater than or equal to value.
2352 *
2353 * <p>Warning: This will return {@link Integer#MIN_VALUE} for any value above
2354 * {@code 1 << 30}. This is the next power of 2 as an unsigned integer.
2355 *
2356 * @param value the value (must be positive)
2357 * @return the closest power-of-two number greater than or equal to value
2358 */
2359 private static int nextPow2(int value) {
2360 // shift by -x is equal to shift by (32 - x) as only the low 5-bits are used.
2361 return 1 << -Integer.numberOfLeadingZeros(value - 1);
2362 }
2363 }