View Javadoc
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 }