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.Objects;
22  import java.util.SplittableRandom;
23  import java.util.function.IntConsumer;
24  import java.util.function.IntUnaryOperator;
25  import java.util.function.Supplier;
26  import org.apache.commons.rng.UniformRandomProvider;
27  import org.apache.commons.rng.simple.RandomSource;
28  
29  /**
30   * Partition array data.
31   *
32   * <p>Arranges elements such that indices {@code k} correspond to their correctly
33   * sorted value in the equivalent fully sorted array. For all indices {@code k}
34   * and any index {@code i}:
35   *
36   * <pre>{@code
37   * data[i < k] <= data[k] <= data[k < i]
38   * }</pre>
39   *
40   * <p>Examples:
41   *
42   * <pre>
43   * data    [0, 1, 2, 1, 2, 5, 2, 3, 3, 6, 7, 7, 7, 7]
44   *
45   *
46   * k=4   : [0, 1, 2, 1], [2], [5, 2, 3, 3, 6, 7, 7, 7, 7]
47   * k=4,8 : [0, 1, 2, 1], [2], [3, 3, 2], [5], [6, 7, 7, 7, 7]
48   * </pre>
49   *
50   * <p>Note: Unless otherwise stated, methods in this class require that the floating-point data
51   * contains no NaN values; ordering does not respect the order of signed zeros imposed by
52   * {@link Double#compare(double, double)}.
53   *
54   * <p>References
55   *
56   * <p>Quickselect is introduced in Hoare [1]. This selects an element {@code k} from {@code n}
57   * using repeat division of the data around a partition element, recursing into the
58   * partition that contains {@code k}.
59   *
60   * <p>Introselect is introduced in Musser [2]. This detects excess recursion in quickselect
61   * and reverts to heapselect to achieve an improved worst case bound on selection.
62   *
63   * <p>Use of dual-pivot quickselect is analysed in Wild et al [3] and shown to require
64   * marginally more comparisons than single-pivot quickselect on a uniformly chosen order
65   * statistic {@code k} and extremal order statistic (see table 1, page 19). This analysis
66   * is reflected in the current implementation where dual-pivot quickselect is marginally
67   * slower when {@code k} is close to the end of the data. However the dual-pivot quickselect
68   * outperforms single-pivot quickselect when using multiple {@code k}; often significantly
69   * when {@code k} or {@code n} are large.
70   *
71   * <p>Use of sampling to identify a pivot that places {@code k} in the smaller partition is
72   * performed in the SELECT algorithm of Floyd and Rivest [4]. The original algorithm partitions
73   * on a single pivot. This was extended by Kiwiel to partition using two pivots either side
74   * of {@code k} with high probability [5].
75   *
76   * <p>Confidence bounds for the number of iterations to reduce a partition length by 2<sup>-x</sup>
77   * are provided in Valois [6].
78   *
79   * <p>A worst-case linear time algorithm PICK is described in Blum <i>et al</i> [7]. This uses the
80   * median-of-medians as a partition element for selection which ensures a minimum fraction of the
81   * elements are eliminated per iteration. This was extended to use an asymmetric pivot choice
82   * with efficient reuse of the medians sample in the QuickselectAdpative algorithm of
83   * Alexandrescu [8].
84   *
85   * <ol>
86   * <li>
87   * Hoare (1961)
88   * Algorithm 65: Find
89   * <a href="https://doi.org/10.1145%2F366622.366647">Comm. ACM. 4 (7): 321–322</a>
90   * <li>
91   * Musser (1999)
92   * Introspective Sorting and Selection Algorithms
93   * <a href="https://doi.org/10.1002/(SICI)1097-024X(199708)27:8%3C983::AID-SPE117%3E3.0.CO;2-%23">
94   * Software: Practice and Experience 27, 983-993.</a>
95   * <li>
96   * Wild, Nebel and Mahmoud (2013)
97   * Analysis of Quickselect under Yaroslavskiy's Dual-Pivoting Algorithm
98   * <a href="https://doi.org/10.48550/arXiv.1306.3819">arXiv:1306.3819</a>
99   * <li>Floyd and Rivest (1975)
100  * Algorithm 489: The Algorithm SELECT - for Finding the ith Smallest of n elements.
101  * Comm. ACM. 18 (3): 173.
102  * <li>Kiwiel (2005)
103  * On Floyd and Rivest's SELECT algorithm.
104  * <a href="https://doi.org/10.1016/j.tcs.2005.06.032">
105  * Theoretical Computer Science 347, 214-238</a>.
106  * <li>Valois (2000)
107  * Introspective sorting and selection revisited
108  * <a href="https://doi.org/10.1002/(SICI)1097-024X(200005)30:6%3C617::AID-SPE311%3E3.0.CO;2-A">
109  * Software: Practice and Experience 30, 617-638.</a>
110  * <li>Blum, Floyd, Pratt, Rivest, and Tarjan (1973)
111  * Time bounds for selection.
112  * <a href="https://doi.org/10.1016%2FS0022-0000%2873%2980033-9">
113  * Journal of Computer and System Sciences. 7 (4): 448–461</a>.
114  * <li>Alexandrescu (2016)
115  * Fast Deterministic Selection
116  * <a href="https://arxiv.org/abs/1606.00484">arXiv:1606.00484</a>.
117  * <li><a href="https://en.wikipedia.org/wiki/Quickselect">Quickselect (Wikipedia)</a>
118  * <li><a href="https://en.wikipedia.org/wiki/Introsort">Introsort (Wikipedia)</a>
119  * <li><a href="https://en.wikipedia.org/wiki/Introselect">Introselect (Wikipedia)</a>
120  * <li><a href="https://en.wikipedia.org/wiki/Median_of_medians">Median of medians (Wikipedia)</a>
121  * </ol>
122  *
123  * @since 1.2
124  */
125 final class Partition {
126     // This class contains implementations for use in benchmarking.
127 
128     /** Default pivoting strategy. Note: Using the dynamic strategy avoids excess recursion
129      * on the Bentley and McIlroy test data vs the MEDIAN_OF_3 strategy. */
130     static final PivotingStrategy PIVOTING_STRATEGY = PivotingStrategy.DYNAMIC;
131     /**
132      * Default pivoting strategy. Choosing from 5 points is unbiased on random data and
133      * has a lower standard deviation around the thirds than choosing 2 points
134      * (Yaroslavskiy's original method, see {@link DualPivotingStrategy#MEDIANS}). It
135      * performs well across various test data.
136      *
137      * <p>There are 3 variants using spacings of approximately 1/6, 1/7, and 1/8 computed
138      * using shifts to create 0.1719, 0.1406, and 0.125; with middle thirds on large
139      * lengths of 0.342, 0.28 and 0.25. The spacing using 1/7 is marginally faster when
140      * performing a full sort than the others; thus favouring a smaller middle third, but
141      * not too small, appears to be most performant.
142      */
143     static final DualPivotingStrategy DUAL_PIVOTING_STRATEGY = DualPivotingStrategy.SORT_5B;
144     /** Minimum selection size for quickselect/quicksort.
145      * Below this switch to sortselect/insertion sort rather than selection.
146      * Dual-pivot quicksort used 27 in Yaroslavskiy's original paper.
147      * Changes to this value are only noticeable when the input array is small.
148      *
149      * <p>This is a legacy setting from when insertion sort was used as the stopper.
150      * This has been replaced by edge selection functions. Using insertion sort
151      * is slower as n must be sorted compared to an edge select that only sorts
152      * up to n/2 from the edge. It is disabled by default but can be used for
153      * benchmarking.
154      *
155      * <p>If using insertion sort as the stopper for quickselect:
156      * <ul>
157      * <li>Single-pivot: Benchmarking random data in range [96, 192] suggests a value of ~16 for n=1.
158      * <li>Dual-pivot: Benchmarking random data in range [162, 486] suggests a value of ~27 for n=1
159      * and increasing with higher n in the same range.
160      * Dual-pivot sorting requires a value of ~120. If keys are saturated between k1 and kn
161      * an increase to this threshold will gain full sort performance.
162      * </ul> */
163     static final int MIN_QUICKSELECT_SIZE = 0;
164     /** Minimum size for heapselect.
165      * Below this switch to insertion sort rather than selection. This is used to avoid
166      * heap select on tiny data. */
167     static final int MIN_HEAPSELECT_SIZE = 5;
168     /** Minimum size for sortselect.
169      * Below this switch to insertion sort rather than selection. This is used to avoid
170      * sort select on tiny data. */
171     static final int MIN_SORTSELECT_SIZE = 4;
172     /** Default selection constant for edgeselect. Any k closer than this to the left/right
173      * bound will be selected using the configured edge selection function. */
174     static final int EDGESELECT_CONSTANT = 20;
175     /** Default sort selection constant for linearselect. Note that linear select variants
176      * recursively call quickselect so very small lengths are included with an initial
177      * medium length. Using lengths of 1023-5 and 2043-53 indicate optimum performance around
178      * 80 for median-of-medians when placing the sample on the left. Adaptive linear methods
179      * are faster and so this value is reduced. Quickselect adaptive has a value around 20-30.
180      * Note: When using {@link ExpandStrategy#T2} the input length must create a sample of at
181      * least length 2 as each end of the sample is used as a sentinel. With a sample length of
182      * 1/12 of the data this requires edge select of at least 12. */
183     static final int LINEAR_SORTSELECT_SIZE = 24;
184     /** Default sub-sampling size to identify a single pivot. Sub-sampling is performed if the
185      * length is above this value thus using MAX_VALUE sets it off by default.
186      * The SELECT algorithm of Floyd-Rivest uses 600. */
187     static final int SUBSAMPLING_SIZE = Integer.MAX_VALUE;
188     /** Default key strategy. */
189     static final KeyStrategy KEY_STRATEGY = KeyStrategy.INDEX_SET;
190     /** Default 1 or 2 key strategy. */
191     static final PairedKeyStrategy PAIRED_KEY_STRATEGY = PairedKeyStrategy.SEARCHABLE_INTERVAL;
192     /** Default recursion multiple. */
193     static final int RECURSION_MULTIPLE = 2;
194     /** Default recursion constant. */
195     static final int RECURSION_CONSTANT = 0;
196     /** Default compression. */
197     static final int COMPRESSION_LEVEL = 1;
198     /** Default control flags. */
199     static final int CONTROL_FLAGS = 0;
200     /** Default option flags. */
201     static final int OPTION_FLAGS = 0;
202     /** Default single-pivot partition strategy. */
203     static final SPStrategy SP_STRATEGY = SPStrategy.KBM;
204     /** Default expand partition strategy. A ternary method is faster on equal elements and no
205      * slower on unique elements. */
206     static final ExpandStrategy EXPAND_STRATEGY = ExpandStrategy.T2;
207     /** Default single-pivot linear select strategy. */
208     static final LinearStrategy LINEAR_STRATEGY = LinearStrategy.RSA;
209     /** Default edge select strategy. */
210     static final EdgeSelectStrategy EDGE_STRATEGY = EdgeSelectStrategy.ESS;
211     /** Default single-pivot stopper strategy. */
212     static final StopperStrategy STOPPER_STRATEGY = StopperStrategy.SQA;
213     /** Default quickselect adaptive mode. */
214     static final AdaptMode ADAPT_MODE = AdaptMode.ADAPT3;
215 
216     /** Sampling mode using Floyd-Rivest sampling. */
217     static final int MODE_FR_SAMPLING = -1;
218     /** Sampling mode. */
219     static final int MODE_SAMPLING = 0;
220     /** No sampling but use adaption of the target k. */
221     static final int MODE_ADAPTION = 1;
222     /** No sampling and no adaption of target k (strict margins). */
223     static final int MODE_STRICT = 2;
224 
225     // Floyd-Rivest flags
226 
227     /** Control flag for random sampling. */
228     static final int FLAG_RANDOM_SAMPLING = 0x2;
229     /** Control flag for vector swap of the sample. */
230     static final int FLAG_MOVE_SAMPLE = 0x4;
231     /** Control flag for random subset sampling. This creates the sample at the end
232      * of the data and requires moving regions to reposition around the target k. */
233     static final int FLAG_SUBSET_SAMPLING = 0x8;
234 
235     // RNG flags
236 
237     /** Control flag for biased nextInt(n) RNG. */
238     static final int FLAG_BIASED_RANDOM = 0x1000;
239     /** Control flag for SplittableRandom RNG. */
240     static final int FLAG_SPLITTABLE_RANDOM = 0x2000;
241     /** Control flag for MSWS RNG. */
242     static final int FLAG_MSWS = 0x4000;
243 
244     // Quickselect adaptive flags. Must not clash with the Floyd-Rivest/RNG flags
245     // that are supported for sample mode.
246 
247     /** Control flag for quickselect adaptive to propagate the no sampling mode recursively. */
248     static final int FLAG_QA_PROPAGATE = 0x1;
249     /** Control flag for quickselect adaptive variant of Floyd-Rivest random sampling. */
250     static final int FLAG_QA_RANDOM_SAMPLING = 0x4;
251     /** Control flag for quickselect adaptive to use a different far left/right step
252      * using min of 4; then median of 3 into the 2nd 12th-tile. The default (original) uses
253      * lower median of 4; then min of 3 into 4th 12th-tile). The default has a larger
254      * upper margin of 3/8 vs 1/3 for the new method. The new method is better
255      * with the original k mapping for far left/right and similar speed to the original
256      * far left/right step using the new k mapping. When sampling is off it is marginally
257      * faster, may be due to improved layout of the sample closer to the strict 1/12 lower margin.
258      * There is no compelling evidence to indicate is it better so the default uses
259      * the original far step method. */
260     static final int FLAG_QA_FAR_STEP = 0x8;
261     /** Control flag for quickselect adaptive to map k using the same k mapping for all
262      * repeated steps. This enables the original algorithm behaviour.
263      *
264      * <p>Note that the original mapping can create a lower margin that
265      * does not contain k. This makes it possible to put k into the larger partition.
266      * For the middle and step left methods this heuristic is acceptable as the bias in
267      * margins is shifted but the smaller margin is at least 1/12 of the data and a choice
268      * of this side is not a severe penalty. For the far step left the original mapping
269      * will always create a smaller margin that does not contain k. Removing this
270      * adaptive k and using the median of the 12th-tile shows a measurable speed-up
271      * as the smaller margin always contains k. This result has been extended to change
272      * the mapping for the far step to ensure the smaller
273      * margin always contains at least k elements. This is faster and so enabled by default. */
274     static final int FLAG_QA_FAR_STEP_ADAPT_ORIGINAL = 0x10;
275     /** Use a 12th-tile for the sampling mode in the middle repeated step method.
276      * The default uses a 9th-tile which is a larger sample than the 12th-tile used in
277      * the step left/far left methods. */
278     static final int FLAG_QA_MIDDLE_12 = 0x20;
279     /** Position the sample for quickselect adaptive to place the mapped k' at the target index k.
280      * This is not possible for the far step methods as it can generated a bounds error as
281      * k approaches the edge. */
282     static final int FLAG_QA_SAMPLE_K = 0x40;
283 
284     /** Threshold to use sub-sampling of the range to identify the single pivot.
285      * Sub-sampling uses the Floyd-Rivest algorithm to partition a sample of the data to
286      * identify a pivot so that the target element is in the smaller set after partitioning.
287      * The original FR paper used 600 otherwise reverted to the target index as the pivot.
288      * This implementation uses a sample to identify a median pivot which increases robustness
289      * at small size on a variety of data and allows raising the original FR threshold.
290      * At 600, FR has no speed-up; at double this the speed-up can be measured. */
291     static final int SELECT_SUB_SAMPLING_SIZE = 1200;
292 
293     /** Message for an unsupported introselect configuration. */
294     private static final String UNSUPPORTED_INTROSELECT = "Unsupported introselect: ";
295 
296     /** Transformer factory for double data with the behaviour of a JDK sort.
297      * Moves NaN to the end of the data and handles signed zeros. Works on the data in-place. */
298     private static final Supplier<DoubleDataTransformer> SORT_TRANSFORMER =
299         DoubleDataTransformers.createFactory(NaNPolicy.INCLUDE, false);
300 
301     /** Minimum length between 2 pivots {@code p2 - p1} that requires a full sort. */
302     private static final int SORT_BETWEEN_SIZE = 2;
303     /** log2(e). Used for conversions: log2(x) = ln(x) * log2(e) */
304     private static final double LOG2_E = 1.4426950408889634;
305 
306     /** Threshold to use repeated step left: 7 / 16. */
307     private static final double STEP_LEFT = 0.4375;
308     /** Threshold to use repeated step right: 9 / 16. */
309     private static final double STEP_RIGHT = 0.5625;
310     /** Threshold to use repeated step far-left: 1 / 12. */
311     private static final double STEP_FAR_LEFT = 0.08333333333333333;
312     /** Threshold to use repeated step far-right: 11 / 12. */
313     private static final double STEP_FAR_RIGHT = 0.9166666666666666;
314 
315     /** Default quickselect adaptive mode. Start with FR sampling. */
316     private static int qaMode = MODE_FR_SAMPLING;
317     /** Default quickselect adaptive mode increment. */
318     private static int qaIncrement = 1;
319 
320     // Use final for settings/objects used within partitioning functions
321 
322     /** A {@link PivotingStrategy} used for pivoting. */
323     private final PivotingStrategy pivotingStrategy;
324     /** A {@link DualPivotingStrategy} used for pivoting. */
325     private final DualPivotingStrategy dualPivotingStrategy;
326 
327     /** Minimum size for quickselect when partitioning multiple keys.
328      * Below this threshold partitioning using quickselect is stopped and a sort selection
329      * is performed.
330      *
331      * <p>This threshold is also used in the sort methods to switch to insertion sort;
332      * and in legacy partition methods which do not use edge selection. These may perform
333      * key analysis using this value to determine saturation. */
334     private final int minQuickSelectSize;
335     /** Constant for edgeselect. */
336     private final int edgeSelectConstant;
337     /** Size for sortselect in the linearselect function. Optimal value for this is higher
338      * than for regular quickselect as the median-of-medians pivot strategy is expensive.
339      * For convenience (limit overrides for the constructor) this is not final. */
340     private int linearSortSelectSize = LINEAR_SORTSELECT_SIZE;
341     /** Threshold to use sub-sampling of the range to identify the single pivot.
342      * Sub-sampling uses the Floyd-Rivest algorithm to partition a sample of the data. This
343      * identifies a pivot so that the target element is in the smaller set after partitioning.
344      * The algorithm applies to searching for a single k.
345      * Not all single-pivot {@link PairedKeyStrategy} methods support sub-sampling. It is
346      * available to test in {@link #introselect(SPEPartition, double[], int, int, int, int)}.
347      *
348      * <p>Sub-sampling can provide up to a 2-fold performance gain on large random data.
349      * It can have a 2-fold slowdown on some structured data (e.g. large shuffle data from
350      * the Bentley and McIlroy test data). Large shuffle data also observes a larger performance
351      * drop when using the SBM/BM/DNF partition methods (collect equal values) verses a
352      * simple SP method ignoring equal values. Here large ~500,000; the behaviour
353      * is observed at smaller sizes and becomes increasingly obvious at larger sizes.
354      *
355      * <p>The algorithm relies on partitioning of a subset to be representative of partitioning
356      * of the entire data. Values in a small range partitioned around a pivot P
357      * should create P in a similar location to its position in the entire fully sorted array,
358      * ideally closer to the middle so ensuring elimination of the larger side.
359      * E.g. ordering around P in [ll, rr] will be similar to P's order in [l, r]:
360      * <pre>
361      * target:                       k
362      * subset:                  ll---P-------rr
363      * sorted: l----------------------P-------------------------------------------r
364      *                                Good pivot
365      * </pre>
366      *
367      * <p>If the data in [ll, rr] is not representative then pivot selection based on a
368      * subset creates bad pivot choices and performance is worse than using a
369      * {@link PivotingStrategy}.
370      * <pre>
371      * target:                       k
372      * subset:                 ll----P-------rr
373      * sorted: l------------------------------------------P----------------------r
374      *                                                    Bad pivot
375      * </pre>
376      *
377      * <p>Use of the Floyd-Rivest subset sampling is not always an improvement and is data
378      * dependent. The type of data cannot be known by the partition algorithm before processing.
379      * Thus the Floyd-Rivest subset sampling is more suitable as an option to be enabled by
380      * user settings.
381      *
382      * <p>A random sub-sample can mitigate issues with non-representative data. This can
383      * be done by sampling with/without replacement into a new array; or shuffling in-place
384      * to part of the array. This implementation supports the later option.
385      *
386      * <p>See <a href="https://en.wikipedia.org/wiki/Floyd%E2%80%93Rivest_algorithm">
387      * Floyd-Rivest Algorithm (Wikipedia)</a>.
388      *
389      * <pre>
390      * Floyd and Rivest (1975)
391      * Algorithm 489: The Algorithm SELECT—for Finding the ith Smallest of n elements.
392      * Comm. ACM. 18 (3): 173.
393      * </pre> */
394     private final int subSamplingSize;
395 
396     // Use non-final members for settings used to configure partitioning functions
397 
398     /** Setting to indicate strategy for processing of multiple keys. */
399     private KeyStrategy keyStrategy = KEY_STRATEGY;
400     /** Setting to indicate strategy for processing of 1 or 2 keys. */
401     private PairedKeyStrategy pairedKeyStrategy = PAIRED_KEY_STRATEGY;
402 
403     /** Multiplication factor {@code m} applied to the length based recursion factor {@code x}.
404      * The recursion is set using {@code m * x + c}.
405      * <p>Also used for the multiple of the original length to check the sum of the partition length
406      * for poor quickselect partitions.
407      * <p>Also used for the number of iterations before checking the partition length has been
408      * reduced by a given factor of 2 (in iselect). */
409     private double recursionMultiple = RECURSION_MULTIPLE;
410     /** Constant {@code c} added to the length based recursion factor {@code x}.
411      * The recursion is set using {@code m * x + c}.
412      * <p>Also used to specify the factor of two to reduce the partition length after a set
413      * number of iterations (in iselect). */
414     private int recursionConstant = RECURSION_CONSTANT;
415     /** Compression level for a {@link CompressedIndexSet} (in [1, 31]). */
416     private int compression = COMPRESSION_LEVEL;
417     /** Control flags level for Floyd-Rivest sub-sampling. */
418     private int controlFlags = CONTROL_FLAGS;
419     /** Consumer for the recursion level reached during partitioning. Used to analyse
420      * the distribution of the recursion for different input data. */
421     private IntConsumer recursionConsumer = i -> { /* no-op */ };
422 
423     /** The single-pivot partition function. */
424     private SPEPartition spFunction;
425     /** The expand partition function. */
426     private ExpandPartition expandFunction;
427     /** The single-pivot linear partition function. */
428     private SPEPartition linearSpFunction;
429     /** Selection function used when {@code k} is close to the edge of the range. */
430     private SelectFunction edgeSelection;
431     /** Selection function used when quickselect progress is poor. */
432     private SelectFunction stopperSelection;
433     /** Quickselect adaptive mode. */
434     private AdaptMode adaptMode = ADAPT_MODE;
435 
436     /** Quickselect adaptive mapping function applied when sampling-mode is on. */
437     private MapDistance samplingAdapt;
438     /** Quickselect adaptive mapping function applied when sampling-mode is on for
439      * distances close to the edge (i.e. the far-step functions). */
440     private MapDistance samplingEdgeAdapt;
441     /** Quickselect adaptive mapping function applied when sampling-mode is off. */
442     private MapDistance noSamplingAdapt;
443     /** Quickselect adaptive mapping function applied when sampling-mode is off for
444      * distances close to the edge (i.e. the far-step functions). */
445     private MapDistance noSamplingEdgeAdapt;
446 
447     /**
448      * Define the strategy for processing multiple keys.
449      */
450     enum KeyStrategy {
451         /** Sort unique keys, collate ranges and process in ascending order. */
452         SEQUENTIAL,
453         /** Process in input order using an {@link IndexSet} to cover the entire range.
454          * Introselect implementations will use a {@link SearchableInterval}. */
455         INDEX_SET,
456         /** Process in input order using a {@link CompressedIndexSet} to cover the entire range.
457          * Introselect implementations will use a {@link SearchableInterval}. */
458         COMPRESSED_INDEX_SET,
459         /** Process in input order using a {@link PivotCache} to cover the minimum range. */
460         PIVOT_CACHE,
461         /** Sort unique keys and process using recursion with division of the keys
462          * for each sub-partition. */
463         ORDERED_KEYS,
464         /** Sort unique keys and process using recursion with a {@link ScanningKeyInterval}. */
465         SCANNING_KEY_SEARCHABLE_INTERVAL,
466         /** Sort unique keys and process using recursion with a {@link BinarySearchKeyInterval}. */
467         SEARCH_KEY_SEARCHABLE_INTERVAL,
468         /** Sort unique keys and process using recursion with a {@link KeyIndexIterator}. */
469         INDEX_ITERATOR,
470         /** Process in input order using an {@link IndexIterator} of a {@link CompressedIndexSet}. */
471         COMPRESSED_INDEX_ITERATOR,
472         /** Process using recursion with an {@link IndexSet}-based {@link UpdatingInterval}. */
473         INDEX_SET_UPDATING_INTERVAL,
474         /** Sort unique keys and process using recursion with an {@link UpdatingInterval}. */
475         KEY_UPDATING_INTERVAL,
476         /** Process using recursion with an {@link IndexSet}-based {@link SplittingInterval}. */
477         INDEX_SET_SPLITTING_INTERVAL,
478         /** Sort unique keys and process using recursion with a {@link SplittingInterval}. */
479         KEY_SPLITTING_INTERVAL;
480     }
481 
482     /**
483      * Define the strategy for processing 1 key or 2 keys: (k, k+1).
484      */
485     enum PairedKeyStrategy {
486         /** Use a dedicated single key method that returns information about (k+1).
487          * Use recursion depth to trigger the stopper select. */
488         PAIRED_KEYS,
489         /** Use a dedicated single key method that returns information about (k+1).
490          * Recursion is monitored by checking the partition is reduced by 2<sup>-x</sup> after
491          * {@code c} iterations where {@code x} is the
492          * {@link #setRecursionConstant(int) recursion constant} and {@code c} is the
493          * {@link #setRecursionMultiple(double) recursion multiple} */
494         PAIRED_KEYS_2,
495         /** Use a dedicated single key method that returns information about (k+1).
496          * Use a multiple of the sum of the length of all partitions to trigger the stopper select. */
497         PAIRED_KEYS_LEN,
498         /** Use a method that accepts two separate keys. The keys do not define a range
499          * and are independent. */
500         TWO_KEYS,
501         /** Use a method that accepts two keys to define a range.
502          * Recursion is monitored by checking the partition is reduced by 2<sup>-x</sup> after
503          * {@code c} iterations where {@code x} is the
504          * {@link #setRecursionConstant(int) recursion constant} and {@code c} is the
505          * {@link #setRecursionMultiple(double) recursion multiple} */
506         KEY_RANGE,
507         /** Use an {@link SearchableInterval} covering the keys. This will reuse a multi-key
508          * strategy with keys that are a very small range. */
509         SEARCHABLE_INTERVAL,
510         /** Use an {@link UpdatingInterval} covering the keys. This will reuse a multi-key
511          * strategy with keys that are a very small range. */
512         UPDATING_INTERVAL;
513     }
514 
515     /**
516      * Define the strategy for single-pivot partitioning. Partitioning may be binary
517      * ({@code <, >}), or ternary ({@code <, ==, >}) by collecting values equal to the
518      * pivot value. Typically partitioning will use two pointers i and j to traverse the
519      * sequence from either end; or a single pointer i for a single pass.
520      *
521      * <p>Binary partitioning will be faster for quickselect when no equal elements are
522      * present. As duplicates become increasingly likely a ternary partition will be
523      * faster for quickselect to avoid repeat processing of values (that matched the
524      * previous pivot) on the next iteration. The type of ternary partition with the best
525      * performance depends on the number of duplicates. In the extreme case of 1 or 2
526      * unique elements it is more likely to match the {@code ==, !=} comparison to the
527      * pivot than {@code <, >} (see {@link #DNF3}). An ideal ternary scheme should have
528      * little impact on data with no repeats, and significantly improve performance as the
529      * number of repeat elements increases.
530      *
531      * <p>Binary partitioning will skip over values already {@code <, >}, or
532      * {@code <=, =>} to the pivot value; otherwise values at the pointers i and j are
533      * swapped. If using {@code <, >} then values can be placed at either end of the
534      * sequence that are {@code >=, <=} respectively to act as sentinels during the scan.
535      * This is always possible in binary partitioning as the pivot can be one sentinel;
536      * any other value will be either {@code <=, =>} to the pivot and so can be used at
537      * one or the other end as appropriate. Note: Many schemes omit using sentinels. Modern
538      * processor branch prediction nullifies the cost of checking indices remain within
539      * the {@code [left, right]} bounds. However placing sentinels is a negligible cost
540      * and at least simplifies the code for the region traversal.
541      *
542      * <p>Bentley-McIlroy ternary partitioning schemes move equal values to the ends
543      * during the traversal, these are moved to the centre after the pass. This may use
544      * minimal swaps based on region sizes. Note that values already {@code <, >} are not
545      * moved during traversal allowing moves to be minimised.
546      *
547      * <p>Dutch National Flag schemes move non-equal values to either end and finish with
548      * the equal value region in the middle. This requires that every element is moved
549      * during traversal, even if already {@code <, >}. This can be mitigated by fast-forward
550      * of pointers at the current {@code <, >} end points until the condition is not true.
551      *
552      * @see SPEPartition
553      */
554     enum SPStrategy {
555         /**
556          * Single-pivot partitioning. Uses a method adapted from Floyd and Rivest (1975)
557          * which uses sentinels to avoid bounds checks on the i and j pointers.
558          * This is a baseline for the maximum speed when no equal elements are present.
559          */
560         SP,
561         /**
562          * Bentley-McIlroy ternary partitioning. Requires bounds checks on the i and j
563          * pointers during traversal. Comparisons to the pivot use {@code <=, =>} and a
564          * second check for {@code ==} if the first is true.
565          */
566         BM,
567         /**
568          * Sedgewick's Bentley-McIlroy ternary partitioning. Requires bounds checks on the
569          * j pointer during traversal. Comparisons to the pivot use {@code <, >} and a
570          * second check for {@code ==} when both i and j have stopped.
571          */
572         SBM,
573         /**
574          * Kiwiel's Bentley-McIlroy ternary partitioning. Similar to Sedgewick's BM but
575          * avoids bounds checks on both pointers during traversal using sentinels.
576          * Comparisons to the pivot use {@code <, >} and a second check for {@code ==}
577          * when both i and j have stopped. Handles i and j meeting at the pivot without a
578          * swap.
579          */
580         KBM,
581         /**
582          * Dutch National Flag partitioning. Single pointer iteration using {@code <, >}
583          * comparisons to move elements to the edges. Fast-forwards any initial {@code <}
584          * region. The {@code ==} region is filled with the pivot after region traversal.
585          */
586         DNF1,
587         /**
588          * Dutch National Flag partitioning. Single pointer iteration using {@code <, >}
589          * comparisons to move elements to the edges. Fast-forwards any initial {@code <}
590          * region. The {@code >} region uses fast-forward to reduce swaps. The {@code ==}
591          * region is filled with the pivot after region traversal.
592          */
593         DNF2,
594         /**
595          * Dutch National Flag partitioning. Single pointer iteration using {@code !=}
596          * comparison to identify elements to move to the edges, then {@code <, >}
597          * comparisons. Fast-forwards any initial {@code <} region. The {@code >} region
598          * uses fast-forward to reduce swaps. The {@code ==} region is filled during
599          * traversal.
600          */
601         DNF3;
602     }
603 
604     /**
605      * Define the strategy for expanding a partition. This function is used when
606      * partitioning has used a sample located within the range to find the pivot.
607      * The remaining range below and above the sample can be partitioned without
608      * re-processing the sample.
609      *
610      * <p>Schemes may be binary ({@code <, >}), or ternary ({@code <, ==, >}) by
611      * collecting values equal to the pivot value. Schemes may process the
612      * unpartitioned range below and above the partitioned middle using a sweep
613      * outwards towards the ends; or start at the ends and sweep inwards towards
614      * the partitioned middle.
615      *
616      * @see ExpandPartition
617      */
618     enum ExpandStrategy {
619         /** Use the current {@link SPStrategy} partition method. This will not expand
620          * the partition but will Partition the Entire Range (PER). This can be used
621          * to test if the implementations of expand are efficient. */
622         PER,
623         /** Ternary partition method 1. Sweeps outwards and uses sentinels at the ends
624          * to avoid pointer range checks. Equal values are moved directly into the
625          * central pivot range. */
626         T1,
627         /** Ternary partition method 2. Similar to {@link #T1} with different method
628          * to set the sentinels. */
629         T2,
630         /** Binary partition method 1. Sweeps outwards and uses sentinels at the ends
631          * to avoid pointer range checks. */
632         B1,
633         /** Binary partition method 2. Similar to {@link #B1} with different method
634          * to set the sentinels. */
635         B2,
636     }
637 
638     /**
639      * Define the strategy for the linear select single-pivot partition function. Linear
640      * select functions use a deterministic sample to find a pivot value that will
641      * eliminate at least a set fraction of the range (fixed borders/margins). After the
642      * sample has been processed to find a pivot the entire range is partitioned. This can
643      * be done by re-processing the entire range, or expanding the partition.
644      *
645      * <p>Adaption (selecting a non-central index in the median-of-medians sample) creates
646      * asymmetric borders; in practice the larger border is typically eliminated per
647      * iteration which improves performance.
648      *
649      * @see SPStrategy
650      * @see ExpandStrategy
651      * @see SPEPartition
652      * @see ExpandPartition
653      */
654     enum LinearStrategy {
655         /** Uses the Blum, Floyd, Pratt, Rivest, and Tarjan (BFPRT) median-of-medians algorithm
656          * with medians of 5. This is the baseline version that creates the median sample
657          * at the left end and repartitions the entire range using the pivot.
658          * Fixed borders of 3/10. */
659         BFPRT,
660         /** Uses the Chen and Dumitrescu repeated step median-of-medians-of-medians algorithm
661          * with medians of 3. This is the baseline version that creates the median sample
662          * at the left end and repartitions the entire range using the pivot.
663          * Fixed borders of 2/9. */
664         RS,
665         /** Uses the Blum, Floyd, Pratt, Rivest, and Tarjan (BFPRT) median-of-medians algorithm
666          * with medians of 5. This is the improved version that creates the median sample
667          * in the centre and expands the partition around the pivot sample.
668          * Fixed borders of 3/10. */
669         BFPRT_IM,
670         /** Uses the Chen and Dumitrescu repeated step median-of-medians-of-medians algorithm
671          * with medians of 3. This is the improved version that creates the median sample
672          * in the centre and expands the partition around the pivot sample.
673          * Fixed borders of 2/9. */
674         RS_IM,
675         /** Uses the Blum, Floyd, Pratt, Rivest, and Tarjan (BFPRT) median-of-medians algorithm
676          * with medians of 5. This is the improved version that creates the median sample
677          * in the centre and expands the partition around the pivot sample; the adaption
678          * is to use k to define the pivot in the sample instead of using the median.
679          * This will not have fixed borders. */
680         BFPRTA,
681         /** Uses the Chen and Dumitrescu repeated step median-of-medians-of-medians algorithm
682          * with medians of 3. This is the adaptive version that creates the median sample
683          * in the centre and expands the partition around the pivot sample; the adaption
684          * is to use k to define the pivot in the sample instead of using the median.
685          * This will not have fixed borders. */
686         RSA;
687     }
688 
689     /**
690      * Define the strategy for selecting {@code k} close to the edge.
691      * <p>These are named to allow regex identification for dynamic configuration
692      * in benchmarking using the name; this uses the E (Edge) prefix.
693      */
694     enum EdgeSelectStrategy {
695         /** Use heapselect version 1. Selects {@code k} and an additional
696          * {@code c} elements closer to the edge than {@code k} using a heap
697          * structure. */
698         ESH,
699         /** Use heapselect version 2. Differs from {@link #ESH} in the
700          * final unwinding of the heap to sort the range {@code [ka, kb]};
701          * the heap construction is identical. */
702         ESH2,
703         /** Use sortselect version 1. Uses an insertion sort to maintain {@code k}
704          * and all elements closer to the edge as sorted. */
705         ESS,
706         /** Use sortselect version 2. Differs from {@link #ESS} by a using pointer
707          * into the sorted range to improve insertion speed. In practice the more
708          * complex code is not more performant. */
709         ESS2;
710     }
711 
712     /**
713      * Define the strategy for selecting {@code k} when quickselect progress is poor
714      * (worst case is quadratic). This should be a method providing good worst-case
715      * performance.
716      * <p>These are named to allow regex identification for dynamic configuration
717      * in benchmarking using the name; this uses the S (Stopper) prefix.
718      */
719     enum StopperStrategy {
720         /** Use heapselect version 1. Selects {@code k} and an additional
721          * {@code c} elements closer to the edge than {@code k}. Heapselect
722          * provides increasingly slower performance with distance from the edge.
723          * It has better worst-case performance than quickselect. */
724         SSH,
725         /** Use heapselect version 2. Differs from {@link #SSH} in the
726          * final unwinding of the heap to sort the range {@code [ka, kb]};
727          * the heap construction is identical. */
728         SSH2,
729         /** Use a linear selection algorithm with Order(n) worst-case performance.
730          * This is a median-of-medians using medians of size 5. This is the base
731          * implementation using a median sample into the first 20% of the data
732          * and not the improved version (with sample in the centre). */
733         SLS,
734         /** Use the quickselect adaptive algorithm with Order(n) worst-case performance. */
735         SQA;
736     }
737 
738     /**
739      * Partition function. Used to benchmark different implementations.
740      *
741      * <p>Note: The function is applied within a {@code [left, right]} bound. This bound
742      * is set using the entire range of the data to process, or it may be a sub-range
743      * due to previous partitioning. In this case the value at {@code left - 1} and/or
744      * {@code right + 1} can be a pivot. The value at these pivot points will be {@code <=} or
745      * {@code >=} respectively to all values within the range. This information is valuable
746      * during recursive partitioning and is passed as flags to the partition method.
747      */
748     private interface PartitionFunction {
749 
750         /**
751          * Partition (partially sort) the array in the range {@code [left, right]} around
752          * a central region {@code [ka, kb]}. The central region should be entirely
753          * sorted.
754          *
755          * <pre>{@code
756          * data[i < ka] <= data[ka] <= data[kb] <= data[kb < i]
757          * }</pre>
758          *
759          * @param a Data array.
760          * @param left Lower bound (inclusive).
761          * @param right Upper bound (inclusive).
762          * @param ka Lower bound (inclusive) of the central region.
763          * @param kb Upper bound (inclusive) of the central region.
764          * @param leftInner Flag to indicate {@code left - 1} is a pivot.
765          * @param rightInner Flag to indicate {@code right + 1} is a pivot.
766          */
767         void partition(double[] a, int left, int right, int ka, int kb,
768             boolean leftInner, boolean rightInner);
769 
770         /**
771          * Partition (partially sort) the array in the range {@code [left, right]} around
772          * a central region {@code [ka, kb]}. The central region should be entirely
773          * sorted.
774          *
775          * <pre>{@code
776          * data[i < ka] <= data[ka] <= data[kb] <= data[kb < i]
777          * }</pre>
778          *
779          * <p>The {@link PivotStore} is only required to record pivots after {@code kb}.
780          * This is to support sequential ascending order processing of regions to partition.
781          *
782          * @param a Data array.
783          * @param left Lower bound (inclusive).
784          * @param right Upper bound (inclusive).
785          * @param ka Lower bound (inclusive) of the central region.
786          * @param kb Upper bound (inclusive) of the central region.
787          * @param leftInner Flag to indicate {@code left - 1} is a pivot.
788          * @param rightInner Flag to indicate {@code right + 1} is a pivot.
789          * @param pivots Used to store sorted regions.
790          */
791         void partitionSequential(double[] a, int left, int right, int ka, int kb,
792             boolean leftInner, boolean rightInner, PivotStore pivots);
793 
794         /**
795          * Partition (partially sort) the array in the range {@code [left, right]} around
796          * a central region {@code [ka, kb]}. The central region should be entirely
797          * sorted.
798          *
799          * <pre>{@code
800          * data[i < ka] <= data[ka] <= data[kb] <= data[kb < i]
801          * }</pre>
802          *
803          * <p>The {@link PivotStore} records all pivots and sorted regions.
804          *
805          * @param a Data array.
806          * @param left Lower bound (inclusive).
807          * @param right Upper bound (inclusive).
808          * @param ka Lower bound (inclusive) of the central region.
809          * @param kb Upper bound (inclusive) of the central region.
810          * @param leftInner Flag to indicate {@code left - 1} is a pivot.
811          * @param rightInner Flag to indicate {@code right + 1} is a pivot.
812          * @param pivots Used to store sorted regions.
813          */
814         void partition(double[] a, int left, int right, int ka, int kb,
815             boolean leftInner, boolean rightInner, PivotStore pivots);
816 
817         /**
818          * Sort the array in the range {@code [left, right]}.
819          *
820          * @param a Data array.
821          * @param left Lower bound (inclusive).
822          * @param right Upper bound (inclusive).
823          * @param leftInner Flag to indicate {@code left - 1} is a pivot.
824          * @param rightInner Flag to indicate {@code right + 1} is a pivot.
825          */
826         void sort(double[] a, int left, int right, boolean leftInner, boolean rightInner);
827     }
828 
829     /**
830      * Single-pivot partition method handling equal values.
831      */
832     @FunctionalInterface
833     private interface SPEPartitionFunction extends PartitionFunction {
834         /**
835          * Partition an array slice around a single pivot. Partitioning exchanges array
836          * elements such that all elements smaller than pivot are before it and all
837          * elements larger than pivot are after it.
838          *
839          * <p>This method returns 2 points describing the pivot range of equal values.
840          * <pre>{@code
841          *                     |k0 k1|
842          * |         <P        | ==P |            >P        |
843          * }</pre>
844          * <ul>
845          * <li>k0: lower pivot point
846          * <li>k1: upper pivot point
847          * </ul>
848          *
849          * @param a Data array.
850          * @param left Lower bound (inclusive).
851          * @param right Upper bound (inclusive).
852          * @param upper Upper bound (inclusive) of the pivot range [k1].
853          * @param leftInner Flag to indicate {@code left - 1} is a pivot.
854          * @param rightInner Flag to indicate {@code right + 1} is a pivot.
855          * @return Lower bound (inclusive) of the pivot range [k0].
856          */
857         int partition(double[] a, int left, int right, int[] upper,
858             boolean leftInner, boolean rightInner);
859 
860         // Add support to have a pivot cache. Assume it is to store pivots after kb.
861         // Switch to not using it when right < kb, or doing a full sort between
862         // left and right (pivots are irrelevant).
863 
864         @Override
865         default void partition(double[] a, int left, int right, int ka, int kb,
866             boolean leftInner, boolean rightInner) {
867             // Skip when [left, right] does not overlap [ka, kb]
868             if (right - left < 1) {
869                 return;
870             }
871             // Assume: left <= right && ka <= kb
872             // Ranges may overlap either way:
873             // left ---------------------- right
874             //        ka --- kb
875             //
876             // Requires full sort:
877             // ka ------------------------- kb
878             //        left ---- right
879             //
880             // This will naturally perform a full sort when ka < left and kb > right
881 
882             // Edge case for a single point
883             if (ka == right) {
884                 selectMax(a, left, ka);
885             } else if (kb == left) {
886                 selectMin(a, kb, right);
887             } else {
888                 final int[] upper = {0};
889                 final int k0 = partition(a, left, right, upper, leftInner, rightInner);
890                 final int k1 = upper[0];
891                 // Sorted in [k0, k1]
892                 // Unsorted in [left, k0) and (k1, right]
893                 if (ka < k0) {
894                     partition(a, left, k0 - 1, ka, kb, leftInner, true);
895                 }
896                 if (kb > k1) {
897                     partition(a, k1 + 1, right, ka, kb, true, rightInner);
898                 }
899             }
900         }
901 
902         @Override
903         default void partitionSequential(double[] a, int left, int right, int ka, int kb,
904             boolean leftInner, boolean rightInner, PivotStore pivots) {
905             // This method is a copy of the above method except:
906             // - It records all sorted ranges to the cache
907             // - It switches to the above method when the cache is not required
908             if (right - left < 1) {
909                 return;
910             }
911             if (ka == right) {
912                 selectMax(a, left, ka);
913                 pivots.add(ka);
914             } else if (kb == left) {
915                 selectMin(a, kb, right);
916                 pivots.add(kb);
917             } else {
918                 final int[] upper = {0};
919                 final int k0 = partition(a, left, right, upper, leftInner, rightInner);
920                 final int k1 = upper[0];
921                 // Sorted in [k0, k1]
922                 // Unsorted in [left, k0) and (k1, right]
923                 pivots.add(k0, k1);
924 
925                 if (ka < k0) {
926                     if (k0 - 1 < kb) {
927                         // Left branch entirely below kb - no cache required
928                         partition(a, left, k0 - 1, ka, kb, leftInner, true);
929                     } else {
930                         partitionSequential(a, left, k0 - 1, ka, kb, leftInner, true, pivots);
931                     }
932                 }
933                 if (kb > k1) {
934                     partitionSequential(a, k1 + 1, right, ka, kb, true, rightInner, pivots);
935                 }
936             }
937         }
938 
939         @Override
940         default void partition(double[] a, int left, int right, int ka, int kb,
941             boolean leftInner, boolean rightInner, PivotStore pivots) {
942             // This method is a copy of the above method except:
943             // - It records all sorted ranges to the cache
944             // - It switches to the above method when the cache is not required
945             if (right - left < 1) {
946                 return;
947             }
948             if (ka == right) {
949                 selectMax(a, left, ka);
950                 pivots.add(ka);
951             } else if (kb == left) {
952                 selectMin(a, kb, right);
953                 pivots.add(kb);
954             } else {
955                 final int[] upper = {0};
956                 final int k0 = partition(a, left, right, upper, leftInner, rightInner);
957                 final int k1 = upper[0];
958                 // Sorted in [k0, k1]
959                 // Unsorted in [left, k0) and (k1, right]
960                 pivots.add(k0, k1);
961 
962                 if (ka < k0) {
963                     partition(a, left, k0 - 1, ka, kb, leftInner, true, pivots);
964                 }
965                 if (kb > k1) {
966                     partition(a, k1 + 1, right, ka, kb, true, rightInner, pivots);
967                 }
968             }
969         }
970 
971         @Override
972         default void sort(double[] a, int left, int right, boolean leftInner, boolean rightInner) {
973             // Skip when [left, right] is sorted
974             // Note: This has no insertion sort for small lengths (so is less performant).
975             // It can be used to test the partition algorithm across the entire data.
976             if (right - left < 1) {
977                 return;
978             }
979             final int[] upper = {0};
980             final int k0 = partition(a, left, right, upper, leftInner, rightInner);
981             final int k1 = upper[0];
982             // Sorted in [k0, k1]
983             // Unsorted in [left, k0) and (k1, right]
984             sort(a, left, k0 - 1, leftInner, true);
985             sort(a, k1 + 1, right, true, rightInner);
986         }
987     }
988 
989     /**
990      * Single-pivot partition method handling equal values.
991      */
992     @FunctionalInterface
993     interface SPEPartition {
994         /**
995          * Partition an array slice around a single pivot. Partitioning exchanges array
996          * elements such that all elements smaller than pivot are before it and all
997          * elements larger than pivot are after it.
998          *
999          * <p>This method returns 2 points describing the pivot range of equal values.
1000          * <pre>{@code
1001          *                     |k0 k1|
1002          * |         <P        | ==P |            >P        |
1003          * }</pre>
1004          * <ul>
1005          * <li>k0: lower pivot point
1006          * <li>k1: upper pivot point (inclusive)
1007          * </ul>
1008          *
1009          * @param a Data array.
1010          * @param left Lower bound (inclusive).
1011          * @param right Upper bound (inclusive).
1012          * @param upper Upper bound (inclusive) of the pivot range [k1].
1013          * @param pivot Pivot location.
1014          * @return Lower bound (inclusive) of the pivot range [k0].
1015          */
1016         int partition(double[] a, int left, int right, int pivot, int[] upper);
1017     }
1018 
1019     /**
1020      * Dual-pivot partition method handling equal values.
1021      */
1022     @FunctionalInterface
1023     interface DPPartition {
1024         /**
1025          * Partition an array slice around two pivots. Partitioning exchanges array
1026          * elements such that all elements smaller than pivot are before it and all
1027          * elements larger than pivot are after it.
1028          *
1029          * <p>This method returns 4 points describing the pivot ranges of equal values.
1030          * <pre>{@code
1031          *         |k0  k1|                |k2  k3|
1032          * |   <P  | ==P1 |  <P1 && <P2    | ==P2 |   >P   |
1033          * }</pre>
1034          * <ul>
1035          * <li>k0: lower pivot1 point
1036          * <li>k1: upper pivot1 point (inclusive)
1037          * <li>k2: lower pivot2 point
1038          * <li>k3: upper pivot2 point (inclusive)
1039          * </ul>
1040          *
1041          * <p>Bounds are set so {@code i < k0}, {@code i > k3} and {@code k1 < i < k2} are
1042          * unsorted. When the range {@code [k0, k3]} contains fully sorted elements the result
1043          * is set to {@code k1 = k3; k2 == k0}. This can occur if
1044          * {@code P1 == P2} or there are zero or 1 value between the pivots
1045          * {@code P1 < v < P2}. Any sort/partition of ranges [left, k0-1], [k1+1, k2-1] and
1046          * [k3+1, right] must check the length is {@code > 1}.
1047          *
1048          * @param a Data array.
1049          * @param left Lower bound (inclusive).
1050          * @param right Upper bound (inclusive).
1051          * @param bounds Points [k1, k2, k3].
1052          * @param pivot1 Pivot1 location.
1053          * @param pivot2 Pivot2 location.
1054          * @return Lower bound (inclusive) of the pivot range [k0].
1055          */
1056         int partition(double[] a, int left, int right, int pivot1, int pivot2, int[] bounds);
1057     }
1058 
1059     /**
1060      * Select function.
1061      *
1062      * <p>Used to define the function to call when {@code k} is close
1063      * to the edge; or when quickselect progress is poor. This allows
1064      * the edge-select or stopper-function to be configured using parameters.
1065      */
1066     @FunctionalInterface
1067     interface SelectFunction {
1068         /**
1069          * Partition the elements between {@code ka} and {@code kb}.
1070          * It is assumed {@code left <= ka <= kb <= right}.
1071          *
1072          * @param a Data array to use to find out the K<sup>th</sup> value.
1073          * @param left Lower bound (inclusive).
1074          * @param right Upper bound (inclusive).
1075          * @param ka Lower index to select.
1076          * @param kb Upper index to select.
1077          */
1078         void partition(double[] a, int left, int right, int ka, int kb);
1079     }
1080 
1081     /**
1082      * Single-pivot partition method handling a pre-partitioned range in the centre.
1083      */
1084     @FunctionalInterface
1085     interface ExpandPartition {
1086         /**
1087          * Expand a partition around a single pivot. Partitioning exchanges array
1088          * elements such that all elements smaller than pivot are before it and all
1089          * elements larger than pivot are after it. The central region is already
1090          * partitioned.
1091          *
1092          * <pre>{@code
1093          * |l             |s   |p0 p1|   e|                r|
1094          * |    ???       | <P | ==P | >P |        ???      |
1095          * }</pre>
1096          *
1097          * <p>This method returns 2 points describing the pivot range of equal values.
1098          * <pre>{@code
1099          * |l                  |k0 k1|                     r|
1100          * |         <P        | ==P |            >P        |
1101          * }</pre>
1102          * <ul>
1103          * <li>k0: lower pivot point
1104          * <li>k1: upper pivot point (inclusive)
1105          * </ul>
1106          *
1107          * @param a Data array.
1108          * @param left Lower bound (inclusive).
1109          * @param right Upper bound (inclusive).
1110          * @param start Start of the partition range (inclusive).
1111          * @param end End of the partitioned range (inclusive).
1112          * @param pivot0 Lower pivot location (inclusive).
1113          * @param pivot1 Upper pivot location (inclusive).
1114          * @param upper Upper bound (inclusive) of the pivot range [k1].
1115          * @return Lower bound (inclusive) of the pivot range [k0].
1116          */
1117         int partition(double[] a, int left, int right, int start, int end,
1118             int pivot0, int pivot1, int[] upper);
1119     }
1120 
1121     /**
1122      * Function to map the distance from the edge of a range {@code [l, r]} to a smaller
1123      * range. The mapping is used in the quickselect adaptive method to adapt {@code k}
1124      * based on the position in the sample: {@code kf'/|A|}; k is the index to partition;
1125      * |A| is the size of the data to partition; f' is the size of the sample.
1126      */
1127     enum MapDistance {
1128         /** Use the median of the new range. */
1129         MEDIAN {
1130             @Override
1131             int mapDistance(int d, int l, int r, int n) {
1132                 return n >>> 1;
1133             }
1134         },
1135         /** Map the distance using a fraction of the original range: {@code d / (r - l)}. */
1136         ADAPT {
1137             @Override
1138             int mapDistance(int d, int l, int r, int n) {
1139                 // If distance==r-l this returns n-1
1140                 return (int) (d * (n - 1.0) / (r - l));
1141             }
1142         },
1143         /** Use the midpoint between the adaption computed by the {@link #MEDIAN} and {@link #ADAPT} methods. */
1144         HALF_ADAPT {
1145             @Override
1146             int mapDistance(int d, int l, int r, int n) {
1147                 // Half-adaption: compute the full adaption
1148                 final int x = ADAPT.mapDistance(d, l, r, n);
1149                 // Compute the median between the x and the middle
1150                 final int m = n >>> 1;
1151                 return (m + x) >>> 1;
1152             }
1153         },
1154         /**
1155          * Map the distance assuming the distance to the edge is small. This method is
1156          * used when the sample has a lower margin (minimum number of elements) in the
1157          * original range of {@code 2(d+1)}. That is each element in the sample has at
1158          * least 1 element below it in the original range. This occurs for example when
1159          * the sample is built using a median-of-3. In this case setting the mapped
1160          * distance as {@code d/2} will ensure that the lower margin in the original data
1161          * is at least {@code d} and consequently {@code d} is inside the lower margin. It
1162          * will generate a bounds error if called with {@code d > 2(r - l)}.
1163          */
1164         EDGE_ADAPT {
1165             @Override
1166             int mapDistance(int d, int l, int r, int n) {
1167                 return d >>> 1;
1168             }
1169         };
1170 
1171         /**
1172          * Map the distance from the edge of {@code [l, r]} to a new distance in {@code [0, n)}.
1173          *
1174          * <p>For convenience this accepts the input range {@code [l, r]} instead of the length
1175          * of the original range. The implementation may use the range or ignore it and only
1176          * use the new range size {@code n}.
1177          *
1178          * @param d Distance from the edge in {@code [0, r - l]}.
1179          * @param l Lower bound (inclusive).
1180          * @param r Upper bound (inclusive).
1181          * @param n Size of the new range.
1182          * @return the mapped distance in [0, n)
1183          */
1184         abstract int mapDistance(int d, int l, int r, int n);
1185     }
1186 
1187     /**
1188      * Encapsulate the state of adaption in the quickselect adaptive algorithm.
1189      *
1190      * <p>To ensure linear runtime performance a fixed size of data must be eliminated at each
1191      * step. This requires a median-of-median-of-medians pivot sample generated from all the data
1192      * with the target {@code k} mapped to the middle of the sample so that the margins of the
1193      * possible partitions are a minimum size.
1194      * Note that random selection of a pivot will achieve the same margins with some probability
1195      * and is less expensive to compute; runtime performance may be better or worse due to average
1196      * quality of the pivot. The adaption in quickselect adaptive is two-fold:
1197      * <ul>
1198      * <li>Sample mode: Do not use all the data to create the pivot sample. This is less expensive
1199      * to compute but invalidates strict margins. The margin quality is better than a random pivot
1200      * due to sampling a reasonable range of the data and using medians to create the sample.
1201      * <li>Adaption mode: Map the target {@code k} from the current range to the size of the pivot
1202      * sample. This create asymmetric margins and adapts the larger margin to the position of
1203      * {@code k} thus increasing the chance of eliminating a large amount of data. However data
1204      * randomness can create a larger margin so large it includes {@code k} and partitioning
1205      * must eliminate a possibly very small other side.
1206      * </ul>
1207      *
1208      * <p>The quickselect adaptive paper suggests sampling mode is turned off when margins are not
1209      * achieved. That is the size after partitioning is not as small as expected.
1210      * However there is no detail on whether to turn off adaption, and the margins that are
1211      * targeted. This provides the following possible state transitions:
1212      * <pre>{@code
1213      * 1: sampling + adaption    --> no-sampling + adaption
1214      * 2: sampling + adaption    --> no-sampling + no-adaption
1215      * 3: sampling + adaption    --> no-sampling + adaption     --> no-sampling + no-adaption
1216      * 4: sampling + no-adaption --> no-sampling + no-adaption
1217      * }</pre>
1218      *
1219      * <p>The behaviour is captured in this enum as a state-machine. The finite state
1220      * is dependent on the start state. The transition from one state to the next may require a
1221      * count of failures to achieve; this is not captured in this state machine.
1222      *
1223      * <p>Note that use of no-adaption when sampling (case 4) is unlikely to work unless the sample
1224      * median is representative of the location of the pivot sample. This is true for
1225      * median-of-median-of-medians but not the offset pivot samples used in quickselect adaptive;
1226      * this is supported for completeness and can be used to demonstrate its inefficiency.
1227      */
1228     enum AdaptMode {
1229         /** No sampling and no adaption (fixed margins) for worst-case linear runtime performance.
1230          * This is a terminal state. */
1231         FIXED {
1232             @Override
1233             boolean isSampleMode() {
1234                 return false;
1235             }
1236             @Override
1237             boolean isAdapt() {
1238                 return false;
1239             }
1240             @Override
1241             AdaptMode update(int size, int l, int r) {
1242                 // No further states
1243                 return this;
1244             }
1245         },
1246         /** Sampling and adaption. Failure to achieve the expected partition size
1247          * will revert to no sampling but retain adaption. */
1248         ADAPT1 {
1249             @Override
1250             boolean isSampleMode() {
1251                 return true;
1252             }
1253             @Override
1254             boolean isAdapt() {
1255                 return true;
1256             }
1257             @Override
1258             AdaptMode update(int size, int l, int r) {
1259                 return r - l <= size ? this : ADAPT1B;
1260             }
1261         },
1262         /** No sampling and use adaption. This is a terminal state. */
1263         ADAPT1B {
1264             @Override
1265             boolean isSampleMode() {
1266                 return false;
1267             }
1268             @Override
1269             boolean isAdapt() {
1270                 return true;
1271             }
1272             @Override
1273             AdaptMode update(int size, int l, int r) {
1274                 // No further states
1275                 return this;
1276             }
1277         },
1278         /** Sampling and adaption. Failure to achieve the expected partition size
1279          * will revert to no sampling and no adaption. */
1280         ADAPT2 {
1281             @Override
1282             boolean isSampleMode() {
1283                 return true;
1284             }
1285             @Override
1286             boolean isAdapt() {
1287                 return true;
1288             }
1289             @Override
1290             AdaptMode update(int size, int l, int r) {
1291                 return r - l <= size ? this : FIXED;
1292             }
1293         },
1294         /** Sampling and adaption. Failure to achieve the expected partition size
1295          * will revert to no sampling but retain adaption. */
1296         ADAPT3 {
1297             @Override
1298             boolean isSampleMode() {
1299                 return true;
1300             }
1301             @Override
1302             boolean isAdapt() {
1303                 return true;
1304             }
1305             @Override
1306             AdaptMode update(int size, int l, int r) {
1307                 return r - l <= size ? this : ADAPT3B;
1308             }
1309         },
1310         /** No sampling and use adaption. Failure to achieve the expected partition size
1311          * will disable adaption (revert to fixed margins). */
1312         ADAPT3B {
1313             @Override
1314             boolean isSampleMode() {
1315                 return false;
1316             }
1317             @Override
1318             boolean isAdapt() {
1319                 return true;
1320             }
1321             @Override
1322             AdaptMode update(int size, int l, int r) {
1323                 return r - l <= size ? this : FIXED;
1324             }
1325         },
1326         /** Sampling and no adaption. Failure to achieve the expected partition size
1327          * will disabled sampling (revert to fixed margins). */
1328         ADAPT4 {
1329             @Override
1330             boolean isSampleMode() {
1331                 return true;
1332             }
1333             @Override
1334             boolean isAdapt() {
1335                 return false;
1336             }
1337             @Override
1338             AdaptMode update(int size, int l, int r) {
1339                 return r - l <= size ? this : FIXED;
1340             }
1341         };
1342 
1343         /**
1344          * Checks if sample-mode is enabled.
1345          *
1346          * @return true if sample mode is enabled
1347          */
1348         abstract boolean isSampleMode();
1349 
1350         /**
1351          * Checks if adaption is enabled.
1352          *
1353          * @return true if adaption is enabled
1354          */
1355         abstract boolean isAdapt();
1356 
1357         /**
1358          * Update the state using the expected {@code size} of the partition and the actual size.
1359          *
1360          * <p>For convenience this accepts the range {@code [l, r]} instead of the actual size.
1361          * The implementation may use the range or ignore it.
1362          *
1363          * @param size Expected size of the partition.
1364          * @param l Lower bound (inclusive).
1365          * @param r Upper bound (inclusive).
1366          * @return the new state
1367          */
1368         abstract AdaptMode update(int size, int l, int r);
1369     }
1370 
1371     /**
1372      * Constructor with defaults.
1373      */
1374     Partition() {
1375         this(PIVOTING_STRATEGY, DUAL_PIVOTING_STRATEGY, MIN_QUICKSELECT_SIZE,
1376             EDGESELECT_CONSTANT, SUBSAMPLING_SIZE);
1377     }
1378 
1379     /**
1380      * Constructor with specified pivoting strategy and quickselect size.
1381      *
1382      * <p>Used to test single-pivot quicksort.
1383      *
1384      * @param pivotingStrategy Pivoting strategy to use.
1385      * @param minQuickSelectSize Minimum size for quickselect.
1386      */
1387     Partition(PivotingStrategy pivotingStrategy, int minQuickSelectSize) {
1388         this(pivotingStrategy, DUAL_PIVOTING_STRATEGY, minQuickSelectSize,
1389             EDGESELECT_CONSTANT, SUBSAMPLING_SIZE);
1390     }
1391 
1392     /**
1393      * Constructor with specified pivoting strategy and quickselect size.
1394      *
1395      * <p>Used to test dual-pivot quicksort.
1396      *
1397      * @param dualPivotingStrategy Dual pivoting strategy to use.
1398      * @param minQuickSelectSize Minimum size for quickselect.
1399      */
1400     Partition(DualPivotingStrategy dualPivotingStrategy, int minQuickSelectSize) {
1401         this(PIVOTING_STRATEGY, dualPivotingStrategy, minQuickSelectSize,
1402             EDGESELECT_CONSTANT, SUBSAMPLING_SIZE);
1403     }
1404 
1405     /**
1406      * Constructor with specified pivoting strategy; quickselect size; and edgeselect configuration.
1407      *
1408      * <p>Used to test single-pivot quickselect.
1409      *
1410      * @param pivotingStrategy Pivoting strategy to use.
1411      * @param minQuickSelectSize Minimum size for quickselect.
1412      * @param edgeSelectConstant Length constant used for edge select distance from end threshold.
1413      * @param subSamplingSize Size threshold to use sub-sampling for single-pivot selection.
1414      */
1415     Partition(PivotingStrategy pivotingStrategy,
1416         int minQuickSelectSize, int edgeSelectConstant, int subSamplingSize) {
1417         this(pivotingStrategy, DUAL_PIVOTING_STRATEGY, minQuickSelectSize, edgeSelectConstant,
1418             subSamplingSize);
1419     }
1420 
1421     /**
1422      * Constructor with specified dual-pivoting strategy; quickselect size; and edgeselect configuration.
1423      *
1424      * <p>Used to test dual-pivot quickselect.
1425      *
1426      * @param dualPivotingStrategy Dual pivoting strategy to use.
1427      * @param minQuickSelectSize Minimum size for quickselect.
1428      * @param edgeSelectConstant Length constant used for edge select distance from end threshold.
1429      */
1430     Partition(DualPivotingStrategy dualPivotingStrategy,
1431         int minQuickSelectSize, int edgeSelectConstant) {
1432         this(PIVOTING_STRATEGY, dualPivotingStrategy, minQuickSelectSize,
1433             edgeSelectConstant, SUBSAMPLING_SIZE);
1434     }
1435 
1436     /**
1437      * Constructor with specified pivoting strategy; quickselect size; and edgeselect configuration.
1438      *
1439      * @param pivotingStrategy Pivoting strategy to use.
1440      * @param dualPivotingStrategy Dual pivoting strategy to use.
1441      * @param minQuickSelectSize Minimum size for quickselect.
1442      * @param edgeSelectConstant Length constant used for distance from end threshold.
1443      * @param subSamplingSize Size threshold to use sub-sampling for single-pivot selection.
1444      */
1445     Partition(PivotingStrategy pivotingStrategy, DualPivotingStrategy dualPivotingStrategy,
1446         int minQuickSelectSize, int edgeSelectConstant, int subSamplingSize) {
1447         this.pivotingStrategy = pivotingStrategy;
1448         this.dualPivotingStrategy = dualPivotingStrategy;
1449         this.minQuickSelectSize = minQuickSelectSize;
1450         this.edgeSelectConstant = edgeSelectConstant;
1451         this.subSamplingSize = subSamplingSize;
1452         // Default strategies
1453         setSPStrategy(SP_STRATEGY);
1454         setEdgeSelectStrategy(EDGE_STRATEGY);
1455         setStopperStrategy(STOPPER_STRATEGY);
1456         setExpandStrategy(EXPAND_STRATEGY);
1457         setLinearStrategy(LINEAR_STRATEGY);
1458         // Called to initialise state
1459         setControlFlags(0);
1460     }
1461 
1462     /**
1463      * Sets the single-pivot partition strategy.
1464      *
1465      * @param v Value.
1466      * @return {@code this} for chaining
1467      */
1468     Partition setSPStrategy(SPStrategy v) {
1469         switch (v) {
1470         case BM:
1471             spFunction = Partition::partitionBM;
1472             break;
1473         case DNF1:
1474             spFunction = Partition::partitionDNF1;
1475             break;
1476         case DNF2:
1477             spFunction = Partition::partitionDNF2;
1478             break;
1479         case DNF3:
1480             spFunction = Partition::partitionDNF3;
1481             break;
1482         case KBM:
1483             spFunction = Partition::partitionKBM;
1484             break;
1485         case SBM:
1486             spFunction = Partition::partitionSBM;
1487             break;
1488         case SP:
1489             spFunction = Partition::partitionSP;
1490             break;
1491         default:
1492             throw new IllegalArgumentException("Unknown single-pivot strategy: " + v);
1493         }
1494         return this;
1495     }
1496 
1497     /**
1498      * Sets the single-pivot partition expansion strategy.
1499      *
1500      * @param v Value.
1501      * @return {@code this} for chaining
1502      */
1503     Partition setExpandStrategy(ExpandStrategy v) {
1504         switch (v) {
1505         case PER:
1506             // Partition the entire range using the single-pivot partition strategy
1507             expandFunction = (a, left, right, start, end, pivot0, pivot1, upper) ->
1508                 spFunction.partition(a, left, right, (pivot0 + pivot1) >>> 1, upper);
1509             break;
1510         case T1:
1511             expandFunction = Partition::expandPartitionT1;
1512             break;
1513         case B1:
1514             expandFunction = Partition::expandPartitionB1;
1515             break;
1516         case T2:
1517             expandFunction = Partition::expandPartitionT2;
1518             break;
1519         case B2:
1520             expandFunction = Partition::expandPartitionB2;
1521             break;
1522         default:
1523             throw new IllegalArgumentException("Unknown expand strategy: " + v);
1524         }
1525         return this;
1526     }
1527 
1528     /**
1529      * Sets the single-pivot linear select strategy.
1530      *
1531      * <p>Note: The linear select strategy will partition remaining range after computing
1532      * a pivot from a sample by single-pivot partitioning or by expanding the partition
1533      * (see {@link #setExpandStrategy(ExpandStrategy)}).
1534      *
1535      * @param v Value.
1536      * @return {@code this} for chaining
1537      * @see #setExpandStrategy(ExpandStrategy)
1538      */
1539     Partition setLinearStrategy(LinearStrategy v) {
1540         switch (v) {
1541         case BFPRT:
1542             linearSpFunction = this::linearBFPRTBaseline;
1543             break;
1544         case RS:
1545             linearSpFunction = this::linearRepeatedStepBaseline;
1546             break;
1547         case BFPRT_IM:
1548             noSamplingAdapt = MapDistance.MEDIAN;
1549             linearSpFunction = this::linearBFPRTImproved;
1550             break;
1551         case BFPRTA:
1552             // Here we re-use the same method as the only difference is adaption of k
1553             noSamplingAdapt = MapDistance.ADAPT;
1554             linearSpFunction = this::linearBFPRTImproved;
1555             break;
1556         case RS_IM:
1557             noSamplingAdapt = MapDistance.MEDIAN;
1558             linearSpFunction = this::linearRepeatedStepImproved;
1559             break;
1560         case RSA:
1561             // Here we re-use the same method as the only difference is adaption of k
1562             noSamplingAdapt = MapDistance.ADAPT;
1563             linearSpFunction = this::linearRepeatedStepImproved;
1564             break;
1565         default:
1566             throw new IllegalArgumentException("Unknown linear strategy: " + v);
1567         }
1568         return this;
1569     }
1570 
1571     /**
1572      * Sets the edge-select strategy.
1573      *
1574      * @param v Value.
1575      * @return {@code this} for chaining
1576      */
1577     Partition setEdgeSelectStrategy(EdgeSelectStrategy v) {
1578         switch (v) {
1579         case ESH:
1580             edgeSelection = Partition::heapSelectRange;
1581             break;
1582         case ESH2:
1583             edgeSelection = Partition::heapSelectRange2;
1584             break;
1585         case ESS:
1586             edgeSelection = Partition::sortSelectRange;
1587             break;
1588         case ESS2:
1589             edgeSelection = Partition::sortSelectRange2;
1590             break;
1591         default:
1592             throw new IllegalArgumentException("Unknown edge select: " + v);
1593         }
1594         return this;
1595     }
1596 
1597     /**
1598      * Sets the stopper strategy (when quickselect progress is poor).
1599      *
1600      * @param v Value.
1601      * @return {@code this} for chaining
1602      */
1603     Partition setStopperStrategy(StopperStrategy v) {
1604         switch (v) {
1605         case SSH:
1606             stopperSelection = Partition::heapSelectRange;
1607             break;
1608         case SSH2:
1609             stopperSelection = Partition::heapSelectRange2;
1610             break;
1611         case SLS:
1612             // Linear select does not match the interface as it:
1613             // - requires the single-pivot partition function
1614             // - uses a bounds array to allow minimising the partition region size after pivot selection
1615             stopperSelection = (a, l, r, ka, kb) -> linearSelect(getSPFunction(),
1616                 a, l, r, ka, kb, new int[2]);
1617             break;
1618         case SQA:
1619             // Linear select does not match the interface as it:
1620             // - uses a bounds array to allow minimising the partition region size after pivot selection
1621             // - uses control flags to set sampling mode on/off
1622             stopperSelection = (a, l, r, ka, kb) -> quickSelectAdaptive(a, l, r, ka, kb, new int[1],
1623                 adaptMode);
1624             break;
1625         default:
1626             throw new IllegalArgumentException("Unknown stopper: " + v);
1627         }
1628         return this;
1629     }
1630 
1631     /**
1632      * Sets the key strategy.
1633      *
1634      * @param v Value.
1635      * @return {@code this} for chaining
1636      */
1637     Partition setKeyStrategy(KeyStrategy v) {
1638         this.keyStrategy = v;
1639         return this;
1640     }
1641 
1642     /**
1643      * Sets the paired key strategy.
1644      *
1645      * @param v Value.
1646      * @return {@code this} for chaining
1647      */
1648     Partition setPairedKeyStrategy(PairedKeyStrategy v) {
1649         this.pairedKeyStrategy = v;
1650         return this;
1651     }
1652 
1653     /**
1654      * Sets the recursion multiple.
1655      *
1656      * @param v Value.
1657      * @return {@code this} for chaining
1658      */
1659     Partition setRecursionMultiple(double v) {
1660         this.recursionMultiple = v;
1661         return this;
1662     }
1663 
1664     /**
1665      * Sets the recursion constant.
1666      *
1667      * @param v Value.
1668      * @return {@code this} for chaining
1669      */
1670     Partition setRecursionConstant(int v) {
1671         this.recursionConstant = v;
1672         return this;
1673     }
1674 
1675     /**
1676      * Sets the compression for a {@link CompressedIndexSet}.
1677      *
1678      * @param v Value.
1679      * @return {@code this} for chaining
1680      */
1681     Partition setCompression(int v) {
1682         if (v < 1 || v > Integer.SIZE - 1) {
1683             throw new IllegalArgumentException("Bad compression: " + v);
1684         }
1685         this.compression = v;
1686         return this;
1687     }
1688 
1689     /**
1690      * Sets the control flags for Floyd-Rivest sub-sampling.
1691      *
1692      * @param v Value.
1693      * @return {@code this} for chaining
1694      */
1695     Partition setControlFlags(int v) {
1696         this.controlFlags = v;
1697         // Quickselect adaptive requires functions to map k to the sample.
1698         // These functions must be set based on the margins in the repeated step method.
1699         // These will differ due to the implementation and whether the first step is
1700         // skipped (sampling mode on).
1701         if ((v & FLAG_QA_FAR_STEP_ADAPT_ORIGINAL) != 0) {
1702             // Use the same mapping for all repeated step functions.
1703             // This is the original behaviour from Alexandrescu (2016).
1704             samplingAdapt = samplingEdgeAdapt = noSamplingAdapt = noSamplingEdgeAdapt = MapDistance.ADAPT;
1705         } else {
1706             // Default behaviour. This optimises the adaption for the algorithm.
1707             samplingAdapt = MapDistance.ADAPT;
1708             if ((v & FLAG_QA_FAR_STEP) != 0) {
1709                 // Switches the far-step to minimum-of-4, median-of-3.
1710                 // When sampling mode is on all samples are from median-of-3 and we
1711                 // use the same adaption.
1712                 samplingEdgeAdapt = MapDistance.ADAPT;
1713             } else {
1714                 // Original far-step of lower-median-of-4, minimum-of-3
1715                 // When sampling mode is on the sample is a minimum-of-3. This halves the
1716                 // lower margin from median-of-3. Change the adaption to avoid
1717                 // a tiny lower margin (and possibility of k falling in a very large partition).
1718                 // Note: The only way we can ensure that k is inside the lower margin is by using
1719                 // (r-l) as the sample k. Compromise by using the midpoint for a 50% chance that
1720                 // k is inside the lower margin.
1721                 samplingEdgeAdapt = MapDistance.MEDIAN;
1722             }
1723             noSamplingAdapt = MapDistance.ADAPT;
1724             // Force edge margin to contain the target index
1725             noSamplingEdgeAdapt = MapDistance.EDGE_ADAPT;
1726         }
1727         return this;
1728     }
1729 
1730     /**
1731      * Sets the size for sortselect for the linearselect algorithm.
1732      * Must be above 0 for the algorithm to return (else an infinite loop occurs).
1733      * The minimum size required depends on the expand partition function, and the
1734      * same size relative to the range (e.g. 1/5, 1/9 or 1/12).
1735      *
1736      * @param v Value.
1737      * @return {@code this} for chaining
1738      */
1739     Partition setLinearSortSelectSize(int v) {
1740         if (v < 1) {
1741             throw new IllegalArgumentException("Bad linear sortselect size: " + v);
1742         }
1743         this.linearSortSelectSize = v;
1744         return this;
1745     }
1746 
1747     /**
1748      * Sets the quickselect adaptive mode.
1749      *
1750      * @param v Value.
1751      * @return {@code this} for chaining
1752      */
1753     Partition setAdaptMode(AdaptMode v) {
1754         this.adaptMode = v;
1755         return this;
1756     }
1757 
1758     /**
1759      * Sets the recursion consumer. This is called with the value of the recursion
1760      * counter immediately before the introselect routine returns. It is used to
1761      * analyse recursion depth on various input data.
1762      *
1763      * @param v Value.
1764      */
1765     void setRecursionConsumer(IntConsumer v) {
1766         this.recursionConsumer = Objects.requireNonNull(v);
1767     }
1768 
1769     /**
1770      * Gets the single-pivot partition function.
1771      *
1772      * @return the single-pivot partition function
1773      */
1774     SPEPartition getSPFunction() {
1775         return spFunction;
1776     }
1777 
1778     /**
1779      * Configure the properties used by the static quickselect adaptive algorithm.
1780      * The increment is used to update the current mode when the margins are not achieved.
1781      *
1782      * @param mode Initial mode.
1783      * @param increment Flag increment
1784      */
1785     static void configureQaAdaptive(int mode, int increment) {
1786         qaMode = mode;
1787         qaIncrement = increment;
1788     }
1789 
1790     /**
1791      * Move the minimum value to the start of the range.
1792      *
1793      * <p>Note: Respects the ordering of signed zeros.
1794      *
1795      * <p>Assumes {@code left <= right}.
1796      *
1797      * @param data Data.
1798      * @param left Lower bound (inclusive).
1799      * @param right Upper bound (inclusive).
1800      */
1801     static void selectMin(double[] data, int left, int right) {
1802         selectMinIgnoreZeros(data, left, right);
1803         // Edge-case: if min was 0.0, check for a -0.0 above and swap.
1804         if (data[left] == 0) {
1805             minZero(data, left, right);
1806         }
1807     }
1808 
1809     /**
1810      * Move the maximum value to the end of the range.
1811      *
1812      * <p>Note: Respects the ordering of signed zeros.
1813      *
1814      * <p>Assumes {@code left <= right}.
1815      *
1816      * @param data Data.
1817      * @param left Lower bound (inclusive).
1818      * @param right Upper bound (inclusive).
1819      */
1820     static void selectMax(double[] data, int left, int right) {
1821         selectMaxIgnoreZeros(data, left, right);
1822         // Edge-case: if max was -0.0, check for a 0.0 below and swap.
1823         if (data[right] == 0) {
1824             maxZero(data, left, right);
1825         }
1826     }
1827 
1828     /**
1829      * Place a negative signed zero at {@code left} before any positive signed zero in the range,
1830      * {@code -0.0 < 0.0}.
1831      *
1832      * <p>Warning: Only call when {@code data[left]} is zero.
1833      *
1834      * @param data Data.
1835      * @param left Lower bound (inclusive).
1836      * @param right Upper bound (inclusive).
1837      */
1838     private static void minZero(double[] data, int left, int right) {
1839         // Assume data[left] is zero and check the sign bit
1840         if (Double.doubleToRawLongBits(data[left]) >= 0) {
1841             // Check for a -0.0 above and swap.
1842             // We only require 1 swap as this is not a full sort of zeros.
1843             for (int k = left; ++k <= right;) {
1844                 if (data[k] == 0 && Double.doubleToRawLongBits(data[k]) < 0) {
1845                     data[k] = 0.0;
1846                     data[left] = -0.0;
1847                     break;
1848                 }
1849             }
1850         }
1851     }
1852 
1853     /**
1854      * Place a positive signed zero at {@code right} after any negative signed zero in the range,
1855      * {@code -0.0 < 0.0}.
1856      *
1857      * <p>Warning: Only call when {@code data[right]} is zero.
1858      *
1859      * @param data Data.
1860      * @param left Lower bound (inclusive).
1861      * @param right Upper bound (inclusive).
1862      */
1863     private static void maxZero(double[] data, int left, int right) {
1864         // Assume data[right] is zero and check the sign bit
1865         if (Double.doubleToRawLongBits(data[right]) < 0) {
1866             // Check for a 0.0 below and swap.
1867             // We only require 1 swap as this is not a full sort of zeros.
1868             for (int k = right; --k >= left;) {
1869                 if (data[k] == 0 && Double.doubleToRawLongBits(data[k]) >= 0) {
1870                     data[k] = -0.0;
1871                     data[right] = 0.0;
1872                     break;
1873                 }
1874             }
1875         }
1876     }
1877 
1878     /**
1879      * Move the minimum value to the start of the range.
1880      *
1881      * <p>Assumes {@code left <= right}.
1882      *
1883      * @param data Data.
1884      * @param left Lower bound (inclusive).
1885      * @param right Upper bound (inclusive).
1886      */
1887     static void selectMinIgnoreZeros(double[] data, int left, int right) {
1888         // Mitigate worst case performance on descending data by backward sweep
1889         double min = data[left];
1890         for (int i = right + 1; --i > left;) {
1891             final double v = data[i];
1892             if (v < min) {
1893                 data[i] = min;
1894                 min = v;
1895             }
1896         }
1897         data[left] = min;
1898     }
1899 
1900     /**
1901      * Move the two smallest values to the start of the range.
1902      *
1903      * <p>Assumes {@code left < right}.
1904      *
1905      * @param data Data.
1906      * @param left Lower bound (inclusive).
1907      * @param right Upper bound (inclusive).
1908      */
1909     static void selectMin2IgnoreZeros(double[] data, int left, int right) {
1910         double min1 = data[left + 1];
1911         if (min1 < data[left]) {
1912             min1 = data[left];
1913             data[left] = data[left + 1];
1914         }
1915         // Mitigate worst case performance on descending data by backward sweep
1916         for (int i = right + 1, end = left + 1; --i > end;) {
1917             final double v = data[i];
1918             if (v < min1) {
1919                 data[i] = min1;
1920                 if (v < data[left]) {
1921                     min1 = data[left];
1922                     data[left] = v;
1923                 } else {
1924                     min1 = v;
1925                 }
1926             }
1927         }
1928         data[left + 1] = min1;
1929     }
1930 
1931     /**
1932      * Move the maximum value to the end of the range.
1933      *
1934      * <p>Assumes {@code left <= right}.
1935      *
1936      * @param data Data.
1937      * @param left Lower bound (inclusive).
1938      * @param right Upper bound (inclusive).
1939      */
1940     static void selectMaxIgnoreZeros(double[] data, int left, int right) {
1941         // Mitigate worst case performance on descending data by backward sweep
1942         double max = data[right];
1943         for (int i = left - 1; ++i < right;) {
1944             final double v = data[i];
1945             if (v > max) {
1946                 data[i] = max;
1947                 max = v;
1948             }
1949         }
1950         data[right] = max;
1951     }
1952 
1953     /**
1954      * Move the two largest values to the end of the range.
1955      *
1956      * <p>Assumes {@code left < right}.
1957      *
1958      * @param data Data.
1959      * @param left Lower bound (inclusive).
1960      * @param right Upper bound (inclusive).
1961      */
1962     static void selectMax2IgnoreZeros(double[] data, int left, int right) {
1963         double max1 = data[right - 1];
1964         if (max1 > data[right]) {
1965             max1 = data[right];
1966             data[right] = data[right - 1];
1967         }
1968         // Mitigate worst case performance on descending data by backward sweep
1969         for (int i = left - 1, end = right - 1; ++i < end;) {
1970             final double v = data[i];
1971             if (v > max1) {
1972                 data[i] = max1;
1973                 if (v > data[right]) {
1974                     max1 = data[right];
1975                     data[right] = v;
1976                 } else {
1977                     max1 = v;
1978                 }
1979             }
1980         }
1981         data[right - 1] = max1;
1982     }
1983 
1984     /**
1985      * Sort the elements using a heap sort algorithm.
1986      *
1987      * @param a Data array to use to find out the K<sup>th</sup> value.
1988      * @param left Lower bound (inclusive).
1989      * @param right Upper bound (inclusive).
1990      */
1991     static void heapSort(double[] a, int left, int right) {
1992         // We could make a choice here between select left or right
1993         heapSelectLeft(a, left, right, right, right - left);
1994     }
1995 
1996     /**
1997      * Partition the elements {@code ka} and {@code kb} using a heap select algorithm. It
1998      * is assumed {@code left <= ka <= kb <= right}. Any range between the two elements is
1999      * not ensured to be sorted.
2000      *
2001      * <p>If there is no range between the two point, i.e. {@code ka == kb} or
2002      * {@code ka + 1 == kb}, it is preferred to use
2003      * {@link #heapSelectRange(double[], int, int, int, int)}. The result is the same but
2004      * the decision choice is simpler for the range function.
2005      *
2006      * @param a Data array to use to find out the K<sup>th</sup> value.
2007      * @param left Lower bound (inclusive).
2008      * @param right Upper bound (inclusive).
2009      * @param ka Lower index to select.
2010      * @param kb Upper index to select.
2011      * @see #heapSelectRange(double[], int, int, int, int)
2012      */
2013     static void heapSelectPair(double[] a, int left, int right, int ka, int kb) {
2014         // Avoid the overhead of heap select on tiny data (supports right <= left).
2015         if (right - left < MIN_HEAPSELECT_SIZE) {
2016             Sorting.sort(a, left, right);
2017             return;
2018         }
2019         // Call the appropriate heap partition function based on
2020         // building a heap up to 50% of the length
2021         // |l|-----|ka|--------|kb|------|r|
2022         //  ---d1----
2023         //                      -----d3----
2024         //  ---------d2----------
2025         //          ----------d4-----------
2026         final int d1 = ka - left;
2027         final int d2 = kb - left;
2028         final int d3 = right - kb;
2029         final int d4 = right - ka;
2030         if (d1 + d3 < Math.min(d2, d4)) {
2031             // Partition both ends.
2032             // Note: Not possible if ka == kb.
2033             // s1 + s3 == r - l and >= than the smallest
2034             // distance to one of the ends
2035             heapSelectLeft(a, left, right, ka, 0);
2036             // Repeat for the other side above ka
2037             heapSelectRight(a, ka + 1, right, kb, 0);
2038         } else if (d2 < d4) {
2039             heapSelectLeft(a, left, right, kb, kb - ka);
2040         } else {
2041             // s4
2042             heapSelectRight(a, left, right, ka, kb - ka);
2043         }
2044     }
2045 
2046     /**
2047      * Partition the elements between {@code ka} and {@code kb} using a heap select
2048      * algorithm. It is assumed {@code left <= ka <= kb <= right}.
2049      *
2050      * @param a Data array to use to find out the K<sup>th</sup> value.
2051      * @param left Lower bound (inclusive).
2052      * @param right Upper bound (inclusive).
2053      * @param ka Lower index to select.
2054      * @param kb Upper index to select.
2055      * @see #heapSelectPair(double[], int, int, int, int)
2056      */
2057     static void heapSelectRange(double[] a, int left, int right, int ka, int kb) {
2058         // Combine the test for right <= left with
2059         // avoiding the overhead of heap select on tiny data.
2060         if (right - left < MIN_HEAPSELECT_SIZE) {
2061             Sorting.sort(a, left, right);
2062             return;
2063         }
2064         // Call the appropriate heap partition function based on
2065         // building a heap up to 50% of the length
2066         // |l|-----|ka|--------|kb|------|r|
2067         // |---------d1-----------|
2068         //         |----------d2-----------|
2069         // Note: Optimisation for small heap size (n=1,2) is negligible.
2070         // The main overhead is the test for insertion against the current top of the heap
2071         // which grows increasingly unlikely as the range is scanned.
2072         if (kb - left < right - ka) {
2073             heapSelectLeft(a, left, right, kb, kb - ka);
2074         } else {
2075             heapSelectRight(a, left, right, ka, kb - ka);
2076         }
2077     }
2078 
2079     /**
2080      * Partition the minimum {@code n} elements below {@code k} where
2081      * {@code n = k - left + 1}. Uses a heap select algorithm.
2082      *
2083      * <p>Works with any {@code k} in the range {@code left <= k <= right}
2084      * and can be used to perform a full sort of the range below {@code k}
2085      * using the {@code count} parameter.
2086      *
2087      * <p>For best performance this should be called with
2088      * {@code k - left < right - k}, i.e.
2089      * to partition a value in the lower half of the range.
2090      *
2091      * @param a Data array to use to find out the K<sup>th</sup> value.
2092      * @param left Lower bound (inclusive).
2093      * @param right Upper bound (inclusive).
2094      * @param k Index to select.
2095      * @param count Size of range to sort below k.
2096      */
2097     static void heapSelectLeft(double[] a, int left, int right, int k, int count) {
2098         // Create a max heap in-place in [left, k], rooted at a[left] = max
2099         // |l|-max-heap-|k|--------------|
2100         // Build the heap using Floyd's heap-construction algorithm for heap size n.
2101         // Start at parent of the last element in the heap (k),
2102         // i.e. start = parent(n-1) : parent(c) = floor((c - 1) / 2) : c = k - left
2103         int end = k + 1;
2104         for (int p = left + ((k - left - 1) >> 1); p >= left; p--) {
2105             maxHeapSiftDown(a, a[p], p, left, end);
2106         }
2107         // Scan the remaining data and insert
2108         // Mitigate worst case performance on descending data by backward sweep
2109         double max = a[left];
2110         for (int i = right + 1; --i > k;) {
2111             final double v = a[i];
2112             if (v < max) {
2113                 a[i] = max;
2114                 maxHeapSiftDown(a, v, left, left, end);
2115                 max = a[left];
2116             }
2117         }
2118 
2119         // To partition elements k (and below) move the top of the heap to the position
2120         // immediately after the end of the reduced size heap; the previous end
2121         // of the heap [k] is placed at the top
2122         // |l|-max-heap-|k|--------------|
2123         //  |  <-swap->  |
2124         // The heap can be restored by sifting down the new top.
2125 
2126         // Always require the top 1
2127         a[left] = a[k];
2128         a[k] = max;
2129 
2130         if (count > 0) {
2131             --end;
2132             // Sifting limited to heap size of 2 (i.e. don't sift heap n==1)
2133             for (int c = Math.min(count, end - left - 1); --c >= 0;) {
2134                 maxHeapSiftDown(a, a[left], left, left, end--);
2135                 // Move top of heap to the sorted end
2136                 max = a[left];
2137                 a[left] = a[end];
2138                 a[end] = max;
2139             }
2140         }
2141     }
2142 
2143     /**
2144      * Sift the element down the max heap.
2145      *
2146      * <p>Assumes {@code root <= p < end}, i.e. the max heap is above root.
2147      *
2148      * @param a Heap data.
2149      * @param v Value to sift.
2150      * @param p Start position.
2151      * @param root Root of the heap.
2152      * @param end End of the heap (exclusive).
2153      */
2154     private static void maxHeapSiftDown(double[] a, double v, int p, int root, int end) {
2155         // child2 = root + 2 * (parent - root) + 2
2156         //        = 2 * parent - root + 2
2157         while (true) {
2158             // Right child
2159             int c = (p << 1) - root + 2;
2160             if (c > end) {
2161                 // No left child
2162                 break;
2163             }
2164             // Use the left child if right doesn't exist, or it is greater
2165             if (c == end || a[c] < a[c - 1]) {
2166                 --c;
2167             }
2168             if (v >= a[c]) {
2169                 // Parent greater than largest child - done
2170                 break;
2171             }
2172             // Swap and descend
2173             a[p] = a[c];
2174             p = c;
2175         }
2176         a[p] = v;
2177     }
2178 
2179     /**
2180      * Partition the maximum {@code n} elements above {@code k} where
2181      * {@code n = right - k + 1}. Uses a heap select algorithm.
2182      *
2183      * <p>Works with any {@code k} in the range {@code left <= k <= right}
2184      * and can be used to perform a full sort of the range above {@code k}
2185      * using the {@code count} parameter.
2186      *
2187      * <p>For best performance this should be called with
2188      * {@code k - left > right - k}, i.e.
2189      * to partition a value in the upper half of the range.
2190      *
2191      * @param a Data array to use to find out the K<sup>th</sup> value.
2192      * @param left Lower bound (inclusive).
2193      * @param right Upper bound (inclusive).
2194      * @param k Index to select.
2195      * @param count Size of range to sort below k.
2196      */
2197     static void heapSelectRight(double[] a, int left, int right, int k, int count) {
2198         // Create a min heap in-place in [k, right], rooted at a[right] = min
2199         // |--------------|k|-min-heap-|r|
2200         // Build the heap using Floyd's heap-construction algorithm for heap size n.
2201         // Start at parent of the last element in the heap (k),
2202         // i.e. start = parent(n-1) : parent(c) = floor((c - 1) / 2) : c = right - k
2203         int end = k - 1;
2204         for (int p = right - ((right - k - 1) >> 1); p <= right; p++) {
2205             minHeapSiftDown(a, a[p], p, right, end);
2206         }
2207         // Scan the remaining data and insert
2208         // Mitigate worst case performance on descending data by backward sweep
2209         double min = a[right];
2210         for (int i = left - 1; ++i < k;) {
2211             final double v = a[i];
2212             if (v > min) {
2213                 a[i] = min;
2214                 minHeapSiftDown(a, v, right, right, end);
2215                 min = a[right];
2216             }
2217         }
2218 
2219         // To partition elements k (and above) move the top of the heap to the position
2220         // immediately before the end of the reduced size heap; the previous end
2221         // of the heap [k] is placed at the top.
2222         // |--------------|k|-min-heap-|r|
2223         //                 |  <-swap->  |
2224         // The heap can be restored by sifting down the new top.
2225 
2226         // Always require the top 1
2227         a[right] = a[k];
2228         a[k] = min;
2229 
2230         if (count > 0) {
2231             ++end;
2232             // Sifting limited to heap size of 2 (i.e. don't sift heap n==1)
2233             for (int c = Math.min(count, right - end - 1); --c >= 0;) {
2234                 minHeapSiftDown(a, a[right], right, right, end++);
2235                 // Move top of heap to the sorted end
2236                 min = a[right];
2237                 a[right] = a[end];
2238                 a[end] = min;
2239             }
2240         }
2241     }
2242 
2243     /**
2244      * Sift the element down the min heap.
2245      *
2246      * <p>Assumes {@code root >= p > end}, i.e. the max heap is below root.
2247      *
2248      * @param a Heap data.
2249      * @param v Value to sift.
2250      * @param p Start position.
2251      * @param root Root of the heap.
2252      * @param end End of the heap (exclusive).
2253      */
2254     private static void minHeapSiftDown(double[] a, double v, int p, int root, int end) {
2255         // child2 = root - 2 * (root - parent) - 2
2256         //        = 2 * parent - root - 2
2257         while (true) {
2258             // Right child
2259             int c = (p << 1) - root - 2;
2260             if (c < end) {
2261                 // No left child
2262                 break;
2263             }
2264             // Use the left child if right doesn't exist, or it is less
2265             if (c == end || a[c] > a[c + 1]) {
2266                 ++c;
2267             }
2268             if (v <= a[c]) {
2269                 // Parent less than smallest child - done
2270                 break;
2271             }
2272             // Swap and descend
2273             a[p] = a[c];
2274             p = c;
2275         }
2276         a[p] = v;
2277     }
2278 
2279     /**
2280      * Partition the elements between {@code ka} and {@code kb} using a heap select
2281      * algorithm. It is assumed {@code left <= ka <= kb <= right}.
2282      *
2283      * <p>Differs from {@link #heapSelectRange(double[], int, int, int, int)} by using
2284      * a different extraction of the sorted elements from the heap.
2285      *
2286      * @param a Data array to use to find out the K<sup>th</sup> value.
2287      * @param left Lower bound (inclusive).
2288      * @param right Upper bound (inclusive).
2289      * @param ka Lower index to select.
2290      * @param kb Upper index to select.
2291      * @see #heapSelectPair(double[], int, int, int, int)
2292      */
2293     static void heapSelectRange2(double[] a, int left, int right, int ka, int kb) {
2294         // Combine the test for right <= left with
2295         // avoiding the overhead of heap select on tiny data.
2296         if (right - left < MIN_HEAPSELECT_SIZE) {
2297             Sorting.sort(a, left, right);
2298             return;
2299         }
2300         // Use the smallest heap
2301         if (kb - left < right - ka) {
2302             heapSelectLeft2(a, left, right, ka, kb);
2303         } else {
2304             heapSelectRight2(a, left, right, ka, kb);
2305         }
2306     }
2307 
2308     /**
2309      * Partition the elements between {@code ka} and {@code kb} using a heap select
2310      * algorithm. It is assumed {@code left <= ka <= kb <= right}.
2311      *
2312      * <p>For best performance this should be called with {@code k} in the lower
2313      * half of the range.
2314      *
2315      * <p>Differs from {@link #heapSelectLeft(double[], int, int, int, int)} by using
2316      * a different extraction of the sorted elements from the heap.
2317      *
2318      * @param a Data array to use to find out the K<sup>th</sup> value.
2319      * @param left Lower bound (inclusive).
2320      * @param right Upper bound (inclusive).
2321      * @param ka Lower index to select.
2322      * @param kb Upper index to select.
2323      */
2324     static void heapSelectLeft2(double[] a, int left, int right, int ka, int kb) {
2325         // Create a max heap in-place in [left, k], rooted at a[left] = max
2326         // |l|-max-heap-|k|--------------|
2327         // Build the heap using Floyd's heap-construction algorithm for heap size n.
2328         // Start at parent of the last element in the heap (k),
2329         // i.e. start = parent(n-1) : parent(c) = floor((c - 1) / 2) : c = k - left
2330         int end = kb + 1;
2331         for (int p = left + ((kb - left - 1) >> 1); p >= left; p--) {
2332             maxHeapSiftDown(a, a[p], p, left, end);
2333         }
2334         // Scan the remaining data and insert
2335         // Mitigate worst case performance on descending data by backward sweep
2336         double max = a[left];
2337         for (int i = right + 1; --i > kb;) {
2338             final double v = a[i];
2339             if (v < max) {
2340                 a[i] = max;
2341                 maxHeapSiftDown(a, v, left, left, end);
2342                 max = a[left];
2343             }
2344         }
2345         // Partition [ka, kb]
2346         // |l|-max-heap-|k|--------------|
2347         //  |  <-swap->  |   then sift down reduced size heap
2348         // Avoid sifting heap of size 1
2349         final int last = Math.max(left, ka - 1);
2350         while (--end > last) {
2351             maxHeapSiftDown(a, a[end], left, left, end);
2352             a[end] = max;
2353             max = a[left];
2354         }
2355     }
2356 
2357     /**
2358      * Partition the elements between {@code ka} and {@code kb} using a heap select
2359      * algorithm. It is assumed {@code left <= ka <= kb <= right}.
2360      *
2361      * <p>For best performance this should be called with {@code k} in the upper
2362      * half of the range.
2363      *
2364      * <p>Differs from {@link #heapSelectRight(double[], int, int, int, int)} by using
2365      * a different extraction of the sorted elements from the heap.
2366      *
2367      * @param a Data array to use to find out the K<sup>th</sup> value.
2368      * @param left Lower bound (inclusive).
2369      * @param right Upper bound (inclusive).
2370      * @param ka Lower index to select.
2371      * @param kb Upper index to select.
2372      */
2373     static void heapSelectRight2(double[] a, int left, int right, int ka, int kb) {
2374         // Create a min heap in-place in [k, right], rooted at a[right] = min
2375         // |--------------|k|-min-heap-|r|
2376         // Build the heap using Floyd's heap-construction algorithm for heap size n.
2377         // Start at parent of the last element in the heap (k),
2378         // i.e. start = parent(n-1) : parent(c) = floor((c - 1) / 2) : c = right - k
2379         int end = ka - 1;
2380         for (int p = right - ((right - ka - 1) >> 1); p <= right; p++) {
2381             minHeapSiftDown(a, a[p], p, right, end);
2382         }
2383         // Scan the remaining data and insert
2384         // Mitigate worst case performance on descending data by backward sweep
2385         double min = a[right];
2386         for (int i = left - 1; ++i < ka;) {
2387             final double v = a[i];
2388             if (v > min) {
2389                 a[i] = min;
2390                 minHeapSiftDown(a, v, right, right, end);
2391                 min = a[right];
2392             }
2393         }
2394         // Partition [ka, kb]
2395         // |--------------|k|-min-heap-|r|
2396         //                 |  <-swap->  |   then sift down reduced size heap
2397         // Avoid sifting heap of size 1
2398         final int last = Math.min(right, kb + 1);
2399         while (++end < last) {
2400             minHeapSiftDown(a, a[end], right, right, end);
2401             a[end] = min;
2402             min = a[right];
2403         }
2404     }
2405 
2406     /**
2407      * Partition the elements between {@code ka} and {@code kb} using a sort select
2408      * algorithm. It is assumed {@code left <= ka <= kb <= right}.
2409      *
2410      * @param a Data array to use to find out the K<sup>th</sup> value.
2411      * @param left Lower bound (inclusive).
2412      * @param right Upper bound (inclusive).
2413      * @param ka Lower index to select.
2414      * @param kb Upper index to select.
2415      */
2416     static void sortSelectRange(double[] a, int left, int right, int ka, int kb) {
2417         // Combine the test for right <= left with
2418         // avoiding the overhead of sort select on tiny data.
2419         if (right - left <= MIN_SORTSELECT_SIZE) {
2420             Sorting.sort(a, left, right);
2421             return;
2422         }
2423         // Sort the smallest side
2424         if (kb - left < right - ka) {
2425             sortSelectLeft(a, left, right, kb);
2426         } else {
2427             sortSelectRight(a, left, right, ka);
2428         }
2429     }
2430 
2431     /**
2432      * Partition the minimum {@code n} elements below {@code k} where
2433      * {@code n = k - left + 1}. Uses an insertion sort algorithm.
2434      *
2435      * <p>Works with any {@code k} in the range {@code left <= k <= right}
2436      * and performs a full sort of the range below {@code k}.
2437      *
2438      * <p>For best performance this should be called with
2439      * {@code k - left < right - k}, i.e.
2440      * to partition a value in the lower half of the range.
2441      *
2442      * @param a Data array to use to find out the K<sup>th</sup> value.
2443      * @param left Lower bound (inclusive).
2444      * @param right Upper bound (inclusive).
2445      * @param k Index to select.
2446      */
2447     static void sortSelectLeft(double[] a, int left, int right, int k) {
2448         // Sort
2449         for (int i = left; ++i <= k;) {
2450             final double v = a[i];
2451             // Move preceding higher elements above (if required)
2452             if (v < a[i - 1]) {
2453                 int j = i;
2454                 while (--j >= left && v < a[j]) {
2455                     a[j + 1] = a[j];
2456                 }
2457                 a[j + 1] = v;
2458             }
2459         }
2460         // Scan the remaining data and insert
2461         // Mitigate worst case performance on descending data by backward sweep
2462         double m = a[k];
2463         for (int i = right + 1; --i > k;) {
2464             final double v = a[i];
2465             if (v < m) {
2466                 a[i] = m;
2467                 int j = k;
2468                 while (--j >= left && v < a[j]) {
2469                     a[j + 1] = a[j];
2470                 }
2471                 a[j + 1] = v;
2472                 m = a[k];
2473             }
2474         }
2475     }
2476 
2477     /**
2478      * Partition the maximum {@code n} elements above {@code k} where
2479      * {@code n = right - k + 1}. Uses an insertion sort algorithm.
2480      *
2481      * <p>Works with any {@code k} in the range {@code left <= k <= right}
2482      * and can be used to perform a full sort of the range above {@code k}.
2483      *
2484      * <p>For best performance this should be called with
2485      * {@code k - left > right - k}, i.e.
2486      * to partition a value in the upper half of the range.
2487      *
2488      * @param a Data array to use to find out the K<sup>th</sup> value.
2489      * @param left Lower bound (inclusive).
2490      * @param right Upper bound (inclusive).
2491      * @param k Index to select.
2492      */
2493     static void sortSelectRight(double[] a, int left, int right, int k) {
2494         // Sort
2495         for (int i = right; --i >= k;) {
2496             final double v = a[i];
2497             // Move succeeding lower elements below (if required)
2498             if (v > a[i + 1]) {
2499                 int j = i;
2500                 while (++j <= right && v > a[j]) {
2501                     a[j - 1] = a[j];
2502                 }
2503                 a[j - 1] = v;
2504             }
2505         }
2506         // Scan the remaining data and insert
2507         // Mitigate worst case performance on descending data by backward sweep
2508         double m = a[k];
2509         for (int i = left - 1; ++i < k;) {
2510             final double v = a[i];
2511             if (v > m) {
2512                 a[i] = m;
2513                 int j = k;
2514                 while (++j <= right && v > a[j]) {
2515                     a[j - 1] = a[j];
2516                 }
2517                 a[j - 1] = v;
2518                 m = a[k];
2519             }
2520         }
2521     }
2522 
2523     /**
2524      * Partition the elements between {@code ka} and {@code kb} using a sort select
2525      * algorithm. It is assumed {@code left <= ka <= kb <= right}.
2526      *
2527      * <p>Differs from {@link #sortSelectRange(double[], int, int, int, int)} by using
2528      * a pointer to a position in the sorted array to skip ahead during insertion.
2529      * This extra complexity does not improve performance.
2530      *
2531      * @param a Data array to use to find out the K<sup>th</sup> value.
2532      * @param left Lower bound (inclusive).
2533      * @param right Upper bound (inclusive).
2534      * @param ka Lower index to select.
2535      * @param kb Upper index to select.
2536      */
2537     static void sortSelectRange2(double[] a, int left, int right, int ka, int kb) {
2538         // Combine the test for right <= left with
2539         // avoiding the overhead of sort select on tiny data.
2540         if (right - left <= MIN_SORTSELECT_SIZE) {
2541             Sorting.sort(a, left, right);
2542             return;
2543         }
2544         // Sort the smallest side
2545         if (kb - left < right - ka) {
2546             sortSelectLeft2(a, left, right, kb);
2547         } else {
2548             sortSelectRight2(a, left, right, ka);
2549         }
2550     }
2551 
2552     /**
2553      * Partition the minimum {@code n} elements below {@code k} where
2554      * {@code n = k - left + 1}. Uses an insertion sort algorithm.
2555      *
2556      * <p>Works with any {@code k} in the range {@code left <= k <= right}
2557      * and performs a full sort of the range below {@code k}.
2558      *
2559      * <p>For best performance this should be called with
2560      * {@code k - left < right - k}, i.e.
2561      * to partition a value in the lower half of the range.
2562      *
2563      * <p>Differs from {@link #sortSelectLeft(double[], int, int, int)} by using
2564      * a pointer to a position in the sorted array to skip ahead during insertion.
2565      *
2566      * @param a Data array to use to find out the K<sup>th</sup> value.
2567      * @param left Lower bound (inclusive).
2568      * @param right Upper bound (inclusive).
2569      * @param k Index to select.
2570      */
2571     static void sortSelectLeft2(double[] a, int left, int right, int k) {
2572         // Sort
2573         for (int i = left; ++i <= k;) {
2574             final double v = a[i];
2575             // Move preceding higher elements above (if required)
2576             if (v < a[i - 1]) {
2577                 int j = i;
2578                 while (--j >= left && v < a[j]) {
2579                     a[j + 1] = a[j];
2580                 }
2581                 a[j + 1] = v;
2582             }
2583         }
2584         // Scan the remaining data and insert
2585         // Mitigate worst case performance on descending data by backward sweep
2586         double m = a[k];
2587         // Pointer to a position in the sorted array
2588         final int p = (left + k) >>> 1;
2589         for (int i = right + 1; --i > k;) {
2590             final double v = a[i];
2591             if (v < m) {
2592                 a[i] = m;
2593                 int j = k;
2594                 if (v < a[p]) {
2595                     // Skip ahead
2596                     //System.arraycopy(a, p, a, p + 1, k - p);
2597                     while (j > p) {
2598                         // left index is evaluated before right decrement
2599                         a[j] = a[--j];
2600                     }
2601                     // j == p
2602                     while (--j >= left && v < a[j]) {
2603                         a[j + 1] = a[j];
2604                     }
2605                 } else {
2606                     // No bounds check on left: a[p] <= v < a[k]
2607                     while (v < a[--j]) {
2608                         a[j + 1] = a[j];
2609                     }
2610                 }
2611                 a[j + 1] = v;
2612                 m = a[k];
2613             }
2614         }
2615     }
2616 
2617     /**
2618      * Partition the maximum {@code n} elements above {@code k} where
2619      * {@code n = right - k + 1}. Uses an insertion sort algorithm.
2620      *
2621      * <p>Works with any {@code k} in the range {@code left <= k <= right}
2622      * and can be used to perform a full sort of the range above {@code k}.
2623      *
2624      * <p>For best performance this should be called with
2625      * {@code k - left > right - k}, i.e.
2626      * to partition a value in the upper half of the range.
2627      *
2628      * <p>Differs from {@link #sortSelectRight(double[], int, int, int)} by using
2629      * a pointer to a position in the sorted array to skip ahead during insertion.
2630      *
2631      * @param a Data array to use to find out the K<sup>th</sup> value.
2632      * @param left Lower bound (inclusive).
2633      * @param right Upper bound (inclusive).
2634      * @param k Index to select.
2635      */
2636     static void sortSelectRight2(double[] a, int left, int right, int k) {
2637         // Sort
2638         for (int i = right; --i >= k;) {
2639             final double v = a[i];
2640             // Move succeeding lower elements below (if required)
2641             if (v > a[i + 1]) {
2642                 int j = i;
2643                 while (++j <= right && v > a[j]) {
2644                     a[j - 1] = a[j];
2645                 }
2646                 a[j - 1] = v;
2647             }
2648         }
2649         // Scan the remaining data and insert
2650         // Mitigate worst case performance on descending data by backward sweep
2651         double m = a[k];
2652         // Pointer to a position in the sorted array
2653         final int p = (right + k) >>> 1;
2654         for (int i = left - 1; ++i < k;) {
2655             final double v = a[i];
2656             if (v > m) {
2657                 a[i] = m;
2658                 int j = k;
2659                 if (v > a[p]) {
2660                     // Skip ahead
2661                     //System.arraycopy(a, p, a, p - 1, p - k);
2662                     while (j < p) {
2663                         // left index is evaluated before right increment
2664                         a[j] = a[++j];
2665                     }
2666                     // j == p
2667                     while (++j <= right && v > a[j]) {
2668                         a[j - 1] = a[j];
2669                     }
2670                 } else {
2671                     // No bounds check on right: a[k] < v <= a[p]
2672                     while (v > a[++j]) {
2673                         a[j - 1] = a[j];
2674                     }
2675                 }
2676                 a[j - 1] = v;
2677                 m = a[k];
2678             }
2679         }
2680     }
2681 
2682     /**
2683      * Partition the array such that indices {@code k} correspond to their correctly
2684      * sorted value in the equivalent fully sorted array. For all indices {@code k}
2685      * and any index {@code i}:
2686      *
2687      * <pre>{@code
2688      * data[i < k] <= data[k] <= data[k < i]
2689      * }</pre>
2690      *
2691      * <p>Note: This is the only quickselect method in this class not based on introselect.
2692      * This is a legacy method containing alternatives for iterating over
2693      * multiple keys that are not supported by introselect, namely:
2694      *
2695      * <ul>
2696      * <li>{@link KeyStrategy#SEQUENTIAL}: This method concatenates close indices into
2697      * ranges and processes them together. It should be able to identify ranges that
2698      * require a full sort. Start-up cost is higher. In practice the indices do not saturate
2699      * the range if the length is reasonable and it is typically possible to cut between indices
2700      * during partitioning to create regions that do not require visiting. Thus trying to identify
2701      * regions for a full sort is a waste of resources.
2702      * <li>{@link KeyStrategy#INDEX_SET}: Uses a {@code BitSet}-type structure to store
2703      * pivots during a call to partition. These can be used to bracket the search for the next index.
2704      * Storage of sparse indices is inefficient as it will require up to length bits of the memory
2705      * of the input array length. Sparse ranges cannot be efficiently searched.
2706      * <li>{@link KeyStrategy#PIVOT_CACHE}: The {@link PivotCache} interface abstracts
2707      * methods from a {@code BitSet}. Indices can be stored and searched. The abstraction allows
2708      * the pivots to be stored efficiently. However there are no sparse implementations
2709      * of the interface other than 1 or 2 points. So performance is similar to the INDEX_SET
2710      * method. One difference is the method finds the outer indices first and then
2711      * only searches the internal region for the rest of the indices. This makes no difference
2712      * to performance.
2713      * </ul>
2714      *
2715      * <p>Note: In each method indices are processed independently. Thus each bracket around an
2716      * index to partition does not know the number of recursion steps used to obtain the start
2717      * pivots defining the bracket. Excess recursion cannot be efficiently tracked for each
2718      * partition. This is unlike introselect which tracks recursion and can switch to algorithm
2719      * if quickselect convergence is slow.
2720      *
2721      * <p>Benchmarking can be used to show these alternatives are slower.
2722      *
2723      * @param part Partition function.
2724      * @param data Values.
2725      * @param right Upper bound of data (inclusive).
2726      * @param k Indices (may be destructively modified).
2727      * @param count Count of indices.
2728      */
2729     private void partition(PartitionFunction part, double[] data, int right, int[] k, int count) {
2730         if (count < 1 || right < 1) {
2731             return;
2732         }
2733         // Validate indices. Excludes indices > right.
2734         final int n = countIndices(k, count, right);
2735         if (n < 1) {
2736             return;
2737         }
2738         if (n == 1) {
2739             part.partition(data, 0, right, k[0], k[0], false, false);
2740         } else if (n == 2 && Math.abs(k[0] - k[1]) <= (minQuickSelectSize >>> 1)) {
2741             final int ka = Math.min(k[0], k[1]);
2742             final int kb = Math.max(k[0], k[1]);
2743             part.partition(data, 0, right, ka, kb, false, false);
2744         } else {
2745             // Allow non-sequential / sequential processing to be selected
2746             if (keyStrategy == KeyStrategy.SEQUENTIAL) {
2747                 // Sequential processing
2748                 final ScanningPivotCache pivots = keyAnalysis(right + 1, k, n, minQuickSelectSize >>> 1);
2749                 if (k[0] == Integer.MIN_VALUE) {
2750                     // Full-sort recommended. Assume the partition function
2751                     // can choose to switch to using Arrays.sort.
2752                     part.sort(data, 0, right, false, false);
2753                 } else {
2754                     partitionSequential(part, data, k, n, right, pivots);
2755                 }
2756             } else if (keyStrategy == KeyStrategy.INDEX_SET) {
2757                 // Non-sequential processing using non-optimised storage
2758                 final IndexSet pivots = IndexSet.ofRange(0, right);
2759                 // First index must partition the entire range
2760                 part.partition(data, 0, right, k[0], k[0], false, false, pivots);
2761                 for (int i = 1; i < n; i++) {
2762                     final int ki = k[i];
2763                     if (pivots.get(ki)) {
2764                         continue;
2765                     }
2766                     final int l = pivots.previousSetBit(ki);
2767                     int r = pivots.nextSetBit(ki);
2768                     if (r < 0) {
2769                         r = right + 1;
2770                     }
2771                     part.partition(data, l + 1, r - 1, ki, ki, l >= 0, r <= right, pivots);
2772                 }
2773             } else if (keyStrategy == KeyStrategy.PIVOT_CACHE) {
2774                 // Non-sequential processing using a pivot cache to optimise storage
2775                 final PivotCache pivots = createPivotCacheForIndices(k, n);
2776 
2777                 // Handle single-point or tiny range
2778                 if ((pivots.right() - pivots.left()) <= (minQuickSelectSize >>> 1)) {
2779                     part.partition(data, 0, right, pivots.left(), pivots.right(), false, false);
2780                     return;
2781                 }
2782 
2783                 // Bracket the range so the rest is internal.
2784                 // Note: Partition function handles min/max searching if ka/kb are
2785                 // at the end of the range.
2786                 final int ka = pivots.left();
2787                 part.partition(data, 0, right, ka, ka, false, false, pivots);
2788                 final int kb = pivots.right();
2789                 int l = pivots.previousPivot(kb);
2790                 int r = pivots.nextPivot(kb);
2791                 if (r < 0) {
2792                     // Partition did not visit downstream
2793                     r = right + 1;
2794                 }
2795                 part.partition(data, l + 1, r - 1, kb, kb, true, r <= right, pivots);
2796                 for (int i = 0; i < n; i++) {
2797                     final int ki = k[i];
2798                     if (pivots.contains(ki)) {
2799                         continue;
2800                     }
2801                     l = pivots.previousPivot(ki);
2802                     r = pivots.nextPivot(ki);
2803                     part.partition(data, l + 1, r - 1, ki, ki, true, true, pivots);
2804                 }
2805             } else {
2806                 throw new IllegalStateException("Unsupported: " + keyStrategy);
2807             }
2808         }
2809     }
2810 
2811     /**
2812      * Return a {@link PivotCache} implementation to support the range
2813      * {@code [left, right]} as defined by minimum and maximum index.
2814      *
2815      * @param indices Indices.
2816      * @param n Count of indices (must be strictly positive).
2817      * @return the pivot cache
2818      */
2819     private static PivotCache createPivotCacheForIndices(int[] indices, int n) {
2820         int min = indices[0];
2821         int max = min;
2822         for (int i = 1; i < n; i++) {
2823             final int k = indices[i];
2824             min = Math.min(min, k);
2825             max = Math.max(max, k);
2826         }
2827         return PivotCaches.ofFullRange(min, max);
2828     }
2829 
2830     /**
2831      * Analysis of keys to partition. The indices k are updated in-place. The keys are
2832      * processed to eliminate duplicates and sorted in ascending order. Close points are
2833      * joined into ranges using the minimum separation. A zero or negative separation
2834      * prevents creating ranges.
2835      *
2836      * <p>On output the indices contain ranges or single points to partition in ascending
2837      * order. Single points are identified as negative values and should be bit-flipped
2838      * to the index value.
2839      *
2840      * <p>If compression occurs the result will contain fewer indices than {@code n}.
2841      * The end of the compressed range is marked using {@link Integer#MIN_VALUE}. This
2842      * is outside the valid range for any single index and signals to stop processing
2843      * the ordered indices.
2844      *
2845      * <p>A {@link PivotCache} implementation is returned for optimal bracketing
2846      * of indices in the range after the first target range / point.
2847      *
2848      * <p>Examples:
2849      *
2850      * <pre>{@code
2851      *                                                 [L, R] PivotCache
2852      * [3]                -> [3]                       -
2853      *
2854      * // min separation 0
2855      * [3, 4, 5]          -> [~3, ~4, ~5]              [4, 5]
2856      * [3, 4, 7, 8]       -> [~3, ~4, ~7, ~8]          [4, 8]
2857      *
2858      * // min separation 1
2859      * [3, 4, 5]          -> [3, 5, MIN_VALUE]         -
2860      * [3, 4, 5, 8]       -> [3, 5, ~8, MIN_VALUE]     [8]
2861      * [3, 4, 5, 6, 7, 8] -> [3, 8, MIN_VALUE, ...]    -
2862      * [3, 4, 7, 8]       -> [3, 4, 7, 8]              [7, 8]
2863      * [3, 4, 7, 8, 99]   -> [3, 4, 7, 8, ~99]         [7, 99]
2864      * }</pre>
2865      *
2866      * <p>The length of data to partition can be used to determine if processing is
2867      * required. A full sort of the data is recommended by returning
2868      * {@code k[0] == Integer.MIN_VALUE}. This occurs if the length is sufficiently small
2869      * or the first range to partition covers the entire data.
2870      *
2871      * <p>Note: The signal marker {@code Integer.MIN_VALUE} is {@code Integer.MAX_VALUE}
2872      * bit flipped. It this is outside the range of any valid index into an array.
2873      *
2874      * @param size Length of the data to partition.
2875      * @param k Indices.
2876      * @param n Count of indices (must be strictly positive).
2877      * @param minSeparation Minimum separation between points (set to zero to disable ranges).
2878      * @return the pivot cache
2879      */
2880     // package-private for testing
2881     ScanningPivotCache keyAnalysis(int size, int[] k, int n, int minSeparation) {
2882         // Tiny data, signal to sort it
2883         if (size < minQuickSelectSize) {
2884             k[0] = Integer.MIN_VALUE;
2885             return null;
2886         }
2887         // Sort the keys
2888         final IndexSet indices = Sorting.sortUnique(Math.max(6, minQuickSelectSize), k, n);
2889         // Find the max index
2890         int right = k[n - 1];
2891         if (right < 0) {
2892             right = ~right;
2893         }
2894         // Join up close keys using the min separation distance.
2895         final int left = compressRange(k, n, minSeparation);
2896         if (left < 0) {
2897             // Nothing to partition after the first target.
2898             // Recommend full sort if the range is effectively complete.
2899             // A range requires n > 1 and positive indices.
2900             if (n != 1 && k[0] >= 0 && size - (k[1] - k[0]) < minQuickSelectSize) {
2901                 k[0] = Integer.MIN_VALUE;
2902             }
2903             return null;
2904         }
2905         // Return an optimal PivotCache to process keys in sorted order
2906         if (indices != null) {
2907             // Reuse storage from sorting large number of indices
2908             return indices.asScanningPivotCache(left, right);
2909         }
2910         return IndexSet.createScanningPivotCache(left, right);
2911     }
2912 
2913     /**
2914      * Compress sorted indices into ranges using the minimum separation.
2915      * Single points are identified by bit flipping to negative. The
2916      * first unused position after compression is set to {@link Integer#MIN_VALUE},
2917      * unless this is outside the array length (i.e. no compression).
2918      *
2919      * @param k Unique indices (sorted).
2920      * @param n Count of indices (must be strictly positive).
2921      * @param minSeparation Minimum separation between points.
2922      * @return the first index after the initial pair / point (or -1)
2923      */
2924     private static int compressRange(int[] k, int n, int minSeparation) {
2925         if (n == 1) {
2926             // Single point, mark the first unused position
2927             if (k.length > 1) {
2928                 k[1] = Integer.MIN_VALUE;
2929             }
2930             return -1;
2931         }
2932         // Start of range is in k[j]; end in p2
2933         int j = 0;
2934         int p2 = k[0];
2935         int secondTarget = -1;
2936         for (int i = 0; ++i < n;) {
2937             if (k[i] < 0) {
2938                 // Start of duplicate indices
2939                 break;
2940             }
2941             if (k[i] <= p2 + minSeparation) {
2942                 // Extend range
2943                 p2 = k[i];
2944             } else {
2945                 // Store range or point (bit flipped)
2946                 if (k[j] == p2) {
2947                     k[j] = ~p2;
2948                 } else {
2949                     k[++j] = p2;
2950                 }
2951                 j++;
2952                 // Next range is k[j] to p2
2953                 k[j] = p2 = k[i];
2954                 // Set the position of the second target
2955                 if (secondTarget < 0) {
2956                     secondTarget = p2;
2957                 }
2958             }
2959         }
2960         // Store range or point (bit flipped)
2961         // Note: If there is only 1 range then the second target is -1
2962         if (k[j] == p2) {
2963             k[j] = ~p2;
2964         } else {
2965             k[++j] = p2;
2966         }
2967         j++;
2968         // Add a marker at the end of the compressed indices
2969         if (k.length > j) {
2970             k[j] = Integer.MIN_VALUE;
2971         }
2972         return secondTarget;
2973     }
2974 
2975     /**
2976      * Partition the array such that indices {@code k} correspond to their correctly
2977      * sorted value in the equivalent fully sorted array. For all indices {@code k}
2978      * and any index {@code i}:
2979      *
2980      * <pre>{@code
2981      * data[i < k] <= data[k] <= data[k < i]
2982      * }</pre>
2983      *
2984      * <p>The keys must have been pre-processed by {@link #keyAnalysis(int, int[], int, int)}
2985      * to structure them for sequential processing.
2986      *
2987      * @param part Partition function.
2988      * @param data Values.
2989      * @param k Indices (created by key analysis).
2990      * @param n Count of indices.
2991      * @param right Upper bound (inclusive).
2992      * @param pivots Cache of pivots (created by key analysis).
2993      */
2994     private static void partitionSequential(PartitionFunction part, double[] data, int[] k, int n,
2995         int right, ScanningPivotCache pivots) {
2996         // Sequential processing of [s, s] single points / [s, e] pairs (regions).
2997         // Single-points are identified as negative indices.
2998         // The partition algorithm must run so each [s, e] is sorted:
2999         // lower---se----------------s---e---------upper
3000         // Pivots are stored to allow lower / upper to be set for the next region:
3001         // lower---se-------p--------s-p-e-----p---upper
3002         int i = 1;
3003         int s = k[0];
3004         int e;
3005         if (s < 0) {
3006             e = s = ~s;
3007         } else {
3008             e = k[i++];
3009         }
3010 
3011         // Key analysis has configured the pivot cache correctly for the first region.
3012         // If there is no cache, there is only 1 region.
3013         if (pivots == null) {
3014             part.partition(data, 0, right, s, e, false, false);
3015             return;
3016         }
3017 
3018         part.partitionSequential(data, 0, right, s, e, false, false, pivots);
3019 
3020         // Process remaining regions
3021         while (i < n) {
3022             s = k[i++];
3023             if (s < 0) {
3024                 e = s = ~s;
3025             } else {
3026                 e = k[i++];
3027             }
3028             if (s > right) {
3029                 // End of indices
3030                 break;
3031             }
3032             // Cases:
3033             // 1. l------s-----------r  Single point (s==e)
3034             // 2. l------se----------r  An adjacent pair of points
3035             // 3. l------s------e----r  A range of points (may contain internal pivots)
3036             // Find bounding region of range: [l, r)
3037             // Left (inclusive) is always above 0 as we have partitioned upstream already.
3038             // Right (exclusive) may not have been searched yet so we check right bounds.
3039             final int l = pivots.previousPivot(s);
3040             final int r = pivots.nextPivotOrElse(e, right + 1);
3041 
3042             // Create regions:
3043             // Partition: l------s--p1
3044             // Sort:                p1-----p2
3045             // Partition:                  p2-----e-----r
3046             // Look for internal pivots.
3047             int p1 = -1;
3048             int p2 = -1;
3049             if (e - s > 1) {
3050                 final int p = pivots.nextPivot(s + 1);
3051                 if (p > s && p < e) {
3052                     p1 = p;
3053                     p2 = pivots.previousPivot(e - 1);
3054                     if (p2 - p1 > SORT_BETWEEN_SIZE) {
3055                         // Special-case: multiple internal pivots
3056                         // Full-sort of (p1, p2). Walk the unsorted regions:
3057                         // l------s--p1                               p2----e-----r
3058                         //             ppppp-----pppp----pppp---------
3059                         //                  s1-e1    s1e1    s1-----e1
3060                         int e1 = pivots.previousNonPivot(p2);
3061                         while (p1 < e1) {
3062                             final int s1 = pivots.previousPivot(e1);
3063                             part.sort(data, s1 + 1, e1, true, true);
3064                             e1 = pivots.previousNonPivot(s1);
3065                         }
3066                     }
3067                 }
3068             }
3069 
3070             // Pivots are only required for the next downstream region
3071             int sn = right + 1;
3072             if (i < n) {
3073                 sn = k[i];
3074                 if (sn < 0) {
3075                     sn = ~sn;
3076                 }
3077             }
3078             // Current implementations will signal if this is outside the support.
3079             // Occurs on the last region the cache was created to support (i.e. sn > right).
3080             final boolean unsupportedCacheRange = !pivots.moveLeft(sn);
3081 
3082             // Note: The partition function uses inclusive left and right bounds
3083             // so use +/- 1 from pivot values. If r is not a pivot it is right + 1
3084             // which is a valid exclusive upper bound.
3085 
3086             if (p1 > s) {
3087                 // At least 1 internal pivot:
3088                 // l <= s < p1 and p2 < e <= r
3089                 // If l == s or r == e these calls should fully sort the respective range
3090                 part.partition(data, l + 1, p1 - 1, s, p1 - 1, true, p1 <= right);
3091                 if (unsupportedCacheRange) {
3092                     part.partition(data, p2 + 1, r - 1, p2 + 1, e, true, r <= right);
3093                 } else {
3094                     part.partitionSequential(data, p2 + 1, r - 1, p2 + 1, e, true, r <= right, pivots);
3095                 }
3096             } else {
3097                 // Single range
3098                 if (unsupportedCacheRange) {
3099                     part.partition(data, l + 1, r - 1, s, e, true, r <= right);
3100                 } else {
3101                     part.partitionSequential(data, l + 1, r - 1, s, e, true, r <= right, pivots);
3102                 }
3103             }
3104         }
3105     }
3106 
3107     /**
3108      * Sort the data.
3109      *
3110      * <p>Uses a Bentley-McIlroy quicksort partition method. Signed zeros
3111      * are corrected when encountered during processing.
3112      *
3113      * @param data Values.
3114      */
3115     void sortSBM(double[] data) {
3116         // Handle NaN
3117         final int right = sortNaN(data);
3118         sort((SPEPartitionFunction) this::partitionSBMWithZeros, data, right);
3119     }
3120 
3121     /**
3122      * Sort the data by recursive partitioning (quicksort).
3123      *
3124      * @param part Partition function.
3125      * @param data Values.
3126      * @param right Upper bound (inclusive).
3127      */
3128     private static void sort(PartitionFunction part, double[] data, int right) {
3129         if (right < 1) {
3130             return;
3131         }
3132         // Signal entire range
3133         part.sort(data, 0, right, false, false);
3134     }
3135 
3136     /**
3137      * Sort the data using an introsort.
3138      *
3139      * <p>Uses the configured single-pivot partition method; falling back
3140      * to heapsort when quicksort recursion is slow.
3141      *
3142      * @param data Values.
3143      */
3144     void sortISP(double[] data) {
3145         // NaN processing is done in the introsort method
3146         introsort(getSPFunction(), data);
3147     }
3148 
3149     /**
3150      * Sort the array using an introsort. The single-pivot partition method is provided as an argument.
3151      * Switches to heapsort when recursive partitioning reaches a maximum depth.
3152      *
3153      * <p>The partition method is not required to handle signed zeros.
3154      *
3155      * @param part Partition function.
3156      * @param a Values.
3157      * @see <a href="https://en.wikipedia.org/wiki/Introsort">Introsort (Wikipedia)</a>
3158      */
3159     private void introsort(SPEPartition part, double[] a) {
3160         // Handle NaN / signed zeros
3161         final DoubleDataTransformer t = SORT_TRANSFORMER.get();
3162         // Assume this is in-place
3163         t.preProcess(a);
3164         final int end = t.length();
3165         if (end > 1) {
3166             introsort(part, a, 0, end - 1, createMaxDepthSinglePivot(end));
3167         }
3168         // Restore signed zeros
3169         t.postProcess(a);
3170     }
3171 
3172     /**
3173      * Sort the array.
3174      *
3175      * <p>Uses an introsort. The single-pivot partition method is provided as an argument.
3176      *
3177      * @param part Partition function.
3178      * @param a Values.
3179      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
3180      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
3181      * @param maxDepth Maximum depth for recursion.
3182      * @see <a href="https://en.wikipedia.org/wiki/Introsort">Introsort (Wikipedia)</a>
3183      */
3184     private void introsort(SPEPartition part, double[] a, int left, int right, int maxDepth) {
3185         // Only one side requires recursion. The other side
3186         // can remain within this function call.
3187         final int l = left;
3188         int r = right;
3189         final int[] upper = {0};
3190         while (true) {
3191             // Full sort of small data
3192             if (r - l < minQuickSelectSize) {
3193                 Sorting.sort(a, l, r);
3194                 return;
3195             }
3196             if (maxDepth == 0) {
3197                 // Too much recursion
3198                 heapSort(a, l, r);
3199                 return;
3200             }
3201 
3202             // Pick a pivot and partition
3203             final int p0 = part.partition(a, l, r,
3204                 pivotingStrategy.pivotIndex(a, l, r, l),
3205                 upper);
3206             final int p1 = upper[0];
3207 
3208             // Recurse right side
3209             introsort(part, a, p1 + 1, r, --maxDepth);
3210             // Continue on the left side
3211             r = p0 - 1;
3212         }
3213     }
3214 
3215     /**
3216      * Sort the data using an introsort.
3217      *
3218      * <p>Uses a dual-pivot quicksort method; falling back
3219      * to heapsort when quicksort recursion is slow.
3220      *
3221      * @param data Values.
3222      */
3223     void sortIDP(double[] data) {
3224         // NaN processing is done in the introsort method
3225         introsort((DPPartition) Partition::partitionDP, data);
3226     }
3227 
3228     /**
3229      * Sort the array using an introsort. The dual-pivot partition method is provided as an argument.
3230      * Switches to heapsort when recursive partitioning reaches a maximum depth.
3231      *
3232      * <p>The partition method is not required to handle signed zeros.
3233      *
3234      * @param part Partition function.
3235      * @param a Values.
3236      * @see <a href="https://en.wikipedia.org/wiki/Introsort">Introsort (Wikipedia)</a>
3237      */
3238     private void introsort(DPPartition part, double[] a) {
3239         // Handle NaN / signed zeros
3240         final DoubleDataTransformer t = SORT_TRANSFORMER.get();
3241         // Assume this is in-place
3242         t.preProcess(a);
3243         final int end = t.length();
3244         if (end > 1) {
3245             introsort(part, a, 0, end - 1, createMaxDepthDualPivot(end));
3246         }
3247         // Restore signed zeros
3248         t.postProcess(a);
3249     }
3250 
3251     /**
3252      * Sort the array.
3253      *
3254      * <p>Uses an introsort. The dual-pivot partition method is provided as an argument.
3255      *
3256      * @param part Partition function.
3257      * @param a Values.
3258      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
3259      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
3260      * @param maxDepth Maximum depth for recursion.
3261      * @see <a href="https://en.wikipedia.org/wiki/Introsort">Introsort (Wikipedia)</a>
3262      */
3263     private void introsort(DPPartition part, double[] a, int left, int right, int maxDepth) {
3264         // Only two regions require recursion. The third region
3265         // can remain within this function call.
3266         final int l = left;
3267         int r = right;
3268         final int[] upper = {0, 0, 0};
3269         while (true) {
3270             // Full sort of small data
3271             if (r - l < minQuickSelectSize) {
3272                 Sorting.sort(a, l, r);
3273                 return;
3274             }
3275             if (maxDepth == 0) {
3276                 // Too much recursion
3277                 heapSort(a, l, r);
3278                 return;
3279             }
3280 
3281             // Pick 2 pivots and partition
3282             int p0 = dualPivotingStrategy.pivotIndex(a, l, r, upper);
3283             p0 = part.partition(a, l, r, p0, upper[0], upper);
3284             final int p1 = upper[0];
3285             final int p2 = upper[1];
3286             final int p3 = upper[2];
3287 
3288             // Recurse middle and right sides
3289             --maxDepth;
3290             introsort(part, a, p3 + 1, r, maxDepth);
3291             introsort(part, a, p1 + 1, p2 - 1, maxDepth);
3292             // Continue on the left side
3293             r = p0 - 1;
3294         }
3295     }
3296 
3297     /**
3298      * Partition the array such that indices {@code k} correspond to their correctly
3299      * sorted value in the equivalent fully sorted array. For all indices {@code k}
3300      * and any index {@code i}:
3301      *
3302      * <pre>{@code
3303      * data[i < k] <= data[k] <= data[k < i]
3304      * }</pre>
3305      *
3306      * <p>All indices are assumed to be within {@code [0, right]}.
3307      *
3308      * <p>Uses an introselect variant. The single-pivot quickselect is provided as an argument;
3309      * the fall-back on poor convergence of the quickselect is controlled by
3310      * current configuration.
3311      *
3312      * <p>The partition method is not required to handle signed zeros.
3313      *
3314      * @param part Partition function.
3315      * @param a Values.
3316      * @param k Indices (may be destructively modified).
3317      * @param count Count of indices (assumed to be strictly positive).
3318      */
3319     private void introselect(SPEPartition part, double[] a, int[] k, int count) {
3320         // Handle NaN / signed zeros
3321         final DoubleDataTransformer t = SORT_TRANSFORMER.get();
3322         // Assume this is in-place
3323         t.preProcess(a);
3324         final int end = t.length();
3325         int n = count;
3326         if (end > 1) {
3327             // Filter indices invalidated by NaN check
3328             if (end < a.length) {
3329                 for (int i = n; --i >= 0;) {
3330                     final int v = k[i];
3331                     if (v >= end) {
3332                         // swap(k, i, --n)
3333                         k[i] = k[--n];
3334                         k[n] = v;
3335                     }
3336                 }
3337             }
3338             introselect(part, a, end - 1, k, n);
3339         }
3340         // Restore signed zeros
3341         t.postProcess(a, k, n);
3342     }
3343 
3344     /**
3345      * Partition the array such that indices {@code k} correspond to their correctly
3346      * sorted value in the equivalent fully sorted array. For all indices {@code k}
3347      * and any index {@code i}:
3348      *
3349      * <pre>{@code
3350      * data[i < k] <= data[k] <= data[k < i]
3351      * }</pre>
3352      *
3353      * <p>All indices are assumed to be within {@code [0, right]}.
3354      *
3355      * <p>Uses an introselect variant. The quickselect is provided as an argument;
3356      * the fall-back on poor convergence of the quickselect is controlled by
3357      * current configuration.
3358      *
3359      * @param part Partition function.
3360      * @param a Values.
3361      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
3362      * @param k Indices (may be destructively modified).
3363      * @param n Count of indices (assumed to be strictly positive).
3364      */
3365     private void introselect(SPEPartition part, double[] a, int right, int[] k, int n) {
3366         if (n < 1) {
3367             return;
3368         }
3369         final int maxDepth = createMaxDepthSinglePivot(right + 1);
3370         // Handle cases without multiple keys
3371         if (n == 1) {
3372             // Dedicated methods for a single key. These use different strategies
3373             // to trigger the stopper on quickselect recursion
3374             if (pairedKeyStrategy == PairedKeyStrategy.PAIRED_KEYS) {
3375                 introselect(part, a, 0, right, k[0], maxDepth);
3376             } else if (pairedKeyStrategy == PairedKeyStrategy.PAIRED_KEYS_2) {
3377                 // This uses the configured recursion constant c.
3378                 // The length must halve every c iterations.
3379                 introselect2(part, a, 0, right, k[0]);
3380             } else if (pairedKeyStrategy == PairedKeyStrategy.PAIRED_KEYS_LEN) {
3381                 introselect(part, a, 0, right, k[0]);
3382             } else if (pairedKeyStrategy == PairedKeyStrategy.TWO_KEYS) {
3383                 // Dedicated method for two separate keys using the same key
3384                 introselect(part, a, 0, right, k[0], k[0], maxDepth);
3385             } else if (pairedKeyStrategy == PairedKeyStrategy.KEY_RANGE) {
3386                 // Dedicated method for a range of keys using the same key
3387                 introselect2(part, a, 0, right, k[0], k[0]);
3388             } else if (pairedKeyStrategy == PairedKeyStrategy.SEARCHABLE_INTERVAL) {
3389                 // Reuse the SearchableInterval method using the same key
3390                 introselect(part, a, 0, right, IndexIntervals.anyIndex(), k[0], k[0], maxDepth);
3391             } else if (pairedKeyStrategy == PairedKeyStrategy.UPDATING_INTERVAL) {
3392                 // Reuse the UpdatingInterval method using a single key
3393                 introselect(part, a, 0, right, IndexIntervals.interval(k[0]), maxDepth);
3394             } else {
3395                 throw new IllegalStateException(UNSUPPORTED_INTROSELECT + pairedKeyStrategy);
3396             }
3397             return;
3398         }
3399         // Special case for partition around adjacent indices (for interpolation)
3400         if (n == 2 && k[0] + 1 == k[1]) {
3401             // Dedicated method for a single key, returns information about k+1
3402             if (pairedKeyStrategy == PairedKeyStrategy.PAIRED_KEYS) {
3403                 final int p = introselect(part, a, 0, right, k[0], maxDepth);
3404                 // p <= k to signal k+1 is unsorted, or p+1 is a pivot.
3405                 // if k is sorted, and p+1 is sorted, k+1 is sorted if k+1 == p.
3406                 if (p > k[1]) {
3407                     selectMinIgnoreZeros(a, k[1], p);
3408                 }
3409             } else if (pairedKeyStrategy == PairedKeyStrategy.PAIRED_KEYS_2) {
3410                 final int p = introselect2(part, a, 0, right, k[0]);
3411                 if (p > k[1]) {
3412                     selectMinIgnoreZeros(a, k[1], p);
3413                 }
3414             } else if (pairedKeyStrategy == PairedKeyStrategy.PAIRED_KEYS_LEN) {
3415                 final int p = introselect(part, a, 0, right, k[0]);
3416                 if (p > k[1]) {
3417                     selectMinIgnoreZeros(a, k[1], p);
3418                 }
3419             } else if (pairedKeyStrategy == PairedKeyStrategy.TWO_KEYS) {
3420                 // Dedicated method for two separate keys
3421                 // Note: This can handle keys that are not adjacent
3422                 // e.g. keys near opposite ends without a partition step.
3423                 final int ka = Math.min(k[0], k[1]);
3424                 final int kb = Math.max(k[0], k[1]);
3425                 introselect(part, a, 0, right, ka, kb, maxDepth);
3426             } else if (pairedKeyStrategy == PairedKeyStrategy.KEY_RANGE) {
3427                 // Dedicated method for a range of keys using the same key
3428                 final int ka = Math.min(k[0], k[1]);
3429                 final int kb = Math.max(k[0], k[1]);
3430                 introselect2(part, a, 0, right, ka, kb);
3431             } else if (pairedKeyStrategy == PairedKeyStrategy.SEARCHABLE_INTERVAL) {
3432                 // Reuse the SearchableInterval method using a range of two keys
3433                 introselect(part, a, 0, right, IndexIntervals.anyIndex(), k[0], k[1], maxDepth);
3434             } else if (pairedKeyStrategy == PairedKeyStrategy.UPDATING_INTERVAL) {
3435                 // Reuse the UpdatingInterval method using a range of two keys
3436                 introselect(part, a, 0, right, IndexIntervals.interval(k[0], k[1]), maxDepth);
3437             } else {
3438                 throw new IllegalStateException(UNSUPPORTED_INTROSELECT + pairedKeyStrategy);
3439             }
3440             return;
3441         }
3442 
3443         // Note: Sorting to unique keys is an overhead. This can be eliminated
3444         // by requesting the caller passes sorted keys.
3445 
3446         // Note: Attempts to perform key analysis here to detect a full sort
3447         // add an overhead for sparse keys and do not increase performance
3448         // for saturated keys unless data is structured with ascending/descending
3449         // runs so that it is fast with JDK's merge sort algorithm in Arrays.sort.
3450 
3451         if (keyStrategy == KeyStrategy.ORDERED_KEYS) {
3452             final int unique = Sorting.sortIndices(k, n);
3453             introselect(part, a, 0, right, k, 0, unique - 1, maxDepth);
3454         } else if (keyStrategy == KeyStrategy.SCANNING_KEY_SEARCHABLE_INTERVAL) {
3455             final int unique = Sorting.sortIndices(k, n);
3456             final SearchableInterval keys = ScanningKeyInterval.of(k, unique);
3457             introselect(part, a, 0, right, keys, keys.left(), keys.right(), maxDepth);
3458         } else if (keyStrategy == KeyStrategy.SEARCH_KEY_SEARCHABLE_INTERVAL) {
3459             final int unique = Sorting.sortIndices(k, n);
3460             final SearchableInterval keys = BinarySearchKeyInterval.of(k, unique);
3461             introselect(part, a, 0, right, keys, keys.left(), keys.right(), maxDepth);
3462         } else if (keyStrategy == KeyStrategy.COMPRESSED_INDEX_SET) {
3463             final SearchableInterval keys = CompressedIndexSet.of(compression, k, n);
3464             introselect(part, a, 0, right, keys, keys.left(), keys.right(), maxDepth);
3465         } else if (keyStrategy == KeyStrategy.INDEX_SET) {
3466             final SearchableInterval keys = IndexSet.of(k, n);
3467             introselect(part, a, 0, right, keys, keys.left(), keys.right(), maxDepth);
3468         } else if (keyStrategy == KeyStrategy.KEY_UPDATING_INTERVAL) {
3469             final int unique = Sorting.sortIndices(k, n);
3470             final UpdatingInterval keys = KeyUpdatingInterval.of(k, unique);
3471             introselect(part, a, 0, right, keys, maxDepth);
3472         } else if (keyStrategy == KeyStrategy.INDEX_SET_UPDATING_INTERVAL) {
3473             final UpdatingInterval keys = BitIndexUpdatingInterval.of(k, n);
3474             introselect(part, a, 0, right, keys, maxDepth);
3475         } else if (keyStrategy == KeyStrategy.KEY_SPLITTING_INTERVAL) {
3476             final int unique = Sorting.sortIndices(k, n);
3477             final SplittingInterval keys = KeyUpdatingInterval.of(k, unique);
3478             introselect(part, a, 0, right, keys, maxDepth);
3479         } else if (keyStrategy == KeyStrategy.INDEX_SET_SPLITTING_INTERVAL) {
3480             final SplittingInterval keys = BitIndexUpdatingInterval.of(k, n);
3481             introselect(part, a, 0, right, keys, maxDepth);
3482         } else if (keyStrategy == KeyStrategy.INDEX_ITERATOR) {
3483             final int unique = Sorting.sortIndices(k, n);
3484             final IndexIterator keys = KeyIndexIterator.of(k, unique);
3485             introselect(part, a, 0, right, keys, keys.left(), keys.right(), maxDepth);
3486         } else if (keyStrategy == KeyStrategy.COMPRESSED_INDEX_ITERATOR) {
3487             final IndexIterator keys = CompressedIndexSet.iterator(compression, k, n);
3488             introselect(part, a, 0, right, keys, keys.left(), keys.right(), maxDepth);
3489         } else {
3490             throw new IllegalStateException(UNSUPPORTED_INTROSELECT + keyStrategy);
3491         }
3492     }
3493 
3494     /**
3495      * Partition the array such that index {@code k} corresponds to its
3496      * correctly sorted value in the equivalent fully sorted array.
3497      *
3498      * <pre>{@code
3499      * data[i < k] <= data[k] <= data[k < i]
3500      * }</pre>
3501      *
3502      * <p>Uses an introselect variant. The quickselect is provided as an argument;
3503      * the fall-back on poor convergence of the quickselect is controlled by
3504      * current configuration.
3505      *
3506      * <p>Returns information {@code p} on whether {@code k+1} is sorted.
3507      * If {@code p <= k} then {@code k+1} is sorted.
3508      * If {@code p > k} then {@code p+1} is a pivot.
3509      *
3510      * @param part Partition function.
3511      * @param a Values.
3512      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
3513      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
3514      * @param k Index.
3515      * @param maxDepth Maximum depth for recursion.
3516      * @return the index {@code p}
3517      */
3518     private int introselect(SPEPartition part, double[] a, int left, int right,
3519         int k, int maxDepth) {
3520         int l = left;
3521         int r = right;
3522         final int[] upper = {0};
3523         while (true) {
3524             // It is possible to use edgeselect when k is close to the end
3525             // |l|-----|k|---------|k|--------|r|
3526             //  ---d1----
3527             //                      -----d2----
3528             final int d1 = k - l;
3529             final int d2 = r - k;
3530             if (Math.min(d1, d2) < edgeSelectConstant) {
3531                 edgeSelection.partition(a, l, r, k, k);
3532                 // Last known unsorted value >= k
3533                 return r;
3534             }
3535 
3536             if (maxDepth == 0) {
3537                 // Too much recursion
3538                 // Note: For testing the Floyd-Rivest algorithm we trigger the recursion
3539                 // consumer as a signal that FR failed due to a non-representative sample.
3540                 recursionConsumer.accept(maxDepth);
3541                 stopperSelection.partition(a, l, r, k, k);
3542                 // Last known unsorted value >= k
3543                 return r;
3544             }
3545 
3546             // Pick a pivot and partition
3547             int pivot;
3548             // length - 1
3549             int n = r - l;
3550             if (n > subSamplingSize) {
3551                 // Floyd-Rivest: use SELECT recursively on a sample of size S to get an estimate
3552                 // for the (k-l+1)-th smallest element into a[k], biased slightly so that the
3553                 // (k-l+1)-th element is expected to lie in the smaller set after partitioning.
3554                 ++n;
3555                 final int ith = k - l + 1;
3556                 final double z = Math.log(n);
3557                 final double s = 0.5 * Math.exp(0.6666666666666666 * z);
3558                 final double sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * Integer.signum(ith - (n >> 1));
3559                 final int ll = Math.max(l, (int) (k - ith * s / n + sd));
3560                 final int rr = Math.min(r, (int) (k + (n - ith) * s / n + sd));
3561                 // Optional random sampling
3562                 if ((controlFlags & FLAG_RANDOM_SAMPLING) != 0) {
3563                     final IntUnaryOperator rng = createRNG(n, k);
3564                     // Shuffle [ll, k) from [l, k)
3565                     if (ll > l) {
3566                         for (int i = k; i > ll;) {
3567                             // l + rand [0, i - l + 1) : i is currently i+1
3568                             final int j = l + rng.applyAsInt(i - l);
3569                             final double t = a[--i];
3570                             a[i] = a[j];
3571                             a[j] = t;
3572                         }
3573                     }
3574                     // Shuffle (k, rr] from (k, r]
3575                     if (rr < r) {
3576                         for (int i = k; i < rr;) {
3577                             // r - rand [0, r - i + 1) : i is currently i-1
3578                             final int j = r - rng.applyAsInt(r - i);
3579                             final double t = a[++i];
3580                             a[i] = a[j];
3581                             a[j] = t;
3582                         }
3583                     }
3584                 }
3585                 introselect(part, a, ll, rr, k, lnNtoMaxDepthSinglePivot(z));
3586                 pivot = k;
3587             } else {
3588                 // default pivot strategy
3589                 pivot = pivotingStrategy.pivotIndex(a, l, r, k);
3590             }
3591 
3592             final int p0 = part.partition(a, l, r, pivot, upper);
3593             final int p1 = upper[0];
3594 
3595             maxDepth--;
3596             if (k < p0) {
3597                 // The element is in the left partition
3598                 r = p0 - 1;
3599             } else if (k > p1) {
3600                 // The element is in the right partition
3601                 l = p1 + 1;
3602             } else {
3603                 // The range contains the element we wanted.
3604                 // Signal if k+1 is sorted.
3605                 // This can be true if the pivot was a range [p0, p1]
3606                 return k < p1 ? k : r;
3607             }
3608         }
3609     }
3610 
3611     /**
3612      * Partition the array such that index {@code k} corresponds to its
3613      * correctly sorted value in the equivalent fully sorted array.
3614      *
3615      * <pre>{@code
3616      * data[i < k] <= data[k] <= data[k < i]
3617      * }</pre>
3618      *
3619      * <p>Uses an introselect variant. The quickselect is provided as an argument;
3620      * the fall-back on poor convergence of the quickselect is controlled by
3621      * current configuration.
3622      *
3623      * <p>Returns information {@code p} on whether {@code k+1} is sorted.
3624      * If {@code p <= k} then {@code k+1} is sorted.
3625      * If {@code p > k} then {@code p+1} is a pivot.
3626      *
3627      * <p>Recursion is monitored by checking the partition is reduced by 2<sup>-x</sup> after
3628      * {@code c} iterations where {@code x} is the
3629      * {@link #setRecursionConstant(int) recursion constant} and {@code c} is the
3630      * {@link #setRecursionMultiple(double) recursion multiple} (variables reused for convenience).
3631      * Confidence bounds for dividing a length by 2<sup>-x</sup> are provided in Valois (2000)
3632      * as {@code c = floor((6/5)x) + b}:
3633      * <pre>
3634      * b  confidence (%)
3635      * 2  76.56
3636      * 3  92.92
3637      * 4  97.83
3638      * 5  99.33
3639      * 6  99.79
3640      * </pre>
3641      * <p>Ideally {@code c >= 3} using {@code x = 1}. E.g. We can use 3 iterations to be 76%
3642      * confident the sequence will divide in half; or 7 iterations to be 99% confident the
3643      * sequence will divide into a quarter. A larger factor {@code b} reduces the sensitivity
3644      * of introspection.
3645      *
3646      * @param part Partition function.
3647      * @param a Values.
3648      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
3649      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
3650      * @param k Index.
3651      * @return the index {@code p}
3652      */
3653     private int introselect2(SPEPartition part, double[] a, int left, int right, int k) {
3654         int l = left;
3655         int r = right;
3656         final int[] upper = {0};
3657         int counter = (int) recursionMultiple;
3658         int threshold = (right - left) >>> recursionConstant;
3659         int depth = singlePivotMaxDepth(right - left);
3660         while (true) {
3661             // It is possible to use edgeselect when k is close to the end
3662             // |l|-----|k|---------|k|--------|r|
3663             //  ---d1----
3664             //                      -----d2----
3665             final int d1 = k - l;
3666             final int d2 = r - k;
3667             if (Math.min(d1, d2) < edgeSelectConstant) {
3668                 edgeSelection.partition(a, l, r, k, k);
3669                 // Last known unsorted value >= k
3670                 return r;
3671             }
3672 
3673             // length - 1
3674             int n = r - l;
3675             depth--;
3676             if (--counter < 0) {
3677                 if (n > threshold) {
3678                     // Did not reduce the length after set number of iterations.
3679                     // Here riselect (Valois (2000)) would use random points to choose the pivot
3680                     // to inject entropy and restart. This continues until the sum of the partition
3681                     // lengths is too high (twice the original length). Here we just switch.
3682 
3683                     // Note: For testing we trigger the recursion consumer
3684                     recursionConsumer.accept(depth);
3685                     stopperSelection.partition(a, l, r, k, k);
3686                     // Last known unsorted value >= k
3687                     return r;
3688                 }
3689                 // Once the confidence has been achieved we use (6/5)x with x=1.
3690                 // So check every 5/6 iterations that the length is halving.
3691                 if (counter == -5) {
3692                     counter = 1;
3693                 }
3694                 threshold >>>= 1;
3695             }
3696 
3697             // Pick a pivot and partition
3698             int pivot;
3699             if (n > subSamplingSize) {
3700                 // Floyd-Rivest: use SELECT recursively on a sample of size S to get an estimate
3701                 // for the (k-l+1)-th smallest element into a[k], biased slightly so that the
3702                 // (k-l+1)-th element is expected to lie in the smaller set after partitioning.
3703                 ++n;
3704                 final int ith = k - l + 1;
3705                 final double z = Math.log(n);
3706                 final double s = 0.5 * Math.exp(0.6666666666666666 * z);
3707                 final double sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * Integer.signum(ith - (n >> 1));
3708                 final int ll = Math.max(l, (int) (k - ith * s / n + sd));
3709                 final int rr = Math.min(r, (int) (k + (n - ith) * s / n + sd));
3710                 // Optional random sampling
3711                 if ((controlFlags & FLAG_RANDOM_SAMPLING) != 0) {
3712                     final IntUnaryOperator rng = createRNG(n, k);
3713                     // Shuffle [ll, k) from [l, k)
3714                     if (ll > l) {
3715                         for (int i = k; i > ll;) {
3716                             // l + rand [0, i - l + 1) : i is currently i+1
3717                             final int j = l + rng.applyAsInt(i - l);
3718                             final double t = a[--i];
3719                             a[i] = a[j];
3720                             a[j] = t;
3721                         }
3722                     }
3723                     // Shuffle (k, rr] from (k, r]
3724                     if (rr < r) {
3725                         for (int i = k; i < rr;) {
3726                             // r - rand [0, r - i + 1) : i is currently i-1
3727                             final int j = r - rng.applyAsInt(r - i);
3728                             final double t = a[++i];
3729                             a[i] = a[j];
3730                             a[j] = t;
3731                         }
3732                     }
3733                 }
3734                 // Sample recursion restarts from [ll, rr]
3735                 introselect2(part, a, ll, rr, k);
3736                 pivot = k;
3737             } else {
3738                 // default pivot strategy
3739                 pivot = pivotingStrategy.pivotIndex(a, l, r, k);
3740             }
3741 
3742             final int p0 = part.partition(a, l, r, pivot, upper);
3743             final int p1 = upper[0];
3744 
3745             if (k < p0) {
3746                 // The element is in the left partition
3747                 r = p0 - 1;
3748             } else if (k > p1) {
3749                 // The element is in the right partition
3750                 l = p1 + 1;
3751             } else {
3752                 // The range contains the element we wanted.
3753                 // Signal if k+1 is sorted.
3754                 // This can be true if the pivot was a range [p0, p1]
3755                 return k < p1 ? k : r;
3756             }
3757         }
3758     }
3759 
3760     /**
3761      * Partition the array such that index {@code k} corresponds to its
3762      * correctly sorted value in the equivalent fully sorted array.
3763      *
3764      * <pre>{@code
3765      * data[i < k] <= data[k] <= data[k < i]
3766      * }</pre>
3767      *
3768      * <p>Uses an introselect variant. The quickselect is provided as an argument;
3769      * the fall-back on poor convergence of the quickselect is controlled by
3770      * current configuration.
3771      *
3772      * <p>Returns information {@code p} on whether {@code k+1} is sorted.
3773      * If {@code p <= k} then {@code k+1} is sorted.
3774      * If {@code p > k} then {@code p+1} is a pivot.
3775      *
3776      * <p>Recursion is monitored by checking the sum of partition lengths is less than
3777      * {@code m * (r - l)} where {@code m} is the
3778      * {@link #setRecursionMultiple(double) recursion multiple}.
3779      * Ideally {@code c} should be a value above 1.
3780      *
3781      * @param part Partition function.
3782      * @param a Values.
3783      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
3784      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
3785      * @param k Index.
3786      * @return the index {@code p}
3787      */
3788     private int introselect(SPEPartition part, double[] a, int left, int right, int k) {
3789         int l = left;
3790         int r = right;
3791         final int[] upper = {0};
3792         // Set the limit on the sum of the length. Since the length is subtracted at the start
3793         // of the loop use (1 + recursionMultiple).
3794         long limit = (long) ((1 + recursionMultiple) * (right - left));
3795         int depth = singlePivotMaxDepth(right - left);
3796         while (true) {
3797             // It is possible to use edgeselect when k is close to the end
3798             // |l|-----|k|---------|k|--------|r|
3799             //  ---d1----
3800             //                      -----d2----
3801             final int d1 = k - l;
3802             final int d2 = r - k;
3803             if (Math.min(d1, d2) < edgeSelectConstant) {
3804                 edgeSelection.partition(a, l, r, k, k);
3805                 // Last known unsorted value >= k
3806                 return r;
3807             }
3808 
3809             // length - 1
3810             int n = r - l;
3811             limit -= n;
3812             depth--;
3813 
3814             if (limit < 0) {
3815                 // Excess total partition length
3816                 // Note: For testing we trigger the recursion consumer
3817                 recursionConsumer.accept(depth);
3818                 stopperSelection.partition(a, l, r, k, k);
3819                 // Last known unsorted value >= k
3820                 return r;
3821             }
3822 
3823             // Pick a pivot and partition
3824             int pivot;
3825             if (n > subSamplingSize) {
3826                 // Floyd-Rivest: use SELECT recursively on a sample of size S to get an estimate
3827                 // for the (k-l+1)-th smallest element into a[k], biased slightly so that the
3828                 // (k-l+1)-th element is expected to lie in the smaller set after partitioning.
3829                 ++n;
3830                 final int ith = k - l + 1;
3831                 final double z = Math.log(n);
3832                 final double s = 0.5 * Math.exp(0.6666666666666666 * z);
3833                 final double sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * Integer.signum(ith - (n >> 1));
3834                 final int ll = Math.max(l, (int) (k - ith * s / n + sd));
3835                 final int rr = Math.min(r, (int) (k + (n - ith) * s / n + sd));
3836                 // Optional random sampling
3837                 if ((controlFlags & FLAG_RANDOM_SAMPLING) != 0) {
3838                     final IntUnaryOperator rng = createRNG(n, k);
3839                     // Shuffle [ll, k) from [l, k)
3840                     if (ll > l) {
3841                         for (int i = k; i > ll;) {
3842                             // l + rand [0, i - l + 1) : i is currently i+1
3843                             final int j = l + rng.applyAsInt(i - l);
3844                             final double t = a[--i];
3845                             a[i] = a[j];
3846                             a[j] = t;
3847                         }
3848                     }
3849                     // Shuffle (k, rr] from (k, r]
3850                     if (rr < r) {
3851                         for (int i = k; i < rr;) {
3852                             // r - rand [0, r - i + 1) : i is currently i-1
3853                             final int j = r - rng.applyAsInt(r - i);
3854                             final double t = a[++i];
3855                             a[i] = a[j];
3856                             a[j] = t;
3857                         }
3858                     }
3859                 }
3860                 // Sample recursion restarts from [ll, rr]
3861                 introselect(part, a, ll, rr, k);
3862                 pivot = k;
3863             } else {
3864                 // default pivot strategy
3865                 pivot = pivotingStrategy.pivotIndex(a, l, r, k);
3866             }
3867 
3868             final int p0 = part.partition(a, l, r, pivot, upper);
3869             final int p1 = upper[0];
3870 
3871             if (k < p0) {
3872                 // The element is in the left partition
3873                 r = p0 - 1;
3874             } else if (k > p1) {
3875                 // The element is in the right partition
3876                 l = p1 + 1;
3877             } else {
3878                 // The range contains the element we wanted.
3879                 // Signal if k+1 is sorted.
3880                 // This can be true if the pivot was a range [p0, p1]
3881                 return k < p1 ? k : r;
3882             }
3883         }
3884     }
3885 
3886     /**
3887      * Partition the array such that indices {@code ka} and {@code kb} correspond to their
3888      * correctly sorted value in the equivalent fully sorted array.
3889      *
3890      * <p>For all indices {@code k} and any index {@code i}:
3891      *
3892      * <pre>{@code
3893      * data[i < k] <= data[k] <= data[k < i]
3894      * }</pre>
3895      *
3896      * <p>Note: Requires {@code ka <= kb}. The use of two indices is to support processing
3897      * of pairs of indices {@code (k, k+1)}. However the indices are treated independently
3898      * and partitioned by recursion. They may be equal, neighbours or well separated.
3899      *
3900      * <p>Uses an introselect variant. The quickselect is provided as an argument; the
3901      * fall-back on poor convergence of the quickselect is a heapselect.
3902      *
3903      * @param part Partition function.
3904      * @param a Values.
3905      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
3906      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
3907      * @param ka Index.
3908      * @param kb Index.
3909      * @param maxDepth Maximum depth for recursion.
3910      */
3911     private void introselect(SPEPartition part, double[] a, int left, int right,
3912         int ka, int kb, int maxDepth) {
3913         // Only one side requires recursion. The other side
3914         // can remain within this function call.
3915         int l = left;
3916         int r = right;
3917         int ka1 = ka;
3918         int kb1 = kb;
3919         final int[] upper = {0};
3920         while (true) {
3921             // length - 1
3922             final int n = r - l;
3923 
3924             if (n < minQuickSelectSize) {
3925                 // Sort selection on small data
3926                 sortSelectRange(a, l, r, ka1, kb1);
3927                 return;
3928             }
3929 
3930             // It is possible to use heapselect when ka1 and kb1 are close to the ends
3931             // |l|-----|ka1|--------|kb1|------|r|
3932             //  ---d1----
3933             //                       -----d3----
3934             //  ---------d2-----------
3935             //          ----------d4-----------
3936             final int d1 = ka1 - l;
3937             final int d2 = kb1 - l;
3938             final int d3 = r - kb1;
3939             final int d4 = r - ka1;
3940             if (maxDepth == 0 ||
3941                 Math.min(d1 + d3, Math.min(d2, d4)) < edgeSelectConstant) {
3942                 // Too much recursion, or ka1 and kb1 are both close to the ends
3943                 // Note: Does not use the edgeSelection function as the indices are not a range
3944                 heapSelectPair(a, l, r, ka1, kb1);
3945                 return;
3946             }
3947 
3948             // Pick a pivot and partition
3949             final int p0 = part.partition(a, l, r,
3950                 pivotingStrategy.pivotIndex(a, l, r, ka),
3951                 upper);
3952             final int p1 = upper[0];
3953 
3954             // Recursion to max depth
3955             // Note: Here we possibly branch left and right with multiple keys.
3956             // It is possible that the partition has split the pair
3957             // and the recursion proceeds with a single point.
3958             maxDepth--;
3959             // Recurse left side if required
3960             if (ka1 < p0) {
3961                 if (kb1 <= p1) {
3962                     // Entirely on left side
3963                     r = p0 - 1;
3964                     kb1 = r < kb1 ? ka1 : kb1;
3965                     continue;
3966                 }
3967                 introselect(part, a, l, p0 - 1, ka1, ka1, maxDepth);
3968                 ka1 = kb1;
3969             }
3970             if (kb1 <= p1) {
3971                 // No right side
3972                 return;
3973             }
3974             // Continue on the right side
3975             l = p1 + 1;
3976             ka1 = ka1 < l ? kb1 : ka1;
3977         }
3978     }
3979 
3980     /**
3981      * Partition the array such that index {@code k} corresponds to its
3982      * correctly sorted value in the equivalent fully sorted array.
3983      *
3984      * <p>For all indices {@code [ka, kb]} and any index {@code i}:
3985      *
3986      * <pre>{@code
3987      * data[i < ka] <= data[ka] <= data[kb] <= data[kb < i]
3988      * }</pre>
3989      *
3990      * <p>This function accepts indices {@code [ka, kb]} that define the
3991      * range of indices to partition. It is expected that the range is small.
3992      *
3993      * <p>Uses an introselect variant. The quickselect is provided as an argument;
3994      * the fall-back on poor convergence of the quickselect is controlled by
3995      * current configuration.
3996      *
3997      * <p>Recursion is monitored by checking the partition is reduced by 2<sup>-x</sup> after
3998      * {@code c} iterations where {@code x} is the
3999      * {@link #setRecursionConstant(int) recursion constant} and {@code c} is the
4000      * {@link #setRecursionMultiple(double) recursion multiple} (variables reused for convenience).
4001      * Confidence bounds for dividing a length by 2<sup>-x</sup> are provided in Valois (2000)
4002      * as {@code c = floor((6/5)x) + b}:
4003      * <pre>
4004      * b  confidence (%)
4005      * 2  76.56
4006      * 3  92.92
4007      * 4  97.83
4008      * 5  99.33
4009      * 6  99.79
4010      * </pre>
4011      * <p>Ideally {@code c >= 3} using {@code x = 1}. E.g. We can use 3 iterations to be 76%
4012      * confident the sequence will divide in half; or 7 iterations to be 99% confident the
4013      * sequence will divide into a quarter. A larger factor {@code b} reduces the sensitivity
4014      * of introspection.
4015      *
4016      * @param part Partition function.
4017      * @param a Values.
4018      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
4019      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
4020      * @param ka First key of interest.
4021      * @param kb Last key of interest.
4022      */
4023     private void introselect2(SPEPartition part, double[] a, int left, int right, int ka, int kb) {
4024         int l = left;
4025         int r = right;
4026         final int[] upper = {0};
4027         int counter = (int) recursionMultiple;
4028         int threshold = (right - left) >>> recursionConstant;
4029         while (true) {
4030             // It is possible to use edgeselect when k is close to the end
4031             // |l|-----|ka|kkkkkkkk|kb|------|r|
4032             if (Math.min(kb - l, r - ka) < edgeSelectConstant) {
4033                 edgeSelection.partition(a, l, r, ka, kb);
4034                 return;
4035             }
4036 
4037             // length - 1
4038             int n = r - l;
4039             if (--counter < 0) {
4040                 if (n > threshold) {
4041                     // Did not reduce the length after set number of iterations.
4042                     // Here riselect (Valois (2000)) would use random points to choose the pivot
4043                     // to inject entropy and restart. This continues until the sum of the partition
4044                     // lengths is too high (twice the original length). Here we just switch.
4045 
4046                     // Note: For testing we trigger the recursion consumer with the remaining length
4047                     recursionConsumer.accept(r - l);
4048                     stopperSelection.partition(a, l, r, ka, kb);
4049                     return;
4050                 }
4051                 // Once the confidence has been achieved we use (6/5)x with x=1.
4052                 // So check every 5/6 iterations that the length is halving.
4053                 if (counter == -5) {
4054                     counter = 1;
4055                 }
4056                 threshold >>>= 1;
4057             }
4058 
4059             // Pick a pivot and partition
4060             int pivot;
4061             if (n > subSamplingSize) {
4062                 // Floyd-Rivest: use SELECT recursively on a sample of size S to get an estimate
4063                 // for the (k-l+1)-th smallest element into a[k], biased slightly so that the
4064                 // (k-l+1)-th element is expected to lie in the smaller set after partitioning.
4065                 ++n;
4066                 final int ith = ka - l + 1;
4067                 final double z = Math.log(n);
4068                 final double s = 0.5 * Math.exp(0.6666666666666666 * z);
4069                 final double sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * Integer.signum(ith - (n >> 1));
4070                 final int ll = Math.max(l, (int) (ka - ith * s / n + sd));
4071                 final int rr = Math.min(r, (int) (ka + (n - ith) * s / n + sd));
4072                 // Optional random sampling
4073                 if ((controlFlags & FLAG_RANDOM_SAMPLING) != 0) {
4074                     final IntUnaryOperator rng = createRNG(n, ka);
4075                     // Shuffle [ll, k) from [l, k)
4076                     if (ll > l) {
4077                         for (int i = ka; i > ll;) {
4078                             // l + rand [0, i - l + 1) : i is currently i+1
4079                             final int j = l + rng.applyAsInt(i - l);
4080                             final double t = a[--i];
4081                             a[i] = a[j];
4082                             a[j] = t;
4083                         }
4084                     }
4085                     // Shuffle (k, rr] from (k, r]
4086                     if (rr < r) {
4087                         for (int i = ka; i < rr;) {
4088                             // r - rand [0, r - i + 1) : i is currently i-1
4089                             final int j = r - rng.applyAsInt(r - i);
4090                             final double t = a[++i];
4091                             a[i] = a[j];
4092                             a[j] = t;
4093                         }
4094                     }
4095                 }
4096                 // Sample recursion restarts from [ll, rr]
4097                 introselect2(part, a, ll, rr, ka, ka);
4098                 pivot = ka;
4099             } else {
4100                 // default pivot strategy
4101                 pivot = pivotingStrategy.pivotIndex(a, l, r, ka);
4102             }
4103 
4104             final int p0 = part.partition(a, l, r, pivot, upper);
4105             final int p1 = upper[0];
4106 
4107             // Note: Here we expect [ka, kb] to be small and splitting is unlikely.
4108             //                   p0 p1
4109             // |l|--|ka|kkkk|kb|--|P|-------------------|r|
4110             // |l|----------------|P|--|ka|kkk|kb|------|r|
4111             // |l|-----------|ka|k|P|k|kb|--------------|r|
4112             if (kb < p0) {
4113                 // The element is in the left partition
4114                 r = p0 - 1;
4115             } else if (ka > p1) {
4116                 // The element is in the right partition
4117                 l = p1 + 1;
4118             } else {
4119                 // Pivot splits [ka, kb]. Expect ends to be close to the pivot and finish.
4120                 if (ka < p0) {
4121                     sortSelectRight(a, l, p0 - 1, ka);
4122                 }
4123                 if (kb > p1) {
4124                     sortSelectLeft(a, p1 + 1, r, kb);
4125                 }
4126                 return;
4127             }
4128         }
4129     }
4130 
4131     /**
4132      * Partition the array such that indices {@code k} correspond to their
4133      * correctly sorted value in the equivalent fully sorted array.
4134      *
4135      * <p>For all indices {@code k} and any index {@code i}:
4136      *
4137      * <pre>{@code
4138      * data[i < k] <= data[k] <= data[k < i]
4139      * }</pre>
4140      *
4141      * <p>This function accepts an ordered array of indices {@code k} and pointers
4142      * to the first and last positions in {@code k} that define the range indices
4143      * to partition.
4144      *
4145      * <pre>{@code
4146      * left <= k[ia] <= k[ib] <= right  : ia <= ib
4147      * }</pre>
4148      *
4149      * <p>A binary search is used to search for keys in {@code [ia, ib]}
4150      * to create {@code [ia, ib1]} and {@code [ia1, ib]} if partitioning splits the range.
4151      *
4152      * <p>Uses an introselect variant. The quickselect is provided as an argument;
4153      * the fall-back on poor convergence of the quickselect is a heapselect.
4154      *
4155      * @param part Partition function.
4156      * @param a Values.
4157      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
4158      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
4159      * @param k Indices to partition (ordered).
4160      * @param ia Index of first key.
4161      * @param ib Index of last key.
4162      * @param maxDepth Maximum depth for recursion.
4163      */
4164     private void introselect(SPEPartition part, double[] a, int left, int right,
4165         int[] k, int ia, int ib, int maxDepth) {
4166         // Only one side requires recursion. The other side
4167         // can remain within this function call.
4168         int l = left;
4169         int r = right;
4170         int ia1 = ia;
4171         int ib1 = ib;
4172         final int[] upper = {0};
4173         while (true) {
4174             // Switch to paired key implementation if possible.
4175             // Note: adjacent indices can refer to well separated keys.
4176             // This is the major difference between this implementation
4177             // and an implementation using an IndexInterval (which does not
4178             // have a fast way to determine if there are any keys within the range).
4179             if (ib1 - ia1 <= 1) {
4180                 introselect(part, a, l, r, k[ia1], k[ib1], maxDepth);
4181                 return;
4182             }
4183 
4184             // length - 1
4185             final int n = r - l;
4186             int ka = k[ia1];
4187             final int kb = k[ib1];
4188 
4189             if (n < minQuickSelectSize) {
4190                 // Sort selection on small data
4191                 sortSelectRange(a, l, r, ka, kb);
4192                 return;
4193             }
4194 
4195             // It is possible to use heapselect when ka and kb are close to the same end
4196             // |l|-----|ka|--------|kb|------|r|
4197             //  ---------s2----------
4198             //          ----------s4-----------
4199             if (Math.min(kb - l, r - ka) < edgeSelectConstant) {
4200                 edgeSelection.partition(a, l, r, ka, kb);
4201                 return;
4202             }
4203 
4204             if (maxDepth == 0) {
4205                 // Too much recursion
4206                 heapSelectRange(a, l, r, ka, kb);
4207                 return;
4208             }
4209 
4210             // Pick a pivot and partition
4211             final int p0 = part.partition(a, l, r,
4212                 pivotingStrategy.pivotIndex(a, l, r, ka),
4213                 upper);
4214             final int p1 = upper[0];
4215 
4216             // Recursion to max depth
4217             // Note: Here we possibly branch left and right with multiple keys.
4218             // It is possible that the partition has split the keys
4219             // and the recursion proceeds with a reduced set on either side.
4220             //                   p0 p1
4221             // |l|--|ka|--k----k--|P|------k--|kb|------|r|
4222             //       ia1       iba  |      ia1  ib1
4223             // Search less/greater is bounded at ia1/ib1
4224             maxDepth--;
4225             // Recurse left side if required
4226             if (ka < p0) {
4227                 if (kb <= p1) {
4228                     // Entirely on left side
4229                     r = p0 - 1;
4230                     if (r < kb) {
4231                         ib1 = searchLessOrEqual(k, ia1, ib1, r);
4232                     }
4233                     continue;
4234                 }
4235                 // Require a split here
4236                 introselect(part, a, l, p0 - 1, k, ia1, searchLessOrEqual(k, ia1, ib1, p0 - 1), maxDepth);
4237                 ia1 = searchGreaterOrEqual(k, ia1, ib1, l);
4238                 ka = k[ia1];
4239             }
4240             if (kb <= p1) {
4241                 // No right side
4242                 recursionConsumer.accept(maxDepth);
4243                 return;
4244             }
4245             // Continue on the right side
4246             l = p1 + 1;
4247             if (ka < l) {
4248                 ia1 = searchGreaterOrEqual(k, ia1, ib1, l);
4249             }
4250         }
4251     }
4252 
4253     /**
4254      * Partition the array such that indices {@code k} correspond to their
4255      * correctly sorted value in the equivalent fully sorted array.
4256      *
4257      * <p>For all indices {@code k} and any index {@code i}:
4258      *
4259      * <pre>{@code
4260      * data[i < k] <= data[k] <= data[k < i]
4261      * }</pre>
4262      *
4263      * <p>This function accepts a {@link SearchableInterval} of indices {@code k} and the
4264      * first index {@code ka} and last index {@code kb} that define the range of indices
4265      * to partition. The {@link SearchableInterval} is used to search for keys in {@code [ka, kb]}
4266      * to create {@code [ka, kb1]} and {@code [ka1, kb]} if partitioning splits the range.
4267      *
4268      * <pre>{@code
4269      * left <= ka <= kb <= right
4270      * }</pre>
4271      *
4272      * <p>Uses an introselect variant. The quickselect is provided as an argument;
4273      * the fall-back on poor convergence of the quickselect is a heapselect.
4274      *
4275      * @param part Partition function.
4276      * @param a Values.
4277      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
4278      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
4279      * @param k Interval of indices to partition (ordered).
4280      * @param ka First key.
4281      * @param kb Last key.
4282      * @param maxDepth Maximum depth for recursion.
4283      */
4284     // package-private for benchmarking
4285     void introselect(SPEPartition part, double[] a, int left, int right,
4286         SearchableInterval k, int ka, int kb, int maxDepth) {
4287         // Only one side requires recursion. The other side
4288         // can remain within this function call.
4289         int l = left;
4290         int r = right;
4291         int ka1 = ka;
4292         int kb1 = kb;
4293         final int[] upper = {0};
4294         while (true) {
4295             // length - 1
4296             int n = r - l;
4297 
4298             if (n < minQuickSelectSize) {
4299                 // Sort selection on small data
4300                 sortSelectRange(a, l, r, ka1, kb1);
4301                 recursionConsumer.accept(maxDepth);
4302                 return;
4303             }
4304 
4305             // It is possible to use heapselect when kaa and kb1 are close to the same end
4306             // |l|-----|ka1|--------|kb1|------|r|
4307             //  ---------s2----------
4308             //          ----------s4-----------
4309             if (Math.min(kb1 - l, r - ka1) < edgeSelectConstant) {
4310                 edgeSelection.partition(a, l, r, ka1, kb1);
4311                 recursionConsumer.accept(maxDepth);
4312                 return;
4313             }
4314 
4315             if (maxDepth == 0) {
4316                 // Too much recursion
4317                 heapSelectRange(a, l, r, ka1, kb1);
4318                 recursionConsumer.accept(maxDepth);
4319                 return;
4320             }
4321 
4322             // Pick a pivot and partition
4323             int pivot;
4324             if (n > subSamplingSize) {
4325                 // Floyd-Rivest: use SELECT recursively on a sample of size S to get an estimate
4326                 // for the (k-l+1)-th smallest element into a[k], biased slightly so that the
4327                 // (k-l+1)-th element is expected to lie in the smaller set after partitioning.
4328                 // Note: This targets ka1 and ignores kb1 for pivot selection.
4329                 ++n;
4330                 final int ith = ka1 - l + 1;
4331                 final double z = Math.log(n);
4332                 final double s = 0.5 * Math.exp(0.6666666666666666 * z);
4333                 final double sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * Integer.signum(ith - (n >> 1));
4334                 final int ll = Math.max(l, (int) (ka1 - ith * s / n + sd));
4335                 final int rr = Math.min(r, (int) (ka1 + (n - ith) * s / n + sd));
4336                 // Optional random sampling
4337                 if ((controlFlags & FLAG_RANDOM_SAMPLING) != 0) {
4338                     final IntUnaryOperator rng = createRNG(n, ka1);
4339                     // Shuffle [ll, k) from [l, k)
4340                     if (ll > l) {
4341                         for (int i = ka1; i > ll;) {
4342                             // l + rand [0, i - l + 1) : i is currently i+1
4343                             final int j = l + rng.applyAsInt(i - l);
4344                             final double t = a[--i];
4345                             a[i] = a[j];
4346                             a[j] = t;
4347                         }
4348                     }
4349                     // Shuffle (k, rr] from (k, r]
4350                     if (rr < r) {
4351                         for (int i = ka1; i < rr;) {
4352                             // r - rand [0, r - i + 1) : i is currently i-1
4353                             final int j = r - rng.applyAsInt(r - i);
4354                             final double t = a[++i];
4355                             a[i] = a[j];
4356                             a[j] = t;
4357                         }
4358                     }
4359                 }
4360                 introselect(part, a, ll, rr, k, ka1, ka1, lnNtoMaxDepthSinglePivot(z));
4361                 pivot = ka1;
4362             } else {
4363                 // default pivot strategy
4364                 pivot = pivotingStrategy.pivotIndex(a, l, r, ka1);
4365             }
4366 
4367             final int p0 = part.partition(a, l, r, pivot, upper);
4368             final int p1 = upper[0];
4369 
4370             // Recursion to max depth
4371             // Note: Here we possibly branch left and right with multiple keys.
4372             // It is possible that the partition has split the keys
4373             // and the recursion proceeds with a reduced set on either side.
4374             //                    p0 p1
4375             // |l|--|ka1|--k----k--|P|------k--|kb1|------|r|
4376             //                 kb1  |      ka1
4377             // Search previous/next is bounded at ka1/kb1
4378             maxDepth--;
4379             // Recurse left side if required
4380             if (ka1 < p0) {
4381                 if (kb1 <= p1) {
4382                     // Entirely on left side
4383                     r = p0 - 1;
4384                     if (r < kb1) {
4385                         kb1 = k.previousIndex(r);
4386                     }
4387                     continue;
4388                 }
4389                 introselect(part, a, l, p0 - 1, k, ka1, k.split(p0, p1, upper), maxDepth);
4390                 ka1 = upper[0];
4391             }
4392             if (kb1 <= p1) {
4393                 // No right side
4394                 recursionConsumer.accept(maxDepth);
4395                 return;
4396             }
4397             // Continue on the right side
4398             l = p1 + 1;
4399             if (ka1 < l) {
4400                 ka1 = k.nextIndex(l);
4401             }
4402         }
4403     }
4404 
4405     /**
4406      * Partition the array such that indices {@code k} correspond to their correctly
4407      * sorted value in the equivalent fully sorted array.
4408      *
4409      * <p>For all indices {@code k} and any index {@code i}:
4410      *
4411      * <pre>{@code
4412      * data[i < k] <= data[k] <= data[k < i]
4413      * }</pre>
4414      *
4415      * <p>This function accepts a {@link UpdatingInterval} of indices {@code k} that define the
4416      * range of indices to partition. The {@link UpdatingInterval} can be narrowed or split as
4417      * partitioning divides the range.
4418      *
4419      * <p>Uses an introselect variant. The quickselect is provided as an argument;
4420      * the fall-back on poor convergence of the quickselect is controlled by
4421      * current configuration.
4422      *
4423      * @param part Partition function.
4424      * @param a Values.
4425      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
4426      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
4427      * @param k Interval of indices to partition (ordered).
4428      * @param maxDepth Maximum depth for recursion.
4429      */
4430     // package-private for benchmarking
4431     void introselect(SPEPartition part, double[] a, int left, int right,
4432         UpdatingInterval k, int maxDepth) {
4433         // Only one side requires recursion. The other side
4434         // can remain within this function call.
4435         int l = left;
4436         int r = right;
4437         int ka = k.left();
4438         int kb = k.right();
4439         final int[] upper = {0};
4440         while (true) {
4441             // length - 1
4442             final int n = r - l;
4443 
4444             if (n < minQuickSelectSize) {
4445                 // Sort selection on small data
4446                 sortSelectRange(a, l, r, ka, kb);
4447                 recursionConsumer.accept(maxDepth);
4448                 return;
4449             }
4450 
4451             // It is possible to use heapselect when ka and kb are close to the same end
4452             // |l|-----|ka|--------|kb|------|r|
4453             //  ---------s2----------
4454             //          ----------s4-----------
4455             if (Math.min(kb - l, r - ka) < edgeSelectConstant) {
4456                 edgeSelection.partition(a, l, r, ka, kb);
4457                 recursionConsumer.accept(maxDepth);
4458                 return;
4459             }
4460 
4461             if (maxDepth == 0) {
4462                 // Too much recursion
4463                 heapSelectRange(a, l, r, ka, kb);
4464                 recursionConsumer.accept(maxDepth);
4465                 return;
4466             }
4467 
4468             // Pick a pivot and partition
4469             final int p0 = part.partition(a, l, r,
4470                 pivotingStrategy.pivotIndex(a, l, r, ka),
4471                 upper);
4472             final int p1 = upper[0];
4473 
4474             // Recursion to max depth
4475             // Note: Here we possibly branch left and right with multiple keys.
4476             // It is possible that the partition has split the keys
4477             // and the recursion proceeds with a reduced set on either side.
4478             //                   p0 p1
4479             // |l|--|ka|--k----k--|P|------k--|kb|------|r|
4480             //                 kb  |       ka
4481             maxDepth--;
4482             // Recurse left side if required
4483             if (ka < p0) {
4484                 if (kb <= p1) {
4485                     // Entirely on left side
4486                     r = p0 - 1;
4487                     if (r < kb) {
4488                         kb = k.updateRight(r);
4489                     }
4490                     continue;
4491                 }
4492                 introselect(part, a, l, p0 - 1, k.splitLeft(p0, p1), maxDepth);
4493                 ka = k.left();
4494             } else if (kb <= p1) {
4495                 // No right side
4496                 recursionConsumer.accept(maxDepth);
4497                 return;
4498             } else if (ka <= p1) {
4499                 ka = k.updateLeft(p1 + 1);
4500             }
4501             // Continue on the right side
4502             l = p1 + 1;
4503         }
4504     }
4505 
4506     /**
4507      * Partition the array such that indices {@code k} correspond to their correctly
4508      * sorted value in the equivalent fully sorted array.
4509      *
4510      * <p>For all indices {@code k} and any index {@code i}:
4511      *
4512      * <pre>{@code
4513      * data[i < k] <= data[k] <= data[k < i]
4514      * }</pre>
4515      *
4516      * <p>This function accepts a {@link SplittingInterval} of indices {@code k} that define the
4517      * range of indices to partition. The {@link SplittingInterval} is split as
4518      * partitioning divides the range.
4519      *
4520      * <p>Uses an introselect variant. The quickselect is provided as an argument;
4521      * the fall-back on poor convergence of the quickselect is a heapselect.
4522      *
4523      * @param part Partition function.
4524      * @param a Values.
4525      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
4526      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
4527      * @param keys Interval of indices to partition (ordered).
4528      * @param maxDepth Maximum depth for recursion.
4529      */
4530     // package-private for benchmarking
4531     void introselect(SPEPartition part, double[] a, int left, int right,
4532         SplittingInterval keys, int maxDepth) {
4533         // Only one side requires recursion. The other side
4534         // can remain within this function call.
4535         int l = left;
4536         int r = right;
4537         SplittingInterval k = keys;
4538         int ka = k.left();
4539         int kb = k.right();
4540         final int[] upper = {0};
4541         while (true) {
4542             // length - 1
4543             final int n = r - l;
4544 
4545             if (n < minQuickSelectSize) {
4546                 // Sort selection on small data
4547                 sortSelectRange(a, l, r, ka, kb);
4548                 recursionConsumer.accept(maxDepth);
4549                 return;
4550             }
4551 
4552             // It is possible to use heapselect when ka and kb are close to the same end
4553             // |l|-----|ka|--------|kb|------|r|
4554             //  ---------s2----------
4555             //          ----------s4-----------
4556             if (Math.min(kb - l, r - ka) < edgeSelectConstant) {
4557                 edgeSelection.partition(a, l, r, ka, kb);
4558                 recursionConsumer.accept(maxDepth);
4559                 return;
4560             }
4561 
4562             if (maxDepth == 0) {
4563                 // Too much recursion
4564                 heapSelectRange(a, l, r, ka, kb);
4565                 recursionConsumer.accept(maxDepth);
4566                 return;
4567             }
4568 
4569             // Pick a pivot and partition
4570             final int p0 = part.partition(a, l, r,
4571                 pivotingStrategy.pivotIndex(a, l, r, ka),
4572                 upper);
4573             final int p1 = upper[0];
4574 
4575             // Recursion to max depth
4576             // Note: Here we possibly branch left and right with multiple keys.
4577             // It is possible that the partition has split the keys
4578             // and the recursion proceeds with a reduced set on either side.
4579             //                   p0 p1
4580             // |l|--|ka|--k----k--|P|------k--|kb|------|r|
4581             //                 kb  |       ka
4582             maxDepth--;
4583             final SplittingInterval lk = k.split(p0, p1);
4584             // Recurse left side if required
4585             if (lk != null) {
4586                 // Avoid recursive method calls
4587                 if (k.empty()) {
4588                     // Entirely on left side
4589                     r = p0 - 1;
4590                     kb = lk.right();
4591                     k = lk;
4592                     continue;
4593                 }
4594                 introselect(part, a, l, p0 - 1, lk, maxDepth);
4595             }
4596             if (k.empty()) {
4597                 // No right side
4598                 recursionConsumer.accept(maxDepth);
4599                 return;
4600             }
4601             // Continue on the right side
4602             l = p1 + 1;
4603             ka = k.left();
4604         }
4605     }
4606 
4607     /**
4608      * Partition the array such that indices {@code k} correspond to their correctly
4609      * sorted value in the equivalent fully sorted array.
4610      *
4611      * <p>For all indices {@code k} and any index {@code i}:
4612      *
4613      * <pre>{@code
4614      * data[i < k] <= data[k] <= data[k < i]
4615      * }</pre>
4616      *
4617      * <p>This function accepts an {@link IndexIterator} of indices {@code k}; for
4618      * convenience the lower and upper indices of the current interval are passed as the
4619      * first index {@code ka} and last index {@code kb} of the closed interval of indices
4620      * to partition. These may be within the lower and upper indices if the interval was
4621      * split during recursion: {@code lower <= ka <= kb <= upper}.
4622      *
4623      * <p>The data is recursively partitioned using left-most ordering. When the current
4624      * interval has been partitioned the {@link IndexIterator} is used to advance to the
4625      * next interval to partition.
4626      *
4627      * <p>Uses an introselect variant. The quickselect is provided as an argument; the
4628      * fall-back on poor convergence of the quickselect is a heapselect.
4629      *
4630      * @param part Partition function.
4631      * @param a Values.
4632      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
4633      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
4634      * @param k Interval of indices to partition (ordered).
4635      * @param ka First key.
4636      * @param kb Last key.
4637      * @param maxDepth Maximum depth for recursion.
4638      */
4639     // package-private for benchmarking
4640     void introselect(SPEPartition part, double[] a, int left, int right,
4641         IndexIterator k, int ka, int kb, int maxDepth) {
4642         // Left side requires recursion; right side remains within this function
4643         // When this function returns all indices in [left, right] must be processed.
4644         int l = left;
4645         int lo = ka;
4646         int hi = kb;
4647         final int[] upper = {0};
4648         while (true) {
4649             if (maxDepth == 0) {
4650                 // Too much recursion.
4651                 // Advance the iterator to the end of the current range.
4652                 // Note: heapSelectRange handles hi > right.
4653                 // Single API method: advanceBeyond(right): return hi <= right
4654                 while (hi < right && k.next()) {
4655                     hi = k.right();
4656                 }
4657                 heapSelectRange(a, l, right, lo, hi);
4658                 recursionConsumer.accept(maxDepth);
4659                 return;
4660             }
4661 
4662             // length - 1
4663             final int n = right - l;
4664 
4665             // If interval is close to one end then edgeselect.
4666             // Only elect left if there are no further indices in the range.
4667             // |l|-----|lo|--------|hi|------|right|
4668             //  ---------d1----------
4669             //          --------------d2-----------
4670             if (Math.min(hi - l, right - lo) < edgeSelectConstant) {
4671                 if (hi - l > right - lo) {
4672                     // Right end. Do not check above hi, just select to the end
4673                     edgeSelection.partition(a, l, right, lo, right);
4674                     recursionConsumer.accept(maxDepth);
4675                     return;
4676                 } else if (k.nextAfter(right)) {
4677                     // Left end
4678                     // Only if no further indices in the range.
4679                     // If false this branch will continue to be triggered until
4680                     // a partition is made to separate the next indices.
4681                     edgeSelection.partition(a, l, right, lo, hi);
4682                     recursionConsumer.accept(maxDepth);
4683                     // Advance iterator
4684                     l = hi + 1;
4685                     if (!k.positionAfter(hi) || Math.max(k.left(), l) > right) {
4686                         // No more keys, or keys beyond the current bounds
4687                         return;
4688                     }
4689                     lo = Math.max(k.left(), l);
4690                     hi = Math.min(right, k.right());
4691                     // Continue right (allows a second heap select for the right side)
4692                     continue;
4693                 }
4694             }
4695 
4696             // If interval is close to both ends then full sort
4697             // |l|-----|lo|--------|hi|------|right|
4698             //  ---d1----
4699             //                       ----d2--------
4700             // (lo - l) + (right - hi) == (right - l) - (hi - lo)
4701             if (n - (hi - lo) < minQuickSelectSize) {
4702                 // Handle small data. This is done as the JDK sort will
4703                 // use insertion sort for small data. For double data it
4704                 // will also pre-process the data for NaN and signed
4705                 // zeros which is an overhead to avoid.
4706                 if (n < minQuickSelectSize) {
4707                     // Must not use sortSelectRange in [lo, hi] as the iterator
4708                     // has not been advanced to check after hi
4709                     sortSelectRight(a, l, right, lo);
4710                 } else {
4711                     // Note: This disregards the current level of recursion
4712                     // but can exploit the JDK's more advanced sort algorithm.
4713                     Arrays.sort(a, l, right + 1);
4714                 }
4715                 recursionConsumer.accept(maxDepth);
4716                 return;
4717             }
4718 
4719             // Here: l <= lo <= hi <= right
4720             // Pick a pivot and partition
4721             final int p0 = part.partition(a, l, right,
4722                 pivotingStrategy.pivotIndex(a, l, right, ka),
4723                 upper);
4724             final int p1 = upper[0];
4725 
4726             maxDepth--;
4727             // Recursion left
4728             if (lo < p0) {
4729                 introselect(part, a, l, p0 - 1, k, lo, Math.min(hi, p0 - 1), maxDepth);
4730                 // Advance iterator
4731                 // Single API method: fastForwardAndLeftWithin(p1, right)
4732                 if (!k.positionAfter(p1) || k.left() > right) {
4733                     // No more keys, or keys beyond the current bounds
4734                     return;
4735                 }
4736                 lo = k.left();
4737                 hi = Math.min(right, k.right());
4738             }
4739             if (hi <= p1) {
4740                 // Advance iterator
4741                 if (!k.positionAfter(p1) || k.left() > right) {
4742                     // No more keys, or keys beyond the current bounds
4743                     return;
4744                 }
4745                 lo = k.left();
4746                 hi = Math.min(right, k.right());
4747             }
4748             // Continue right
4749             l = p1 + 1;
4750             lo = Math.max(lo, l);
4751         }
4752     }
4753 
4754     /**
4755      * Partition the array such that indices {@code k} correspond to their correctly
4756      * sorted value in the equivalent fully sorted array. For all indices {@code k}
4757      * and any index {@code i}:
4758      *
4759      * <pre>{@code
4760      * data[i < k] <= data[k] <= data[k < i]
4761      * }</pre>
4762      *
4763      * <p>All indices are assumed to be within {@code [0, right]}.
4764      *
4765      * <p>Uses an introselect variant. The dual pivot quickselect is provided as an argument;
4766      * the fall-back on poor convergence of the quickselect is controlled by
4767      * current configuration.
4768      *
4769      * <p>The partition method is not required to handle signed zeros.
4770      *
4771      * @param part Partition function.
4772      * @param a Values.
4773      * @param k Indices (may be destructively modified).
4774      * @param count Count of indices (assumed to be strictly positive).
4775      */
4776     void introselect(DPPartition part, double[] a, int[] k, int count) {
4777         // Handle NaN / signed zeros
4778         final DoubleDataTransformer t = SORT_TRANSFORMER.get();
4779         // Assume this is in-place
4780         t.preProcess(a);
4781         final int end = t.length();
4782         int n = count;
4783         if (end > 1) {
4784             // Filter indices invalidated by NaN check
4785             if (end < a.length) {
4786                 for (int i = n; --i >= 0;) {
4787                     final int v = k[i];
4788                     if (v >= end) {
4789                         // swap(k, i, --n)
4790                         k[i] = k[--n];
4791                         k[n] = v;
4792                     }
4793                 }
4794             }
4795             introselect(part, a, end - 1, k, n);
4796         }
4797         // Restore signed zeros
4798         t.postProcess(a, k, n);
4799     }
4800 
4801     /**
4802      * Partition the array such that indices {@code k} correspond to their correctly
4803      * sorted value in the equivalent fully sorted array. For all indices {@code k}
4804      * and any index {@code i}:
4805      *
4806      * <pre>{@code
4807      * data[i < k] <= data[k] <= data[k < i]
4808      * }</pre>
4809      *
4810      * <p>All indices are assumed to be within {@code [0, right]}.
4811      *
4812      * <p>Uses an introselect variant. The dual pivot quickselect is provided as an argument;
4813      * the fall-back on poor convergence of the quickselect is controlled by
4814      * current configuration.
4815      *
4816      * <p>This function assumes {@code n > 0} and {@code right > 0}; otherwise
4817      * there is nothing to do.
4818      *
4819      * @param part Partition function.
4820      * @param a Values.
4821      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
4822      * @param k Indices (may be destructively modified).
4823      * @param n Count of indices (assumed to be strictly positive).
4824      */
4825     private void introselect(DPPartition part, double[] a, int right, int[] k, int n) {
4826         if (n < 1) {
4827             return;
4828         }
4829         final int maxDepth = createMaxDepthDualPivot(right + 1);
4830         // Handle cases without multiple keys
4831         if (n == 1) {
4832             if (pairedKeyStrategy == PairedKeyStrategy.PAIRED_KEYS) {
4833                 // Dedicated method for a single key
4834                 introselect(part, a, 0, right, k[0], maxDepth);
4835             } else if (pairedKeyStrategy == PairedKeyStrategy.TWO_KEYS) {
4836                 // Dedicated method for two keys using the same key
4837                 introselect(part, a, 0, right, k[0], k[0], maxDepth);
4838             } else if (pairedKeyStrategy == PairedKeyStrategy.SEARCHABLE_INTERVAL) {
4839                 // Reuse the IndexInterval method using the same key
4840                 introselect(part, a, 0, right, IndexIntervals.anyIndex(), k[0], k[0], maxDepth);
4841             } else if (pairedKeyStrategy == PairedKeyStrategy.UPDATING_INTERVAL) {
4842                 // Reuse the Interval method using a single key
4843                 introselect(part, a, 0, right, IndexIntervals.interval(k[0]), maxDepth);
4844             } else {
4845                 throw new IllegalStateException(UNSUPPORTED_INTROSELECT + pairedKeyStrategy);
4846             }
4847             return;
4848         }
4849         // Special case for partition around adjacent indices (for interpolation)
4850         if (n == 2 && k[0] + 1 == k[1]) {
4851             if (pairedKeyStrategy == PairedKeyStrategy.PAIRED_KEYS) {
4852                 // Dedicated method for a single key, returns information about k+1
4853                 final int p = introselect(part, a, 0, right, k[0], maxDepth);
4854                 // p <= k to signal k+1 is unsorted, or p+1 is a pivot.
4855                 // if k is sorted, and p+1 is sorted, k+1 is sorted if k+1 == p.
4856                 if (p > k[1]) {
4857                     selectMinIgnoreZeros(a, k[1], p);
4858                 }
4859             } else if (pairedKeyStrategy == PairedKeyStrategy.TWO_KEYS) {
4860                 // Dedicated method for two keys
4861                 // Note: This can handle keys that are not adjacent
4862                 // e.g. keys near opposite ends without a partition step.
4863                 final int ka = Math.min(k[0], k[1]);
4864                 final int kb = Math.max(k[0], k[1]);
4865                 introselect(part, a, 0, right, ka, kb, maxDepth);
4866             } else if (pairedKeyStrategy == PairedKeyStrategy.SEARCHABLE_INTERVAL) {
4867                 // Reuse the IndexInterval method using a range of two keys
4868                 introselect(part, a, 0, right, IndexIntervals.anyIndex(), k[0], k[1], maxDepth);
4869             } else if (pairedKeyStrategy == PairedKeyStrategy.UPDATING_INTERVAL) {
4870                 // Reuse the Interval method using a range of two keys
4871                 introselect(part, a, 0, right, IndexIntervals.interval(k[0], k[1]), maxDepth);
4872             } else {
4873                 throw new IllegalStateException(UNSUPPORTED_INTROSELECT + pairedKeyStrategy);
4874             }
4875             return;
4876         }
4877 
4878         // Detect possible saturated range.
4879         // minimum keys = 10
4880         // min separation = 2^3  (could use log2(minQuickSelectSize) here)
4881         // saturation = 0.95
4882         //if (keysAreSaturated(right + 1, k, n, 10, 3, 0.95)) {
4883         //    Arrays.sort(a, 0, right + 1);
4884         //    return;
4885         //}
4886 
4887         // Note: Sorting to unique keys is an overhead. This can be eliminated
4888         // by requesting the caller passes sorted keys (or quantiles in order).
4889 
4890         if (keyStrategy == KeyStrategy.ORDERED_KEYS) {
4891             // DP does not offer ORDERED_KEYS implementation but we include the branch
4892             // for completeness.
4893             throw new IllegalStateException(UNSUPPORTED_INTROSELECT + keyStrategy);
4894         } else if (keyStrategy == KeyStrategy.SCANNING_KEY_SEARCHABLE_INTERVAL) {
4895             final int unique = Sorting.sortIndices(k, n);
4896             final SearchableInterval keys = ScanningKeyInterval.of(k, unique);
4897             introselect(part, a, 0, right, keys, keys.left(), keys.right(), maxDepth);
4898         } else if (keyStrategy == KeyStrategy.SEARCH_KEY_SEARCHABLE_INTERVAL) {
4899             final int unique = Sorting.sortIndices(k, n);
4900             final SearchableInterval keys = BinarySearchKeyInterval.of(k, unique);
4901             introselect(part, a, 0, right, keys, keys.left(), keys.right(), maxDepth);
4902         } else if (keyStrategy == KeyStrategy.COMPRESSED_INDEX_SET) {
4903             final SearchableInterval keys = CompressedIndexSet.of(compression, k, n);
4904             introselect(part, a, 0, right, keys, keys.left(), keys.right(), maxDepth);
4905         } else if (keyStrategy == KeyStrategy.INDEX_SET) {
4906             final SearchableInterval keys = IndexSet.of(k, n);
4907             introselect(part, a, 0, right, keys, keys.left(), keys.right(), maxDepth);
4908         } else if (keyStrategy == KeyStrategy.KEY_UPDATING_INTERVAL) {
4909             final int unique = Sorting.sortIndices(k, n);
4910             final UpdatingInterval keys = KeyUpdatingInterval.of(k, unique);
4911             introselect(part, a, 0, right, keys, maxDepth);
4912         } else if (keyStrategy == KeyStrategy.INDEX_SET_UPDATING_INTERVAL) {
4913             final UpdatingInterval keys = BitIndexUpdatingInterval.of(k, n);
4914             introselect(part, a, 0, right, keys, maxDepth);
4915         } else if (keyStrategy == KeyStrategy.KEY_SPLITTING_INTERVAL) {
4916             final int unique = Sorting.sortIndices(k, n);
4917             final SplittingInterval keys = KeyUpdatingInterval.of(k, unique);
4918             introselect(part, a, 0, right, keys, maxDepth);
4919         } else if (keyStrategy == KeyStrategy.INDEX_SET_SPLITTING_INTERVAL) {
4920             final SplittingInterval keys = BitIndexUpdatingInterval.of(k, n);
4921             introselect(part, a, 0, right, keys, maxDepth);
4922         } else if (keyStrategy == KeyStrategy.INDEX_ITERATOR) {
4923             final int unique = Sorting.sortIndices(k, n);
4924             final IndexIterator keys = KeyIndexIterator.of(k, unique);
4925             introselect(part, a, 0, right, keys, keys.left(), keys.right(), maxDepth);
4926         } else if (keyStrategy == KeyStrategy.COMPRESSED_INDEX_ITERATOR) {
4927             final IndexIterator keys = CompressedIndexSet.iterator(compression, k, n);
4928             introselect(part, a, 0, right, keys, keys.left(), keys.right(), maxDepth);
4929         } else {
4930             throw new IllegalStateException(UNSUPPORTED_INTROSELECT + keyStrategy);
4931         }
4932     }
4933 
4934     /**
4935      * Partition the array such that index {@code k} corresponds to its
4936      * correctly sorted value in the equivalent fully sorted array.
4937      *
4938      * <pre>{@code
4939      * data[i < k] <= data[k] <= data[k < i]
4940      * }</pre>
4941      *
4942      * <p>Uses an introselect variant. The quickselect is provided as an argument;
4943      * the fall-back on poor convergence of the quickselect is controlled by
4944      * current configuration.
4945      *
4946      * <p>Returns information {@code p} on whether {@code k+1} is sorted.
4947      * If {@code p <= k} then {@code k+1} is sorted.
4948      * If {@code p > k} then {@code p+1} is a pivot.
4949      *
4950      * @param part Partition function.
4951      * @param a Values.
4952      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
4953      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
4954      * @param k Index.
4955      * @param maxDepth Maximum depth for recursion.
4956      * @return the index {@code p}
4957      */
4958     private int introselect(DPPartition part, double[] a, int left, int right,
4959         int k, int maxDepth) {
4960         int l = left;
4961         int r = right;
4962         final int[] upper = {0, 0, 0};
4963         while (true) {
4964             // It is possible to use edgeselect when k is close to the end
4965             // |l|-----|k|---------|k|--------|r|
4966             //  ---d1----
4967             //                      -----d3----
4968             final int d1 = k - l;
4969             final int d3 = r - k;
4970             if (Math.min(d1, d3) < edgeSelectConstant) {
4971                 edgeSelection.partition(a, l, r, k, k);
4972                 // Last known unsorted value >= k
4973                 return r;
4974             }
4975 
4976             if (maxDepth == 0) {
4977                 // Too much recursion
4978                 stopperSelection.partition(a, l, r, k, k);
4979                 // Last known unsorted value >= k
4980                 return r;
4981             }
4982 
4983             // Pick 2 pivots and partition
4984             int p0 = dualPivotingStrategy.pivotIndex(a, l, r, upper);
4985             p0 = part.partition(a, l, r, p0, upper[0], upper);
4986             final int p1 = upper[0];
4987             final int p2 = upper[1];
4988             final int p3 = upper[2];
4989 
4990             maxDepth--;
4991             if (k < p0) {
4992                 // The element is in the left partition
4993                 r = p0 - 1;
4994                 continue;
4995             } else if (k > p3) {
4996                 // The element is in the right partition
4997                 l = p3 + 1;
4998                 continue;
4999             }
5000             // Check the interval overlaps the middle; and the middle exists.
5001             //                    p0 p1                p2 p3
5002             // |l|-----------------|P|------------------|P|----|r|
5003             // Eliminate:     ----kb1                    ka1----
5004             if (k <= p1 || p2 <= k || p2 - p1 <= 2) {
5005                 // Signal if k+1 is sorted.
5006                 // This can be true if the pivots were ranges [p0, p1] or [p2, p3]
5007                 // This check will match *most* sorted k for the 3 eliminated cases.
5008                 // It will not identify p2 - p1 <= 2 when k == p1. In this case
5009                 // k+1 is sorted and a min-select for k+1 is a fast scan up to r.
5010                 return k != p1 && k < p3 ? k : r;
5011             }
5012             // Continue in the middle partition
5013             l = p1 + 1;
5014             r = p2 - 1;
5015         }
5016     }
5017 
5018     /**
5019      * Partition the array such that indices {@code ka} and {@code kb} correspond to their
5020      * correctly sorted value in the equivalent fully sorted array.
5021      *
5022      * <p>For all indices {@code k} and any index {@code i}:
5023      *
5024      * <pre>{@code
5025      * data[i < k] <= data[k] <= data[k < i]
5026      * }</pre>
5027      *
5028      * <p>Note: Requires {@code ka <= kb}. The use of two indices is to support processing
5029      * of pairs of indices {@code (k, k+1)}. However the indices are treated independently
5030      * and partitioned by recursion. They may be equal, neighbours or well separated.
5031      *
5032      * <p>Uses an introselect variant. The quickselect is provided as an argument; the
5033      * fall-back on poor convergence of the quickselect is a heapselect.
5034      *
5035      * @param part Partition function.
5036      * @param a Values.
5037      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
5038      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
5039      * @param ka Index.
5040      * @param kb Index.
5041      * @param maxDepth Maximum depth for recursion.
5042      */
5043     private void introselect(DPPartition part, double[] a, int left, int right,
5044         int ka, int kb, int maxDepth) {
5045         // Only one side requires recursion. The other side
5046         // can remain within this function call.
5047         int l = left;
5048         int r = right;
5049         int ka1 = ka;
5050         int kb1 = kb;
5051         final int[] upper = {0, 0, 0};
5052         while (true) {
5053             // length - 1
5054             final int n = r - l;
5055 
5056             if (n < minQuickSelectSize) {
5057                 // Sort selection on small data
5058                 sortSelectRange(a, l, r, ka1, kb1);
5059                 return;
5060             }
5061 
5062             // It is possible to use heapselect when ka1 and kb1 are close to the ends
5063             // |l|-----|ka1|--------|kb1|------|r|
5064             //  ---s1----
5065             //                       -----s3----
5066             //  ---------s2-----------
5067             //          ----------s4-----------
5068             final int s1 = ka1 - l;
5069             final int s2 = kb1 - l;
5070             final int s3 = r - kb1;
5071             final int s4 = r - ka1;
5072             if (maxDepth == 0 ||
5073                 Math.min(s1 + s3, Math.min(s2, s4)) < edgeSelectConstant) {
5074                 // Too much recursion, or ka1 and kb1 are both close to the ends
5075                 // Note: Does not use the edgeSelection function as the indices are not a range
5076                 heapSelectPair(a, l, r, ka1, kb1);
5077                 return;
5078             }
5079 
5080             // Pick 2 pivots and partition
5081             int p0 = dualPivotingStrategy.pivotIndex(a, l, r, upper);
5082             p0 = part.partition(a, l, r, p0, upper[0], upper);
5083             final int p1 = upper[0];
5084             final int p2 = upper[1];
5085             final int p3 = upper[2];
5086 
5087             // Recursion to max depth
5088             // Note: Here we possibly branch left and right with multiple keys.
5089             // It is possible that the partition has split the pair
5090             // and the recursion proceeds with a single point.
5091             maxDepth--;
5092             // Recurse left side if required
5093             if (ka1 < p0) {
5094                 if (kb1 <= p1) {
5095                     // Entirely on left side
5096                     r = p0 - 1;
5097                     kb1 = r < kb1 ? ka1 : kb1;
5098                     continue;
5099                 }
5100                 introselect(part, a, l, p0 - 1, ka1, ka1, maxDepth);
5101                 // Here we must process middle and possibly right
5102                 ka1 = kb1;
5103             }
5104             // Recurse middle if required
5105             // Check the either k is in the range (p1, p2)
5106             //                    p0 p1                p2 p3
5107             // |l|-----------------|P|------------------|P|----|r|
5108             if (ka1 < p2 && ka1 > p1 || kb1 < p2 && kb1 > p1) {
5109                 // Advance lower bound
5110                 l = p1 + 1;
5111                 ka1 = ka1 < l ? kb1 : ka1;
5112                 if (kb1 <= p3) {
5113                     // Entirely in middle
5114                     r = p2 - 1;
5115                     kb1 = r < kb1 ? ka1 : kb1;
5116                     continue;
5117                 }
5118                 introselect(part, a, l, p2 - 1, ka1, ka1, maxDepth);
5119                 // Here we must process right
5120                 ka1 = kb1;
5121             }
5122             if (kb1 <= p3) {
5123                 // No right side
5124                 return;
5125             }
5126             // Continue right
5127             l = p3 + 1;
5128             ka1 = ka1 < l ? kb1 : ka1;
5129         }
5130     }
5131 
5132     /**
5133      * Partition the array such that indices {@code k} correspond to their
5134      * correctly sorted value in the equivalent fully sorted array.
5135      *
5136      * <p>For all indices {@code k} and any index {@code i}:
5137      *
5138      * <pre>{@code
5139      * data[i < k] <= data[k] <= data[k < i]
5140      * }</pre>
5141      *
5142      * <p>This function accepts a {@link SearchableInterval} of indices {@code k} and the
5143      * first index {@code ka} and last index {@code kb} that define the range of indices
5144      * to partition. The {@link SearchableInterval} is used to search for keys in {@code [ka, kb]}
5145      * to create {@code [ka, kb1]} and {@code [ka1, kb]} if partitioning splits the range.
5146      *
5147      * <pre>{@code
5148      * left <= ka <= kb <= right
5149      * }</pre>
5150      *
5151      * <p>Uses an introselect variant. The dual pivot quickselect is provided as an argument;
5152      * the fall-back on poor convergence of the quickselect is a heapselect.
5153      *
5154      * @param part Partition function.
5155      * @param a Values.
5156      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
5157      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
5158      * @param k Interval of indices to partition (ordered).
5159      * @param ka First key.
5160      * @param kb Last key.
5161      * @param maxDepth Maximum depth for recursion.
5162      */
5163     // package-private for benchmarking
5164     void introselect(DPPartition part, double[] a, int left, int right,
5165         SearchableInterval k, int ka, int kb, int maxDepth) {
5166         // If partitioning splits the interval then recursion is used for left and/or
5167         // right sides and the middle remains within this function. If partitioning does
5168         // not split the interval then it remains within this function.
5169         int l = left;
5170         int r = right;
5171         int ka1 = ka;
5172         int kb1 = kb;
5173         final int[] upper = {0, 0, 0};
5174         while (true) {
5175             // length - 1
5176             final int n = r - l;
5177 
5178             if (n < minQuickSelectSize) {
5179                 // Sort selection on small data
5180                 sortSelectRange(a, l, r, ka1, kb1);
5181                 recursionConsumer.accept(maxDepth);
5182                 return;
5183             }
5184 
5185             // It is possible to use heapselect when ka1 and kb1 are close to the same end
5186             // |l|-----|ka1|--------|kb1|------|r|
5187             //  ---------s2-----------
5188             //          ----------s4-----------
5189             if (Math.min(kb1 - l, r - ka1) < edgeSelectConstant) {
5190                 edgeSelection.partition(a, l, r, ka1, kb1);
5191                 recursionConsumer.accept(maxDepth);
5192                 return;
5193             }
5194 
5195             if (maxDepth == 0) {
5196                 // Too much recursion
5197                 heapSelectRange(a, l, r, ka1, kb1);
5198                 recursionConsumer.accept(maxDepth);
5199                 return;
5200             }
5201 
5202             // Pick 2 pivots and partition
5203             int p0 = dualPivotingStrategy.pivotIndex(a, l, r, upper);
5204             p0 = part.partition(a, l, r, p0, upper[0], upper);
5205             final int p1 = upper[0];
5206             final int p2 = upper[1];
5207             final int p3 = upper[2];
5208 
5209             // Recursion to max depth
5210             // Note: Here we possibly branch left, middle and right with multiple keys.
5211             // It is possible that the partition has split the keys
5212             // and the recursion proceeds with a reduced set in each region.
5213             //                    p0 p1                p2 p3
5214             // |l|--|ka1|--k----k--|P|------k--|kb1|----|P|----|r|
5215             //                 kb1  |      ka1
5216             // Search previous/next is bounded at ka1/kb1
5217             maxDepth--;
5218             // Recurse left side if required
5219             if (ka1 < p0) {
5220                 if (kb1 <= p1) {
5221                     // Entirely on left side
5222                     r = p0 - 1;
5223                     if (r < kb1) {
5224                         kb1 = k.previousIndex(r);
5225                     }
5226                     continue;
5227                 }
5228                 introselect(part, a, l, p0 - 1, k, ka1, k.split(p0, p1, upper), maxDepth);
5229                 ka1 = upper[0];
5230             }
5231             // Recurse right side if required
5232             if (kb1 > p3) {
5233                 if (ka1 >= p2) {
5234                     // Entirely on right-side
5235                     l = p3 + 1;
5236                     if (ka1 < l) {
5237                         ka1 = k.nextIndex(l);
5238                     }
5239                     continue;
5240                 }
5241                 final int lo = k.split(p2, p3, upper);
5242                 introselect(part, a, p3 + 1, r, k, upper[0], kb1, maxDepth);
5243                 kb1 = lo;
5244             }
5245             // Check the interval overlaps the middle; and the middle exists.
5246             //                    p0 p1                p2 p3
5247             // |l|-----------------|P|------------------|P|----|r|
5248             // Eliminate:     ----kb1                    ka1----
5249             if (kb1 <= p1 || p2 <= ka1 || p2 - p1 <= 2) {
5250                 // No middle
5251                 recursionConsumer.accept(maxDepth);
5252                 return;
5253             }
5254             l = p1 + 1;
5255             r = p2 - 1;
5256             // Interval [ka1, kb1] overlaps the middle but there may be nothing in the interval.
5257             // |l|-----------------|P|------------------|P|----|r|
5258             // Eliminate:          ka1                  kb1
5259             // Detect this if ka1 is advanced too far.
5260             if (ka1 < l) {
5261                 ka1 = k.nextIndex(l);
5262                 if (ka1 > r) {
5263                     // No middle
5264                     recursionConsumer.accept(maxDepth);
5265                     return;
5266                 }
5267             }
5268             if (r < kb1) {
5269                 kb1 = k.previousIndex(r);
5270             }
5271         }
5272     }
5273 
5274     /**
5275      * Partition the array such that indices {@code k} correspond to their
5276      * correctly sorted value in the equivalent fully sorted array.
5277      *
5278      * <p>For all indices {@code k} and any index {@code i}:
5279      *
5280      * <pre>{@code
5281      * data[i < k] <= data[k] <= data[k < i]
5282      * }</pre>
5283      *
5284      * <p>This function accepts a {@link UpdatingInterval} of indices {@code k} that define the
5285      * range of indices to partition. The {@link UpdatingInterval} can be narrowed or split as
5286      * partitioning divides the range.
5287      *
5288      * <p>Uses an introselect variant. The dual pivot quickselect is provided as an argument;
5289      * the fall-back on poor convergence of the quickselect is a heapselect.
5290      *
5291      * @param part Partition function.
5292      * @param a Values.
5293      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
5294      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
5295      * @param k Interval of indices to partition (ordered).
5296      * @param maxDepth Maximum depth for recursion.
5297      */
5298     // package-private for benchmarking
5299     void introselect(DPPartition part, double[] a, int left, int right,
5300         UpdatingInterval k, int maxDepth) {
5301         // If partitioning splits the interval then recursion is used for left and/or
5302         // right sides and the middle remains within this function. If partitioning does
5303         // not split the interval then it remains within this function.
5304         int l = left;
5305         int r = right;
5306         int ka = k.left();
5307         int kb = k.right();
5308         final int[] upper = {0, 0, 0};
5309         while (true) {
5310             // length - 1
5311             final int n = r - l;
5312 
5313             if (n < minQuickSelectSize) {
5314                 // Sort selection on small data
5315                 sortSelectRange(a, l, r, ka, kb);
5316                 recursionConsumer.accept(maxDepth);
5317                 return;
5318             }
5319 
5320             // It is possible to use heapselect when ka and kb are close to the same end
5321             // |l|-----|ka|--------|kb|------|r|
5322             //  ---------s2-----------
5323             //          ----------s4-----------
5324             if (Math.min(kb - l, r - ka) < edgeSelectConstant) {
5325                 edgeSelection.partition(a, l, r, ka, kb);
5326                 recursionConsumer.accept(maxDepth);
5327                 return;
5328             }
5329 
5330             if (maxDepth == 0) {
5331                 // Too much recursion
5332                 heapSelectRange(a, l, r, ka, kb);
5333                 recursionConsumer.accept(maxDepth);
5334                 return;
5335             }
5336 
5337             // Pick 2 pivots and partition
5338             int p0 = dualPivotingStrategy.pivotIndex(a, l, r, upper);
5339             p0 = part.partition(a, l, r, p0, upper[0], upper);
5340             final int p1 = upper[0];
5341             final int p2 = upper[1];
5342             final int p3 = upper[2];
5343 
5344             // Recursion to max depth
5345             // Note: Here we possibly branch left, middle and right with multiple keys.
5346             // It is possible that the partition has split the keys
5347             // and the recursion proceeds with a reduced set in each region.
5348             //                   p0 p1               p2 p3
5349             // |l|--|ka|--k----k--|P|------k--|kb|----|P|----|r|
5350             //                 kb  |      ka
5351             // Search previous/next is bounded at ka/kb
5352             maxDepth--;
5353             // Recurse left side if required
5354             if (ka < p0) {
5355                 if (kb <= p1) {
5356                     // Entirely on left side
5357                     r = p0 - 1;
5358                     if (r < kb) {
5359                         kb = k.updateRight(r);
5360                     }
5361                     continue;
5362                 }
5363                 introselect(part, a, l, p0 - 1, k.splitLeft(p0, p1), maxDepth);
5364                 ka = k.left();
5365             } else if (kb <= p1) {
5366                 // No middle/right side
5367                 return;
5368             } else if (ka <= p1) {
5369                 // Advance lower bound
5370                 ka = k.updateLeft(p1 + 1);
5371             }
5372             // Recurse middle if required
5373             if (ka < p2) {
5374                 l = p1 + 1;
5375                 if (kb <= p3) {
5376                     // Entirely in middle
5377                     r = p2 - 1;
5378                     if (r < kb) {
5379                         kb = k.updateRight(r);
5380                     }
5381                     continue;
5382                 }
5383                 introselect(part, a, l, p2 - 1, k.splitLeft(p2, p3), maxDepth);
5384                 ka = k.left();
5385             } else if (kb <= p3) {
5386                 // No right side
5387                 return;
5388             } else if (ka <= p3) {
5389                 ka = k.updateLeft(p3 + 1);
5390             }
5391             // Continue right
5392             l = p3 + 1;
5393         }
5394     }
5395 
5396     /**
5397      * Partition the array such that indices {@code k} correspond to their
5398      * correctly sorted value in the equivalent fully sorted array.
5399      *
5400      * <p>For all indices {@code k} and any index {@code i}:
5401      *
5402      * <pre>{@code
5403      * data[i < k] <= data[k] <= data[k < i]
5404      * }</pre>
5405      *
5406      * <p>This function accepts a {@link SplittingInterval} of indices {@code k} that define the
5407      * range of indices to partition. The {@link SplittingInterval} is split as
5408      * partitioning divides the range.
5409      *
5410      * <p>Uses an introselect variant. The dual pivot quickselect is provided as an argument;
5411      * the fall-back on poor convergence of the quickselect is a heapselect.
5412      *
5413      * @param part Partition function.
5414      * @param a Values.
5415      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
5416      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
5417      * @param k Interval of indices to partition (ordered).
5418      * @param maxDepth Maximum depth for recursion.
5419      */
5420     // package-private for benchmarking
5421     void introselect(DPPartition part, double[] a, int left, int right,
5422         SplittingInterval k, int maxDepth) {
5423         // If partitioning splits the interval then recursion is used for left and/or
5424         // right sides and the middle remains within this function. If partitioning does
5425         // not split the interval then it remains within this function.
5426         int l = left;
5427         int r = right;
5428         int ka = k.left();
5429         int kb = k.right();
5430         final int[] upper = {0, 0, 0};
5431         while (true) {
5432             // length - 1
5433             final int n = r - l;
5434 
5435             if (n < minQuickSelectSize) {
5436                 // Sort selection on small data
5437                 sortSelectRange(a, l, r, ka, kb);
5438                 recursionConsumer.accept(maxDepth);
5439                 return;
5440             }
5441 
5442             // It is possible to use heapselect when ka and kb are close to the same end
5443             // |l|-----|ka|--------|kb|------|r|
5444             //  ---------s2-----------
5445             //          ----------s4-----------
5446             if (Math.min(kb - l, r - ka) < edgeSelectConstant) {
5447                 edgeSelection.partition(a, l, r, ka, kb);
5448                 recursionConsumer.accept(maxDepth);
5449                 return;
5450             }
5451 
5452             if (maxDepth == 0) {
5453                 // Too much recursion
5454                 heapSelectRange(a, l, r, ka, kb);
5455                 recursionConsumer.accept(maxDepth);
5456                 return;
5457             }
5458 
5459             // Pick 2 pivots and partition
5460             int p0 = dualPivotingStrategy.pivotIndex(a, l, r, upper);
5461             p0 = part.partition(a, l, r, p0, upper[0], upper);
5462             final int p1 = upper[0];
5463             final int p2 = upper[1];
5464             final int p3 = upper[2];
5465 
5466             // Recursion to max depth
5467             // Note: Here we possibly branch left, middle and right with multiple keys.
5468             // It is possible that the partition has split the keys
5469             // and the recursion proceeds with a reduced set in each region.
5470             //                   p0 p1               p2 p3
5471             // |l|--|ka|--k----k--|P|------k--|kb|----|P|----|r|
5472             //                 kb  |      ka
5473             // Search previous/next is bounded at ka/kb
5474             maxDepth--;
5475             SplittingInterval lk = k.split(p0, p1);
5476             // Recurse left side if required
5477             if (lk != null) {
5478                 // Avoid recursive method calls
5479                 if (k.empty()) {
5480                     // Entirely on left side
5481                     r = p0 - 1;
5482                     kb = lk.right();
5483                     k = lk;
5484                     continue;
5485                 }
5486                 introselect(part, a, l, p0 - 1, lk, maxDepth);
5487             }
5488             if (k.empty()) {
5489                 // No middle/right side
5490                 recursionConsumer.accept(maxDepth);
5491                 return;
5492             }
5493             lk = k.split(p2, p3);
5494             // Recurse middle side if required
5495             if (lk != null) {
5496                 // Avoid recursive method calls
5497                 if (k.empty()) {
5498                     // Entirely in middle side
5499                     l = p1 + 1;
5500                     r = p2 - 1;
5501                     ka = lk.left();
5502                     kb = lk.right();
5503                     k = lk;
5504                     continue;
5505                 }
5506                 introselect(part, a, p1 + 1, p2 - 1, lk, maxDepth);
5507             }
5508             if (k.empty()) {
5509                 // No right side
5510                 recursionConsumer.accept(maxDepth);
5511                 return;
5512             }
5513             // Continue right
5514             l = p3 + 1;
5515             ka = k.left();
5516         }
5517     }
5518 
5519     /**
5520      * Partition the array such that indices {@code k} correspond to their
5521      * correctly sorted value in the equivalent fully sorted array.
5522      *
5523      * <p>For all indices {@code k} and any index {@code i}:
5524      *
5525      * <pre>{@code
5526      * data[i < k] <= data[k] <= data[k < i]
5527      * }</pre>
5528      *
5529      *
5530      * <p>This function accepts an {@link IndexIterator} of indices {@code k}; for
5531      * convenience the lower and upper indices of the current interval are passed as the
5532      * first index {@code ka} and last index {@code kb} of the closed interval of indices
5533      * to partition. These may be within the lower and upper indices if the interval was
5534      * split during recursion: {@code lower <= ka <= kb <= upper}.
5535      *
5536      * <p>The data is recursively partitioned using left-most ordering. When the current
5537      * interval has been partitioned the {@link IndexIterator} is used to advance to the
5538      * next interval to partition.
5539      *
5540      * <p>Uses an introselect variant. The dual pivot quickselect is provided as an argument;
5541      * the fall-back on poor convergence of the quickselect is a heapselect.
5542      *
5543      * @param part Partition function.
5544      * @param a Values.
5545      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
5546      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
5547      * @param k Interval of indices to partition (ordered).
5548      * @param ka First key.
5549      * @param kb Last key.
5550      * @param maxDepth Maximum depth for recursion.
5551      */
5552     // package-private for benchmarking
5553     void introselect(DPPartition part, double[] a, int left, int right,
5554         IndexIterator k, int ka, int kb, int maxDepth) {
5555         // If partitioning splits the interval then recursion is used for left and/or
5556         // right sides and the middle remains within this function. If partitioning does
5557         // not split the interval then it remains within this function.
5558         int l = left;
5559         final int r = right;
5560         int lo = ka;
5561         int hi = kb;
5562         final int[] upper = {0, 0, 0};
5563         while (true) {
5564             if (maxDepth == 0) {
5565                 // Too much recursion.
5566                 // Advance the iterator to the end of the current range.
5567                 // Note: heapSelectRange handles hi > right.
5568                 // Single API method: advanceBeyond(right): return hi <= right
5569                 while (hi < right && k.next()) {
5570                     hi = k.right();
5571                 }
5572                 heapSelectRange(a, l, right, lo, hi);
5573                 recursionConsumer.accept(maxDepth);
5574                 return;
5575             }
5576 
5577             // length - 1
5578             final int n = right - l;
5579 
5580             // If interval is close to one end then heapselect.
5581             // Only heapselect left if there are no further indices in the range.
5582             // |l|-----|lo|--------|hi|------|right|
5583             //  ---------d1----------
5584             //          --------------d2-----------
5585             if (Math.min(hi - l, right - lo) < edgeSelectConstant) {
5586                 if (hi - l > right - lo) {
5587                     // Right end. Do not check above hi, just select to the end
5588                     edgeSelection.partition(a, l, right, lo, right);
5589                     recursionConsumer.accept(maxDepth);
5590                     return;
5591                 } else if (k.nextAfter(right)) {
5592                     // Left end
5593                     // Only if no further indices in the range.
5594                     // If false this branch will continue to be triggered until
5595                     // a partition is made to separate the next indices.
5596                     edgeSelection.partition(a, l, right, lo, hi);
5597                     recursionConsumer.accept(maxDepth);
5598                     // Advance iterator
5599                     l = hi + 1;
5600                     if (!k.positionAfter(hi) || Math.max(k.left(), l) > right) {
5601                         // No more keys, or keys beyond the current bounds
5602                         return;
5603                     }
5604                     lo = Math.max(k.left(), l);
5605                     hi = Math.min(right, k.right());
5606                     // Continue right (allows a second heap select for the right side)
5607                     continue;
5608                 }
5609             }
5610 
5611             // If interval is close to both ends then sort
5612             // |l|-----|lo|--------|hi|------|right|
5613             //  ---d1----
5614             //                       ----d2--------
5615             // (lo - l) + (right - hi) == (right - l) - (hi - lo)
5616             if (n - (hi - lo) < minQuickSelectSize) {
5617                 // Handle small data. This is done as the JDK sort will
5618                 // use insertion sort for small data. For double data it
5619                 // will also pre-process the data for NaN and signed
5620                 // zeros which is an overhead to avoid.
5621                 if (n < minQuickSelectSize) {
5622                     // Must not use sortSelectRange in [lo, hi] as the iterator
5623                     // has not been advanced to check after hi
5624                     sortSelectRight(a, l, right, lo);
5625                 } else {
5626                     // Note: This disregards the current level of recursion
5627                     // but can exploit the JDK's more advanced sort algorithm.
5628                     Arrays.sort(a, l, right + 1);
5629                 }
5630                 recursionConsumer.accept(maxDepth);
5631                 return;
5632             }
5633 
5634             // Here: l <= lo <= hi <= right
5635             // Pick 2 pivots and partition
5636             int p0 = dualPivotingStrategy.pivotIndex(a, l, r, upper);
5637             p0 = part.partition(a, l, r, p0, upper[0], upper);
5638             final int p1 = upper[0];
5639             final int p2 = upper[1];
5640             final int p3 = upper[2];
5641 
5642             maxDepth--;
5643             // Recursion left
5644             if (lo < p0) {
5645                 introselect(part, a, l, p0 - 1, k, lo, Math.min(hi, p0 - 1), maxDepth);
5646                 // Advance iterator
5647                 if (!k.positionAfter(p1) || k.left() > right) {
5648                     // No more keys, or keys beyond the current bounds
5649                     return;
5650                 }
5651                 lo = k.left();
5652                 hi = Math.min(right, k.right());
5653             }
5654             if (hi <= p1) {
5655                 // Advance iterator
5656                 if (!k.positionAfter(p1) || k.left() > right) {
5657                     // No more keys, or keys beyond the current bounds
5658                     return;
5659                 }
5660                 lo = k.left();
5661                 hi = Math.min(right, k.right());
5662             }
5663 
5664             // Recursion middle
5665             l = p1 + 1;
5666             lo = Math.max(lo, l);
5667             if (lo < p2) {
5668                 introselect(part, a, l, p2 - 1, k, lo, Math.min(hi, p2 - 1), maxDepth);
5669                 // Advance iterator
5670                 if (!k.positionAfter(p3) || k.left() > right) {
5671                     // No more keys, or keys beyond the current bounds
5672                     return;
5673                 }
5674                 lo = k.left();
5675                 hi = Math.min(right, k.right());
5676             }
5677             if (hi <= p3) {
5678                 // Advance iterator
5679                 if (!k.positionAfter(p3) || k.left() > right) {
5680                     // No more keys, or keys beyond the current bounds
5681                     return;
5682                 }
5683                 lo = k.left();
5684                 hi = Math.min(right, k.right());
5685             }
5686 
5687             // Continue right
5688             l = p3 + 1;
5689             lo = Math.max(lo, l);
5690         }
5691     }
5692 
5693     /**
5694      * Partition the array such that indices {@code k} correspond to their correctly
5695      * sorted value in the equivalent fully sorted array. For all indices {@code k}
5696      * and any index {@code i}:
5697      *
5698      * <pre>{@code
5699      * data[i < k] <= data[k] <= data[k < i]
5700      * }</pre>
5701      *
5702      * <p>Uses a Bentley-McIlroy quicksort partition method by Sedgewick.
5703      *
5704      * @param data Values.
5705      * @param k Indices (may be destructively modified).
5706      * @param n Count of indices.
5707      */
5708     void partitionSBM(double[] data, int[] k, int n) {
5709         // Handle NaN (this does assume n > 0)
5710         final int right = sortNaN(data);
5711         partition((SPEPartitionFunction) this::partitionSBMWithZeros, data, right, k, n);
5712     }
5713 
5714     /**
5715      * Partition the array such that indices {@code k} correspond to their correctly
5716      * sorted value in the equivalent fully sorted array. For all indices {@code k}
5717      * and any index {@code i}:
5718      *
5719      * <pre>{@code
5720      * data[i < k] <= data[k] <= data[k < i]
5721      * }</pre>
5722      *
5723      * <p>The method assumes all {@code k} are valid indices into the data.
5724      * It handles NaN and signed zeros in the data.
5725      *
5726      * <p>Uses an introselect variant. Uses the configured single-pivot quicksort method;
5727      * the fall-back on poor convergence of the quickselect is controlled by
5728      * current configuration.
5729      *
5730      * @param data Values.
5731      * @param k Indices (may be destructively modified).
5732      * @param n Count of indices.
5733      */
5734     void partitionISP(double[] data, int[] k, int n) {
5735         introselect(getSPFunction(), data, k, n);
5736     }
5737 
5738     /**
5739      * Partition the array such that indices {@code k} correspond to their correctly
5740      * sorted value in the equivalent fully sorted array. For all indices {@code k}
5741      * and any index {@code i}:
5742      *
5743      * <pre>{@code
5744      * data[i < k] <= data[k] <= data[k < i]
5745      * }</pre>
5746      *
5747      * <p>The method assumes all {@code k} are valid indices into the data.
5748      * It handles NaN and signed zeros in the data.
5749      *
5750      * <p>Uses an introselect variant. The quickselect is a dual-pivot quicksort
5751      * partition method by Vladimir Yaroslavskiy; the fall-back on poor convergence of
5752      * the quickselect is controlled by current configuration.
5753      *
5754      * @param data Values.
5755      * @param k Indices (may be destructively modified).
5756      * @param n Count of indices.
5757      */
5758     void partitionIDP(double[] data, int[] k, int n) {
5759         introselect((DPPartition) Partition::partitionDP, data, k, n);
5760     }
5761 
5762     /**
5763      * Partition the array such that indices {@code k} correspond to their correctly
5764      * sorted value in the equivalent fully sorted array. For all indices {@code k}
5765      * and any index {@code i}:
5766      *
5767      * <pre>{@code
5768      * data[i < k] <= data[k] <= data[k < i]
5769      * }</pre>
5770      *
5771      * <p>The method assumes all {@code k} are valid indices into the data.
5772      * It handles NaN and signed zeros in the data.
5773      *
5774      * <p>Uses the <a href="https://en.wikipedia.org/wiki/Floyd%E2%80%93Rivest_algorithm">
5775      * Floyd-Rivest Algorithm (Wikipedia)</a>
5776      *
5777      * <p>WARNING: Currently this only supports a single {@code k}. For parity with other
5778      * select methods this accepts an array {@code k} and pre/post processes the data for
5779      * NaN and signed zeros.
5780      *
5781      * @param a Values.
5782      * @param k Indices (may be destructively modified).
5783      * @param count Count of indices.
5784      */
5785     void partitionFR(double[] a, int[] k, int count) {
5786         // Handle NaN / signed zeros
5787         final DoubleDataTransformer t = SORT_TRANSFORMER.get();
5788         // Assume this is in-place
5789         t.preProcess(a);
5790         final int end = t.length();
5791         int n = count;
5792         if (end > 1) {
5793             // Filter indices invalidated by NaN check
5794             if (end < a.length) {
5795                 for (int i = n; --i >= 0;) {
5796                     final int v = k[i];
5797                     if (v >= end) {
5798                         // swap(k, i, --n)
5799                         k[i] = k[--n];
5800                         k[n] = v;
5801                     }
5802                 }
5803             }
5804             // Only handles a single k
5805             if (n != 0) {
5806                 selectFR(a, 0, end - 1, k[0], controlFlags);
5807             }
5808         }
5809         // Restore signed zeros
5810         t.postProcess(a, k, n);
5811     }
5812 
5813     /**
5814      * Select the k-th element of the array.
5815      *
5816      * <p>Uses the <a href="https://en.wikipedia.org/wiki/Floyd%E2%80%93Rivest_algorithm">
5817      * Floyd-Rivest Algorithm (Wikipedia)</a>.
5818      *
5819      * <p>This code has been adapted from:
5820      * <pre>
5821      * Floyd and Rivest (1975)
5822      * Algorithm 489: The Algorithm SELECT—for Finding the ith Smallest of n elements.
5823      * Comm. ACM. 18 (3): 173.
5824      * </pre>
5825      *
5826      * @param a Values.
5827      * @param left Lower bound (inclusive).
5828      * @param right Upper bound (inclusive).
5829      * @param k Key of interest.
5830      * @param flags Control behaviour.
5831      */
5832     private void selectFR(double[] a, int left, int right, int k, int flags) {
5833         int l = left;
5834         int r = right;
5835         while (true) {
5836             // The following edgeselect modifications are additions to the
5837             // FR algorithm. These have been added for testing and only affect the finishing
5838             // selection of small lengths.
5839 
5840             // It is possible to use edgeselect when k is close to the end
5841             // |l|-----|ka|--------|kb|------|r|
5842             //  ---------s2----------
5843             //          ----------s4-----------
5844             if (Math.min(k - l, r - k) < edgeSelectConstant) {
5845                 edgeSelection.partition(a, l, r, k, k);
5846                 return;
5847             }
5848 
5849             // use SELECT recursively on a sample of size S to get an estimate for the
5850             // (k-l+1)-th smallest element into a[k], biased slightly so that the (k-l+1)-th
5851             // element is expected to lie in the smaller set after partitioning.
5852             int pivot = k;
5853             int p = l;
5854             int q = r;
5855             // length - 1
5856             int n = r - l;
5857             if (n > 600) {
5858                 ++n;
5859                 final int ith = k - l + 1;
5860                 final double z = Math.log(n);
5861                 final double s = 0.5 * Math.exp(0.6666666666666666 * z);
5862                 final double sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * Integer.signum(ith - (n >> 1));
5863                 final int ll = Math.max(l, (int) (k - ith * s / n + sd));
5864                 final int rr = Math.min(r, (int) (k + (n - ith) * s / n + sd));
5865                 // Optional: sample [l, r] into [l, rs]
5866                 if ((flags & FLAG_SUBSET_SAMPLING) != 0) {
5867                     // Create a random sample at the left end.
5868                     // This creates an unbiased random sample.
5869                     // This method is not as fast as sampling into [ll, rr] (see below).
5870                     final IntUnaryOperator rng = createRNG(n, k);
5871                     final int rs = l + rr - ll;
5872                     for (int i = l - 1; i < rs;) {
5873                         // r - rand [0, r - i + 1) : i is currently i-1
5874                         final int j = r - rng.applyAsInt(r - i);
5875                         final double t = a[++i];
5876                         a[i] = a[j];
5877                         a[j] = t;
5878                     }
5879                     selectFR(a, l, rs, k - ll + l, flags);
5880                     // Current:
5881                     // |l      |k-ll+l|     rs|                               r|
5882                     // |  < v  |   v  |  > v  |              ???               |
5883                     // Move partitioned data
5884                     // |l       |p                     |k|            q|      r|
5885                     // |  < v   |         ???          |v|      ???    |  > v  |
5886                     p = k - ll + l;
5887                     q = r - rs + p;
5888                     vectorSwap(a, p + 1, rs, r);
5889                     vectorSwap(a, p, p, k);
5890                 } else {
5891                     // Note: Random sampling is a redundant overhead on fully random data
5892                     // and will part destroy sorted data. On data that is: partially partitioned;
5893                     // has many repeat elements; or is structured with repeat patterns, the
5894                     // shuffle removes side-effects of patterns and stabilises performance.
5895                     if ((flags & FLAG_RANDOM_SAMPLING) != 0) {
5896                         // This is not a random sample from [l, r] when k is not exactly
5897                         // in the middle. By sampling either side of k the sample
5898                         // will maintain the value of k if the data is already partitioned
5899                         // around k. However sorted data will be part scrambled by the shuffle.
5900                         // This sampling has the best performance overall across datasets.
5901                         final IntUnaryOperator rng = createRNG(n, k);
5902                         // Shuffle [ll, k) from [l, k)
5903                         if (ll > l) {
5904                             for (int i = k; i > ll;) {
5905                                 // l + rand [0, i - l + 1) : i is currently i+1
5906                                 final int j = l + rng.applyAsInt(i - l);
5907                                 final double t = a[--i];
5908                                 a[i] = a[j];
5909                                 a[j] = t;
5910                             }
5911                         }
5912                         // Shuffle (k, rr] from (k, r]
5913                         if (rr < r) {
5914                             for (int i = k; i < rr;) {
5915                                 // r - rand [0, r - i + 1) : i is currently i-1
5916                                 final int j = r - rng.applyAsInt(r - i);
5917                                 final double t = a[++i];
5918                                 a[i] = a[j];
5919                                 a[j] = t;
5920                             }
5921                         }
5922                     }
5923                     selectFR(a, ll, rr, k, flags);
5924                     // Current:
5925                     // |l                    |ll      |k|     rr|            r|
5926                     // |        ???          |  < v   |v|  > v  |      ???    |
5927                     // Optional: move partitioned data
5928                     // Unlikely to make a difference as the partitioning will skip
5929                     // over <v and >v.
5930                     // |l       |p                    |k|            q|      r|
5931                     // |  < v   |        ???          |v|      ???    |  > v  |
5932                     if ((flags & FLAG_MOVE_SAMPLE) != 0) {
5933                         vectorSwap(a, l, ll - 1, k - 1);
5934                         vectorSwap(a, k + 1, rr, r);
5935                         p += k - ll;
5936                         q -= rr - k;
5937                     }
5938                 }
5939             } else {
5940                 // Optional: use pivot strategy
5941                 pivot = pivotingStrategy.pivotIndex(a, l, r, k);
5942             }
5943 
5944             // This uses the original binary partition of FR.
5945             // FR sub-sampling can be used in some introselect methods; this
5946             // allows the original FR to be compared with introselect.
5947 
5948             // Partition a[p : q] about t.
5949             // Sub-script range checking has been eliminated by appropriate placement of t
5950             // at the p or q end.
5951             final double t = a[pivot];
5952             // swap(left, pivot)
5953             a[pivot] = a[p];
5954             if (a[q] > t) {
5955                 // swap(right, left)
5956                 a[p] = a[q];
5957                 a[q] = t;
5958                 // Here after the first swap: a[p] = t; a[q] > t
5959             } else {
5960                 a[p] = t;
5961                 // Here after the first swap: a[p] <= t; a[q] = t
5962             }
5963             int i = p;
5964             int j = q;
5965             while (i < j) {
5966                 // swap(i, j)
5967                 final double temp = a[i];
5968                 a[i] = a[j];
5969                 a[j] = temp;
5970                 do {
5971                     ++i;
5972                 } while (a[i] < t);
5973                 do {
5974                     --j;
5975                 } while (a[j] > t);
5976             }
5977             if (a[p] == t) {
5978                 // data[j] <= t : swap(left, j)
5979                 a[p] = a[j];
5980                 a[j] = t;
5981             } else {
5982                 // data[j+1] > t : swap(j+1, right)
5983                 a[q] = a[++j];
5984                 a[j] = t;
5985             }
5986             // Continue on the correct side
5987             if (k < j) {
5988                 r = j - 1;
5989             } else if (k > j) {
5990                 l = j + 1;
5991             } else {
5992                 return;
5993             }
5994         }
5995     }
5996 
5997     /**
5998      * Partition the array such that indices {@code k} correspond to their correctly
5999      * sorted value in the equivalent fully sorted array. For all indices {@code k}
6000      * and any index {@code i}:
6001      *
6002      * <pre>{@code
6003      * data[i < k] <= data[k] <= data[k < i]
6004      * }</pre>
6005      *
6006      * <p>The method assumes all {@code k} are valid indices into the data.
6007      * It handles NaN and signed zeros in the data.
6008      *
6009      * <p>Uses the <a href="https://en.wikipedia.org/wiki/Floyd%E2%80%93Rivest_algorithm">
6010      * Floyd-Rivest Algorithm (Wikipedia)</a>, modified by Kiwiel.
6011      *
6012      * <p>WARNING: Currently this only supports a single {@code k}. For parity with other
6013      * select methods this accepts an array {@code k} and pre/post processes the data for
6014      * NaN and signed zeros.
6015      *
6016      * @param a Values.
6017      * @param k Indices (may be destructively modified).
6018      * @param count Count of indices.
6019      */
6020     void partitionKFR(double[] a, int[] k, int count) {
6021         // Handle NaN / signed zeros
6022         final DoubleDataTransformer t = SORT_TRANSFORMER.get();
6023         // Assume this is in-place
6024         t.preProcess(a);
6025         final int end = t.length();
6026         int n = count;
6027         if (end > 1) {
6028             // Filter indices invalidated by NaN check
6029             if (end < a.length) {
6030                 for (int i = n; --i >= 0;) {
6031                     final int v = k[i];
6032                     if (v >= end) {
6033                         // swap(k, i, --n)
6034                         k[i] = k[--n];
6035                         k[n] = v;
6036                     }
6037                 }
6038             }
6039             // Only handles a single k
6040             if (n != 0) {
6041                 final int[] bounds = new int[5];
6042                 selectKFR(a, 0, end - 1, k[0], bounds, null);
6043             }
6044         }
6045         // Restore signed zeros
6046         t.postProcess(a, k, n);
6047     }
6048 
6049     /**
6050      * Select the k-th element of the array.
6051      *
6052      * <p>Uses the <a href="https://en.wikipedia.org/wiki/Floyd%E2%80%93Rivest_algorithm">
6053      * Floyd-Rivest Algorithm (Wikipedia)</a>, modified by Kiwiel.
6054      *
6055      * <p>References:
6056      * <ul>
6057      * <li>Floyd and Rivest (1975)
6058      * Algorithm 489: The Algorithm SELECT—for Finding the ith Smallest of n elements.
6059      * Comm. ACM. 18 (3): 173.
6060      * <li>Kiwiel (2005)
6061      * On Floyd and Rivest's SELECT algorithm.
6062      * Theoretical Computer Science 347, 214-238.
6063      * </ul>
6064      *
6065      * @param x Values.
6066      * @param left Lower bound (inclusive).
6067      * @param right Upper bound (inclusive).
6068      * @param k Key of interest.
6069      * @param bounds Inclusive bounds {@code [k-, k+]} containing {@code k}.
6070      * @param rng Random generator for samples in {@code [0, n)}.
6071      */
6072     private void selectKFR(double[] x, int left, int right, int k, int[] bounds,
6073         IntUnaryOperator rng) {
6074         int l = left;
6075         int r = right;
6076         while (true) {
6077             // The following edgeselect modifications are additions to the
6078             // KFR algorithm. These have been added for testing and only affect the finishing
6079             // selection of small lengths.
6080 
6081             // It is possible to use edgeselect when k is close to the end
6082             // |l|-----|ka|--------|kb|------|r|
6083             //  ---------s2----------
6084             //          ----------s4-----------
6085             if (Math.min(k - l, r - k) < edgeSelectConstant) {
6086                 edgeSelection.partition(x, l, r, k, k);
6087                 bounds[0] = bounds[1] = k;
6088                 return;
6089             }
6090 
6091             // length - 1
6092             int n = r - l;
6093             if (n < 600) {
6094                 // Switch to quickselect
6095                 final int p0 = partitionKBM(x, l, r,
6096                     pivotingStrategy.pivotIndex(x, l, r, k), bounds);
6097                 final int p1 = bounds[0];
6098                 if (k < p0) {
6099                     // The element is in the left partition
6100                     r = p0 - 1;
6101                 } else if (k > p1) {
6102                     // The element is in the right partition
6103                     l = p1 + 1;
6104                 } else {
6105                     // The range contains the element we wanted.
6106                     bounds[0] = p0;
6107                     bounds[1] = p1;
6108                     return;
6109                 }
6110                 continue;
6111             }
6112 
6113             // Floyd-Rivest sub-sampling
6114             ++n;
6115             // Step 1: Choose sample size s <= n-1 and gap g > 0
6116             final double z = Math.log(n);
6117             // sample size = alpha * n^(2/3) * ln(n)^1/3  (4.1)
6118             // sample size = alpha * n^(2/3)              (4.17; original Floyd-Rivest size)
6119             final double s = 0.5 * Math.exp(0.6666666666666666 * z) * Math.cbrt(z);
6120             //final double s = 0.5 * Math.exp(0.6666666666666666 * z);
6121             // gap = sqrt(beta * s * ln(n))
6122             final double g = Math.sqrt(0.25 * s * z);
6123             final int rs = (int) (l + s - 1);
6124             // Step 2: Sample selection
6125             // Convenient to place the random sample in [l, rs]
6126             if (rng == null) {
6127                 rng = createRNG(n, k);
6128             }
6129             for (int i = l - 1; i < rs;) {
6130                 // r - rand [0, r - i + 1) : i is currently i-1
6131                 final int j = r - rng.applyAsInt(r - i);
6132                 final double t = x[++i];
6133                 x[i] = x[j];
6134                 x[j] = t;
6135             }
6136 
6137             // Step 3: pivot selection
6138             final double isn = (k - l + 1) * s / n;
6139             final int ku = (int) Math.max(Math.floor(l - 1 + isn - g), l);
6140             final int kv = (int) Math.min(Math.ceil(l - 1 + isn + g), rs);
6141             // Find u and v by recursion
6142             selectKFR(x, l, rs, ku, bounds, rng);
6143             final int kum = bounds[0];
6144             int kup = bounds[1];
6145             int kvm;
6146             int kvp;
6147             if (kup >= kv) {
6148                 kvm = kv;
6149                 kvp = kup;
6150                 kup = kv - 1;
6151                 // u == v will use single-pivot ternary partitioning
6152             } else {
6153                 selectKFR(x, kup + 1, rs, kv, bounds, rng);
6154                 kvm = bounds[0];
6155                 kvp = bounds[1];
6156             }
6157 
6158             // Step 4: Partitioning
6159             final double u = x[kup];
6160             final double v = x[kvm];
6161             // |l      |ku- ku+|                   |kv- kv+|     rs|            r|     (6.4)
6162             // | x < u | x = u |     u < x < v     | x = v | x > v |      ???    |
6163             final int ll = kum;
6164             int pp = kup;
6165             final int rr = r - rs + kvp;
6166             int qq = rr - kvp + kvm;
6167             vectorSwap(x, kvp + 1, rs, r);
6168             vectorSwap(x, kvm, kvp, rr);
6169             // |l      |ll   pp|                   |kv-          |qq   rr|      r|     (6.5)
6170             // | x < u | x = u |     u < x < v     |      ???    | x = v | x > v |
6171 
6172             int a;
6173             int b;
6174             int c;
6175             int d;
6176 
6177             // Note: The quintary partitioning is as specified in Kiwiel.
6178             // Moving each branch to methods had no effect on performance.
6179 
6180             if (u == v) {
6181                 // Can be optimised by omitting step A1 (moving of sentinels). Here the
6182                 // size of ??? is large and initialisation is insignificant.
6183                 a = partitionKBM(x, ll, rr, pp, bounds);
6184                 d = bounds[0];
6185                 // Make ternary and quintary partitioning compatible
6186                 b = d + 1;
6187                 c = a - 1;
6188             } else if (k < (r + l) >>> 1) {
6189                 // Left k: u < x[k] < v --> expects x > v.
6190                 // Quintary partitioning using the six-part array:
6191                 // |ll   pp|              p|          |i        j|       |q    rr|     (6.6)
6192                 // | x = u |    u < x < v  |   x < u  |   ???    | x > v | x = v |
6193                 //
6194                 // |ll   pp|              p|              j|i            |q    rr|     (6.7)
6195                 // | x = u |    u < x < v  |   x < u       |       x > v | x = v |
6196                 //
6197                 // Swap the second and third part:
6198                 // |ll   pp|               |b             c|i            |q    rr|     (6.8)
6199                 // | x = u |   x < u       |    u < x < v  |       x > v | x = v |
6200                 //
6201                 // Swap the extreme parts with their neighbours:
6202                 // |ll             |a      |b             c|      d|           rr|     (6.9)
6203                 // |   x < u       | x = u |    u < x < v  | x = v |       x > v |
6204                 int p = kvm - 1;
6205                 int q = qq;
6206                 int i = p;
6207                 int j = q;
6208                 for (;;) {
6209                     while (x[++i] < v) {
6210                         if (x[i] < u) {
6211                             continue;
6212                         }
6213                         // u <= xi < v
6214                         final double xi = x[i];
6215                         x[i] = x[++p];
6216                         if (xi > u) {
6217                             x[p] = xi;
6218                         } else {
6219                             x[p] = x[++pp];
6220                             x[pp] = xi;
6221                         }
6222                     }
6223                     while (x[--j] >= v) {
6224                         if (x[j] == v) {
6225                             final double xj = x[j];
6226                             x[j] = x[--q];
6227                             x[q] = xj;
6228                         }
6229                     }
6230                     // Here x[j] < v <= x[i]
6231                     if (i >= j) {
6232                         break;
6233                     }
6234                     //swap(x, i, j)
6235                     final double xi = x[j];
6236                     final double xj = x[i];
6237                     x[i] = xi;
6238                     x[j] = xj;
6239                     if (xi > u) {
6240                         x[i] = x[++p];
6241                         x[p] = xi;
6242                     } else if (xi == u) {
6243                         x[i] = x[++p];
6244                         x[p] = x[++pp];
6245                         x[pp] = xi;
6246                     }
6247                     if (xj == v) {
6248                         x[j] = x[--q];
6249                         x[q] = xj;
6250                     }
6251                 }
6252                 a = ll + i - p - 1;
6253                 b = a + pp + 1 - ll;
6254                 d = rr - q + 1 + j;
6255                 c = d - rr + q - 1;
6256                 vectorSwap(x, pp + 1, p, j);
6257                 //vectorSwap(x, ll, pp, b - 1);
6258                 //vectorSwap(x, i, q - 1, rr);
6259                 vectorSwapL(x, ll, pp, b - 1, u);
6260                 vectorSwapR(x, i, q - 1, rr, v);
6261             } else {
6262                 // Right k: u < x[k] < v --> expects x < u.
6263                 // Symmetric quintary partitioning replacing 6.6-6.8 with:
6264                 // |ll    p|          |i        j|       |q              |qq   rr|     (6.10)
6265                 // | x = u |   x < u  |   ???    | x > v |    u < x < v  | x = v |
6266                 //
6267                 // |ll    p|                j|i      |q                  |qq   rr|     (6.11)
6268                 // | x = u |   x < u         | x > v |        u < x < v  | x = v |
6269                 //
6270                 // |ll    p|                j|b                 c|       |qq   rr|     (6.12)
6271                 // | x = u |   x < u         |        u < x < v  | x > v | x = v |
6272                 //
6273                 // |ll               |a      |b                 c|      d|     rr|     (6.9)
6274                 // |   x < u         | x = u |        u < x < v  | x = v | x > v |
6275                 int p = pp;
6276                 int q = qq - kvm + kup + 1;
6277                 int i = p;
6278                 int j = q;
6279                 vectorSwap(x, pp + 1, kvm - 1, qq - 1);
6280                 for (;;) {
6281                     while (x[++i] <= u) {
6282                         if (x[i] == u) {
6283                             final double xi = x[i];
6284                             x[i] = x[++p];
6285                             x[p] = xi;
6286                         }
6287                     }
6288                     while (x[--j] > u) {
6289                         if (x[j] > v) {
6290                             continue;
6291                         }
6292                         // u < xj <= v
6293                         final double xj = x[j];
6294                         x[j] = x[--q];
6295                         if (xj < v) {
6296                             x[q] = xj;
6297                         } else {
6298                             x[q] = x[--qq];
6299                             x[qq] = xj;
6300                         }
6301                     }
6302                     // Here x[j] < v <= x[i]
6303                     if (i >= j) {
6304                         break;
6305                     }
6306                     //swap(x, i, j)
6307                     final double xi = x[j];
6308                     final double xj = x[i];
6309                     x[i] = xi;
6310                     x[j] = xj;
6311                     if (xi == u) {
6312                         x[i] = x[++p];
6313                         x[p] = xi;
6314                     }
6315                     if (xj < v) {
6316                         x[j] = x[--q];
6317                         x[q] = xj;
6318                     } else if (xj == v) {
6319                         x[j] = x[--q];
6320                         x[q] = x[--qq];
6321                         x[qq] = xj;
6322                     }
6323                 }
6324                 a = ll + i - p - 1;
6325                 b = a + p + 1 - ll;
6326                 d = rr - q + 1 + j;
6327                 c = d - rr + qq - 1;
6328                 vectorSwap(x, i, q - 1, qq - 1);
6329                 //vectorSwap(x, ll, p, j);
6330                 //vectorSwap(x, c + 1, qq - 1, rr);
6331                 vectorSwapL(x, ll, p, j, u);
6332                 vectorSwapR(x, c + 1, qq - 1, rr, v);
6333             }
6334 
6335             // Step 5/6/7: Stopping test, reduction and recursion
6336             // |l              |a      |b             c|      d|            r|
6337             // |   x < u       | x = u |    u < x < v  | x = v |       x > v |
6338             if (a <= k) {
6339                 l = b;
6340             }
6341             if (c < k) {
6342                 l = d + 1;
6343             }
6344             if (k <= d) {
6345                 r = c;
6346             }
6347             if (k < b) {
6348                 r = a - 1;
6349             }
6350             if (l >= r) {
6351                 if (l == r) {
6352                     // [b, c]
6353                     bounds[0] = bounds[1] = k;
6354                 } else {
6355                     // l > r
6356                     bounds[0] = r + 1;
6357                     bounds[1] = l - 1;
6358                 }
6359                 return;
6360             }
6361         }
6362     }
6363 
6364     /**
6365      * Vector swap x[a:b] <-> x[b+1:c] means the first m = min(b+1-a, c-b)
6366      * elements of the array x[a:c] are exchanged with its last m elements.
6367      *
6368      * @param x Array.
6369      * @param a Index.
6370      * @param b Index.
6371      * @param c Index.
6372      */
6373     private static void vectorSwap(double[] x, int a, int b, int c) {
6374         for (int i = a - 1, j = c + 1, m = Math.min(b + 1 - a, c - b); --m >= 0;) {
6375             final double v = x[++i];
6376             x[i] = x[--j];
6377             x[j] = v;
6378         }
6379     }
6380 
6381     /**
6382      * Vector swap x[a:b] <-> x[b+1:c] means the first m = min(b+1-a, c-b)
6383      * elements of the array x[a:c] are exchanged with its last m elements.
6384      *
6385      * <p>This is a specialisation of {@link #vectorSwap(double[], int, int, int)}
6386      * where the current left-most value is a constant {@code v}.
6387      *
6388      * @param x Array.
6389      * @param a Index.
6390      * @param b Index.
6391      * @param c Index.
6392      * @param v Constant value in [a, b]
6393      */
6394     private static void vectorSwapL(double[] x, int a, int b, int c, double v) {
6395         for (int i = a - 1, j = c + 1, m = Math.min(b + 1 - a, c - b); --m >= 0;) {
6396             x[++i] = x[--j];
6397             x[j] = v;
6398         }
6399     }
6400 
6401     /**
6402      * Vector swap x[a:b] <-> x[b+1:c] means the first m = min(b+1-a, c-b)
6403      * elements of the array x[a:c] are exchanged with its last m elements.
6404      *
6405      * <p>This is a specialisation of {@link #vectorSwap(double[], int, int, int)}
6406      * where the current right-most value is a constant {@code v}.
6407      *
6408      * @param x Array.
6409      * @param a Index.
6410      * @param b Index.
6411      * @param c Index.
6412      * @param v Constant value in (b, c]
6413      */
6414     private static void vectorSwapR(double[] x, int a, int b, int c, double v) {
6415         for (int i = a - 1, j = c + 1, m = Math.min(b + 1 - a, c - b); --m >= 0;) {
6416             x[--j] = x[++i];
6417             x[i] = v;
6418         }
6419     }
6420 
6421     /**
6422      * Partition the array such that indices {@code k} correspond to their correctly
6423      * sorted value in the equivalent fully sorted array. For all indices {@code k}
6424      * and any index {@code i}:
6425      *
6426      * <pre>{@code
6427      * data[i < k] <= data[k] <= data[k < i]
6428      * }</pre>
6429      *
6430      * <p>The method assumes all {@code k} are valid indices into the data in {@code [0, length)}.
6431      * It assumes no NaNs or signed zeros in the data. Data must be pre- and post-processed.
6432      *
6433      * <p>Uses the configured single-pivot quicksort method;
6434      * and median-of-medians algorithm for pivot selection with medians-of-5.
6435      *
6436      * <p>Note:
6437      * <p>This method is not configurable with the exception of the single-pivot quickselect method
6438      * and the size to stop quickselect recursion and finish using sort select. It has been superceded by
6439      * {@link #partitionLinear(double[], int[], int)} which has configurable deterministic
6440      * pivot selection including those using partition expansion in-place of full partitioning.
6441      *
6442      * @param data Values.
6443      * @param k Indices (may be destructively modified).
6444      * @param n Count of indices.
6445      */
6446     void partitionLSP(double[] data, int[] k, int n) {
6447         linearSelect(getSPFunction(), data, k, n);
6448     }
6449 
6450     /**
6451      * Partition the array such that indices {@code k} correspond to their correctly
6452      * sorted value in the equivalent fully sorted array. For all indices {@code k}
6453      * and any index {@code i}:
6454      *
6455      * <pre>{@code
6456      * data[i < k] <= data[k] <= data[k < i]
6457      * }</pre>
6458      *
6459      * <p>The method assumes all {@code k} are valid indices into the data.
6460      * It handles NaN and signed zeros in the data.
6461      *
6462      * <p>Uses the median-of-medians algorithm for pivot selection.
6463      *
6464      * <p>WARNING: Currently this only supports a single or range of {@code k}.
6465      * For parity with other select methods this accepts an array {@code k} and pre/post
6466      * processes the data for NaN and signed zeros.
6467      *
6468      * @param part Partition function.
6469      * @param a Values.
6470      * @param k Indices (may be destructively modified).
6471      * @param count Count of indices.
6472      */
6473     private void linearSelect(SPEPartition part, double[] a, int[] k, int count) {
6474         // Handle NaN / signed zeros
6475         final DoubleDataTransformer t = SORT_TRANSFORMER.get();
6476         // Assume this is in-place
6477         t.preProcess(a);
6478         final int end = t.length();
6479         int n = count;
6480         if (end > 1) {
6481             // Filter indices invalidated by NaN check
6482             if (end < a.length) {
6483                 for (int i = n; --i >= 0;) {
6484                     final int v = k[i];
6485                     if (v >= end) {
6486                         // swap(k, i, --n)
6487                         k[i] = k[--n];
6488                         k[n] = v;
6489                     }
6490                 }
6491             }
6492             if (n != 0) {
6493                 final int ka = Math.min(k[0], k[n - 1]);
6494                 final int kb = Math.max(k[0], k[n - 1]);
6495                 linearSelect(part, a, 0, end - 1, ka, kb, new int[2]);
6496             }
6497         }
6498         // Restore signed zeros
6499         t.postProcess(a, k, n);
6500     }
6501 
6502     /**
6503      * Partition the array such that indices {@code k} correspond to their
6504      * correctly sorted value in the equivalent fully sorted array.
6505      *
6506      * <p>For all indices {@code [ka, kb]} and any index {@code i}:
6507      *
6508      * <pre>{@code
6509      * data[i < ka] <= data[ka] <= data[kb] <= data[kb < i]
6510      * }</pre>
6511      *
6512      * <p>This function accepts indices {@code [ka, kb]} that define the
6513      * range of indices to partition. It is expected that the range is small.
6514      *
6515      * <p>Uses quickselect with median-of-medians pivot selection to provide Order(n)
6516      * performance.
6517      *
6518      * <p>Returns the bounds containing {@code [ka, kb]}. These may be lower/higher
6519      * than the keys if equal values are present in the data. This is to be used by
6520      * {@link #pivotMedianOfMedians(SPEPartition, double[], int, int, int[])} to identify
6521      * the equal value range of the pivot.
6522      *
6523      * @param part Partition function.
6524      * @param a Values.
6525      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
6526      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
6527      * @param ka First key of interest.
6528      * @param kb Last key of interest.
6529      * @param bounds Bounds of the range containing {@code [ka, kb]} (inclusive).
6530      * @see <a href="https://en.wikipedia.org/wiki/Median_of_medians">Median of medians (Wikipedia)</a>
6531      */
6532     private void linearSelect(SPEPartition part, double[] a, int left, int right, int ka, int kb,
6533             int[] bounds) {
6534         int l = left;
6535         int r = right;
6536         while (true) {
6537             // Select when ka and kb are close to the same end
6538             // |l|-----|ka|kkkkkkkk|kb|------|r|
6539             // Optimal value for this is much higher than standard quickselect due
6540             // to the high cost of median-of-medians pivot computation and reuse via
6541             // mutual recursion so we have a different value.
6542             if (Math.min(kb - l, r - ka) < linearSortSelectSize) {
6543                 sortSelectRange(a, l, r, ka, kb);
6544                 // We could scan left/right to extend the bounds here after the sort.
6545                 // Since the move_sample strategy is not generally useful we do not bother.
6546                 bounds[0] = ka;
6547                 bounds[1] = kb;
6548                 return;
6549             }
6550             int p0 = pivotMedianOfMedians(part, a, l, r, bounds);
6551             if ((controlFlags & FLAG_MOVE_SAMPLE) != 0) {
6552                 // Note: medians with 5 elements creates a sample size of 20%.
6553                 // Avoid partitioning the sample known to be above the pivot.
6554                 // The pivot identified the lower pivot (lp) and upper pivot (p).
6555                 // This strategy is not faster unless there are a large number of duplicates
6556                 // (e.g. less than 10 unique values).
6557                 // On random data with no duplicates this is slower.
6558                 // Note: The methods based on quickselect adaptive create the sample in
6559                 // a region corresponding to expected k and expand the partition (faster).
6560                 //
6561                 // |l  |lp p0| rr|                              r|
6562                 // | < |  == | > |        ???                    |
6563                 //
6564                 // Move region above P to r
6565                 //
6566                 // |l  |pp p0|                                  r|
6567                 // | < |  == |           ???                 | > |
6568                 final int lp = bounds[0];
6569                 final int rr = bounds[1];
6570                 vectorSwap(a, p0 + 1, rr, r);
6571                 // 20% less to partition
6572                 final int p = part.partition(a, p0, r - rr + p0, p0, bounds);
6573                 // |l    |pp  |p0         |p  u|                r|
6574                 // |  <  | == |    <      | == |        >        |
6575                 //
6576                 // Move additional equal pivot region to the centre:
6577                 // |l                |p0      u|                r|
6578                 // |        <        |   ==    |        >        |
6579                 vectorSwapL(a, lp, p0 - 1, p - 1, a[p]);
6580                 p0 = p - p0 + lp;
6581             } else {
6582                 p0 = part.partition(a, l, r, p0, bounds);
6583             }
6584             final int p1 = bounds[0];
6585 
6586             // Note: Here we expect [ka, kb] to be small and splitting is unlikely.
6587             //                   p0 p1
6588             // |l|--|ka|kkkk|kb|--|P|-------------------|r|
6589             // |l|----------------|P|--|ka|kkk|kb|------|r|
6590             // |l|-----------|ka|k|P|k|kb|--------------|r|
6591             if (kb < p0) {
6592                 // Entirely on left side
6593                 r = p0 - 1;
6594             } else if (ka > p1) {
6595                 // Entirely on right side
6596                 l = p1 + 1;
6597             } else {
6598                 // Pivot splits [ka, kb]. Expect ends to be close to the pivot and finish.
6599                 // Here we set the bounds for use after median-of-medians pivot selection.
6600                 // In the event there are many equal values this allows collecting those
6601                 // known to be equal together when moving around the medians sample.
6602                 bounds[0] = p0;
6603                 bounds[1] = p1;
6604                 if (ka < p0) {
6605                     sortSelectRight(a, l, p0 - 1, ka);
6606                     bounds[0] = ka;
6607                 }
6608                 if (kb > p1) {
6609                     sortSelectLeft(a, p1 + 1, r, kb);
6610                     bounds[1] = kb;
6611                 }
6612                 return;
6613             }
6614         }
6615     }
6616 
6617     /**
6618      * Compute the median-of-medians pivot. Divides the length {@code n} into groups
6619      * of at most 5 elements, computes the median of each group, and the median of the
6620      * {@code n/5} medians. Assumes {@code l <= r}.
6621      *
6622      * <p>The median-of-medians in computed in-place at the left end. The range containing
6623      * the medians is {@code [l, rr]} with the right bound {@code rr} returned.
6624      * In the event the pivot is a region of equal values, the range of the pivot values
6625      * is {@code [lp, p]}, with the {@code p} returned and {@code lp} set in the output bounds.
6626      *
6627      * @param part Partition function.
6628      * @param a Values.
6629      * @param l Lower bound of data (inclusive, assumed to be strictly positive).
6630      * @param r Upper bound of data (inclusive, assumed to be strictly positive).
6631      * @param bounds Bounds {@code [lp, rr]}.
6632      * @return the pivot index {@code p}
6633      */
6634     private int pivotMedianOfMedians(SPEPartition part, double[] a, int l, int r, int[] bounds) {
6635         // Process blocks of 5.
6636         // Moves the median of each block to the left of the array.
6637         int rr = l - 1;
6638         for (int e = l + 5;; e += 5) {
6639             if (e > r) {
6640                 // Final block of size 1-5
6641                 Sorting.sort(a, e - 5, r);
6642                 final int m = (e - 5 + r) >>> 1;
6643                 final double v = a[m];
6644                 a[m] = a[++rr];
6645                 a[rr] = v;
6646                 break;
6647             }
6648 
6649             // Various methods for time-critical step.
6650             // Each must be compiled and run on the same benchmark data.
6651             // Decision tree is fastest.
6652             //final int m = Sorting.median5(a, e - 5);
6653             //final int m = Sorting.median5(a, e - 5, e - 4, e - 3, e - 2, e - 1);
6654             // Bigger decision tree (same as median5)
6655             //final int m = Sorting.median5b(a, e - 5);
6656             // Sorting network of 4 + insertion (3-4% slower)
6657             //final int m = Sorting.median5c(a, e - 5);
6658             // In-place median: Sorting of 5, or median of 5
6659             final int m = e - 3;
6660             //Sorting.sort(a, e - 5, e - 1); // insertion sort
6661             //Sorting.sort5(a, e - 5, e - 4, e - 3, e - 2, e - 1);
6662             Sorting.median5d(a, e - 5, e - 4, e - 3, e - 2, e - 1);
6663 
6664             final double v = a[m];
6665             a[m] = a[++rr];
6666             a[rr] = v;
6667         }
6668 
6669         int m = (l + rr + 1) >>> 1;
6670         // mutual recursion
6671         linearSelect(part, a, l, rr, m, m, bounds);
6672         // bounds contains the range of the pivot.
6673         // return the upper pivot and record the end of the range.
6674         m = bounds[1];
6675         bounds[1] = rr;
6676         return m;
6677     }
6678 
6679     /**
6680      * Partition the array such that indices {@code k} correspond to their correctly
6681      * sorted value in the equivalent fully sorted array. For all indices {@code k}
6682      * and any index {@code i}:
6683      *
6684      * <pre>{@code
6685      * data[i < k] <= data[k] <= data[k < i]
6686      * }</pre>
6687      *
6688      * <p>The method assumes all {@code k} are valid indices into the data in {@code [0, length)}.
6689      * It assumes no NaNs or signed zeros in the data. Data must be pre- and post-processed.
6690      *
6691      * <p>Uses the median-of-medians algorithm to provide Order(n) performance.
6692      * This method has configurable deterministic pivot selection including those using
6693      * partition expansion in-place of full partitioning. The methods are based on the
6694      * QuickselectAdaptive method of Alexandrescu.
6695      *
6696      * @param data Values.
6697      * @param k Indices (may be destructively modified).
6698      * @param n Count of indices.
6699      * @see #setLinearStrategy(LinearStrategy)
6700      */
6701     void partitionLinear(double[] data, int[] k, int n) {
6702         quickSelect(linearSpFunction, data, k, n);
6703     }
6704 
6705     /**
6706      * Partition the array such that indices {@code k} correspond to their correctly
6707      * sorted value in the equivalent fully sorted array. For all indices {@code k}
6708      * and any index {@code i}:
6709      *
6710      * <pre>{@code
6711      * data[i < k] <= data[k] <= data[k < i]
6712      * }</pre>
6713      *
6714      * <p>The method assumes all {@code k} are valid indices into the data.
6715      * It handles NaN and signed zeros in the data.
6716      *
6717      * <p>This method assumes that the partition function can compute a pivot.
6718      * It is used for variants of the median-of-medians algorithm which use mutual
6719      * recursion for pivot selection.
6720      *
6721      * <p>WARNING: Currently this only supports a single or range of {@code k}.
6722      * For parity with other select methods this accepts an array {@code k} and pre/post
6723      * processes the data for NaN and signed zeros.
6724      *
6725      * @param part Partition function.
6726      * @param a Values.
6727      * @param k Indices (may be destructively modified).
6728      * @param count Count of indices.
6729      * @see #setLinearStrategy(LinearStrategy)
6730      */
6731     private void quickSelect(SPEPartition part, double[] a, int[] k, int count) {
6732         // Handle NaN / signed zeros
6733         final DoubleDataTransformer t = SORT_TRANSFORMER.get();
6734         // Assume this is in-place
6735         t.preProcess(a);
6736         final int end = t.length();
6737         int n = count;
6738         if (end > 1) {
6739             // Filter indices invalidated by NaN check
6740             if (end < a.length) {
6741                 for (int i = n; --i >= 0;) {
6742                     final int v = k[i];
6743                     if (v >= end) {
6744                         // swap(k, i, --n)
6745                         k[i] = k[--n];
6746                         k[n] = v;
6747                     }
6748                 }
6749             }
6750             if (n != 0) {
6751                 final int ka = Math.min(k[0], k[n - 1]);
6752                 final int kb = Math.max(k[0], k[n - 1]);
6753                 quickSelect(part, a, 0, end - 1, ka, kb, new int[2]);
6754             }
6755         }
6756         // Restore signed zeros
6757         t.postProcess(a, k, n);
6758     }
6759 
6760     /**
6761      * Partition the array such that indices {@code k} correspond to their
6762      * correctly sorted value in the equivalent fully sorted array.
6763      *
6764      * <p>For all indices {@code [ka, kb]} and any index {@code i}:
6765      *
6766      * <pre>{@code
6767      * data[i < ka] <= data[ka] <= data[kb] <= data[kb < i]
6768      * }</pre>
6769      *
6770      * <p>This function accepts indices {@code [ka, kb]} that define the
6771      * range of indices to partition. It is expected that the range is small.
6772      *
6773      * <p>This method assumes that the partition function can compute a pivot.
6774      * It is used for variants of the median-of-medians algorithm which use mutual
6775      * recursion for pivot selection. This method is based on the improvements
6776      * for median-of-medians algorithms in Alexandrescu (2016) (median-of-medians
6777      * and median-of-median-of-medians).
6778      *
6779      * <p>Returns the bounds containing {@code [ka, kb]}. These may be lower/higher
6780      * than the keys if equal values are present in the data.
6781      *
6782      * @param part Partition function.
6783      * @param a Values.
6784      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
6785      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
6786      * @param ka First key of interest.
6787      * @param kb Last key of interest.
6788      * @param bounds Bounds of the range containing {@code [ka, kb]} (inclusive).
6789      * @see #setLinearStrategy(LinearStrategy)
6790      */
6791     private void quickSelect(SPEPartition part, double[] a, int left, int right, int ka, int kb,
6792             int[] bounds) {
6793         int l = left;
6794         int r = right;
6795         while (true) {
6796             // Select when ka and kb are close to the same end
6797             // |l|-----|ka|kkkkkkkk|kb|------|r|
6798             // Optimal value for this is much higher than standard quickselect due
6799             // to the high cost of median-of-medians pivot computation and reuse via
6800             // mutual recursion so we have a different value.
6801             // Note: Use of this will not break the Order(n) performance for worst
6802             // case data, i.e. data where all values require full insertion.
6803             // This will be Order(n * k) == Order(n); k becomes a multiplier as long as
6804             // k << n; otherwise worst case is Order(n^2 / 2) when k=n/2.
6805             if (Math.min(kb - l, r - ka) < linearSortSelectSize) {
6806                 sortSelectRange(a, l, r, ka, kb);
6807                 // We could scan left/right to extend the bounds here after the sort.
6808                 // Attempts to do this were not measurable in benchmarking.
6809                 bounds[0] = ka;
6810                 bounds[1] = kb;
6811                 return;
6812             }
6813             // Only target ka; kb is assumed to be close
6814             final int p0 = part.partition(a, l, r, ka, bounds);
6815             final int p1 = bounds[0];
6816 
6817             // Note: Here we expect [ka, kb] to be small and splitting is unlikely.
6818             //                   p0 p1
6819             // |l|--|ka|kkkk|kb|--|P|-------------------|r|
6820             // |l|----------------|P|--|ka|kkk|kb|------|r|
6821             // |l|-----------|ka|k|P|k|kb|--------------|r|
6822             if (kb < p0) {
6823                 // Entirely on left side
6824                 r = p0 - 1;
6825             } else if (ka > p1) {
6826                 // Entirely on right side
6827                 l = p1 + 1;
6828             } else {
6829                 // Pivot splits [ka, kb]. Expect ends to be close to the pivot and finish.
6830                 // Here we set the bounds for use after median-of-medians pivot selection.
6831                 // In the event there are many equal values this allows collecting those
6832                 // known to be equal together when moving around the medians sample.
6833                 bounds[0] = p0;
6834                 bounds[1] = p1;
6835                 if (ka < p0) {
6836                     sortSelectRight(a, l, p0 - 1, ka);
6837                     bounds[0] = ka;
6838                 }
6839                 if (kb > p1) {
6840                     sortSelectLeft(a, p1 + 1, r, kb);
6841                     bounds[1] = kb;
6842                 }
6843                 return;
6844             }
6845         }
6846     }
6847 
6848     /**
6849      * Partition the array such that indices {@code k} correspond to their correctly
6850      * sorted value in the equivalent fully sorted array. For all indices {@code k}
6851      * and any index {@code i}:
6852      *
6853      * <pre>{@code
6854      * data[i < k] <= data[k] <= data[k < i]
6855      * }</pre>
6856      *
6857      * <p>The method assumes all {@code k} are valid indices into the data in {@code [0, length)}.
6858      * It assumes no NaNs or signed zeros in the data. Data must be pre- and post-processed.
6859      *
6860      * <p>Uses the QuickselectAdaptive method of Alexandrescu. This is based on the
6861      * median-of-medians algorithm. The median sample strategy is chosen based on
6862      * the target index.
6863      *
6864      * @param data Values.
6865      * @param k Indices (may be destructively modified).
6866      * @param n Count of indices.
6867      */
6868     void partitionQA(double[] data, int[] k, int n) {
6869         quickSelectAdaptive(data, k, n);
6870     }
6871 
6872     /**
6873      * Partition the array such that indices {@code k} correspond to their correctly
6874      * sorted value in the equivalent fully sorted array. For all indices {@code k}
6875      * and any index {@code i}:
6876      *
6877      * <pre>{@code
6878      * data[i < k] <= data[k] <= data[k < i]
6879      * }</pre>
6880      *
6881      * <p>The method assumes all {@code k} are valid indices into the data.
6882      * It handles NaN and signed zeros in the data.
6883      *
6884      * <p>WARNING: Currently this only supports a single or range of {@code k}.
6885      * For parity with other select methods this accepts an array {@code k} and pre/post
6886      * processes the data for NaN and signed zeros.
6887      *
6888      * @param a Values.
6889      * @param k Indices (may be destructively modified).
6890      * @param count Count of indices.
6891      */
6892     private void quickSelectAdaptive(double[] a, int[] k, int count) {
6893         // Handle NaN / signed zeros
6894         final DoubleDataTransformer t = SORT_TRANSFORMER.get();
6895         // Assume this is in-place
6896         t.preProcess(a);
6897         final int end = t.length();
6898         int n = count;
6899         if (end > 1) {
6900             // Filter indices invalidated by NaN check
6901             if (end < a.length) {
6902                 for (int i = n; --i >= 0;) {
6903                     final int v = k[i];
6904                     if (v >= end) {
6905                         // swap(k, i, --n)
6906                         k[i] = k[--n];
6907                         k[n] = v;
6908                     }
6909                 }
6910             }
6911             if (n != 0) {
6912                 final int ka = Math.min(k[0], k[n - 1]);
6913                 final int kb = Math.max(k[0], k[n - 1]);
6914                 quickSelectAdaptive(a, 0, end - 1, ka, kb, new int[1], adaptMode);
6915             }
6916         }
6917         // Restore signed zeros
6918         t.postProcess(a, k, n);
6919     }
6920 
6921     /**
6922      * Partition the array such that indices {@code k} correspond to their
6923      * correctly sorted value in the equivalent fully sorted array.
6924      *
6925      * <p>For all indices {@code [ka, kb]} and any index {@code i}:
6926      *
6927      * <pre>{@code
6928      * data[i < ka] <= data[ka] <= data[kb] <= data[kb < i]
6929      * }</pre>
6930      *
6931      * <p>This function accepts indices {@code [ka, kb]} that define the
6932      * range of indices to partition. It is expected that the range is small.
6933      *
6934      * <p>Uses the QuickselectAdaptive method of Alexandrescu. This is based on the
6935      * median-of-medians algorithm. The median sample is strategy is chosen based on
6936      * the target index.
6937      *
6938      * <p>The adaption {@code mode} is used to control the sampling mode and adaption of
6939      * the index within the sample.
6940      *
6941      * <p>Returns the bounds containing {@code [ka, kb]}. These may be lower/higher
6942      * than the keys if equal values are present in the data.
6943      *
6944      * @param a Values.
6945      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
6946      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
6947      * @param ka First key of interest.
6948      * @param kb Last key of interest.
6949      * @param bounds Upper bound of the range containing {@code [ka, kb]} (inclusive).
6950      * @param mode Adaption mode.
6951      * @return Lower bound of the range containing {@code [ka, kb]} (inclusive).
6952      */
6953     private int quickSelectAdaptive(double[] a, int left, int right, int ka, int kb,
6954             int[] bounds, AdaptMode mode) {
6955         int l = left;
6956         int r = right;
6957         AdaptMode m = mode;
6958         while (true) {
6959             // Select when ka and kb are close to the same end
6960             // |l|-----|ka|kkkkkkkk|kb|------|r|
6961             // Optimal value for this is much higher than standard quickselect due
6962             // to the high cost of median-of-medians pivot computation and reuse via
6963             // mutual recursion so we have a different value.
6964             if (Math.min(kb - l, r - ka) < linearSortSelectSize) {
6965                 sortSelectRange(a, l, r, ka, kb);
6966                 bounds[0] = kb;
6967                 return ka;
6968             }
6969 
6970             // Only target ka; kb is assumed to be close
6971             int p0;
6972             int n = r - l;
6973             // f in [0, 1]
6974             final double f = (double) (ka - l) / n;
6975             // Note: Margins for fraction left/right of pivot L : R.
6976             // Subtract the larger margin to create the estimated size
6977             // after partitioning. If the new size subtracted from
6978             // the estimated size is negative (partition did not meet
6979             // the margin guarantees) then adaption mode is changed (to
6980             // a method more likely to achieve the margins).
6981             if (f <= STEP_LEFT) {
6982                 if (m.isSampleMode() && n > subSamplingSize) {
6983                     // Floyd-Rivest sampling. Expect to eliminate the same as QA steps.
6984                     if (f <= STEP_FAR_LEFT) {
6985                         n -= (n >> 2) + (n >> 5) + (n >> 6);
6986                     } else {
6987                         n -= (n >> 2) + (n >> 3);
6988                     }
6989                     p0 = sampleStep(a, l, r, ka, bounds, m);
6990                 } else if (f <= STEP_FAR_LEFT) {
6991                     if ((controlFlags & FLAG_QA_FAR_STEP) != 0) {
6992                         // 1/12 : 1/3 (use 1/4 + 1/32 + 1/64 ~ 0.328)
6993                         n -= (n >> 2) + (n >> 5) + (n >> 6);
6994                         p0 = repeatedStepFarLeft(a, l, r, ka, bounds, m);
6995                     } else {
6996                         // 1/12 : 3/8
6997                         n -= (n >> 2) + (n >> 3);
6998                         p0 = repeatedStepLeft(a, l, r, ka, bounds, m, true);
6999                     }
7000                 } else {
7001                     // 1/6 : 1/4
7002                     n -= n >> 2;
7003                     p0 = repeatedStepLeft(a, l, r, ka, bounds, m, false);
7004                 }
7005             } else if (f >= STEP_RIGHT) {
7006                 if (m.isSampleMode() && n > subSamplingSize) {
7007                     // Floyd-Rivest sampling. Expect to eliminate the same as QA steps.
7008                     if (f >= STEP_FAR_RIGHT) {
7009                         n -= (n >> 2) + (n >> 5) + (n >> 6);
7010                     } else {
7011                         n -= (n >> 2) + (n >> 3);
7012                     }
7013                     p0 = sampleStep(a, l, r, ka, bounds, m);
7014                 } else if (f >= STEP_FAR_RIGHT) {
7015                     if ((controlFlags & FLAG_QA_FAR_STEP) != 0) {
7016                         // 1/12 : 1/3 (use 1/4 + 1/32 + 1/64 ~ 0.328)
7017                         n -= (n >> 2) + (n >> 5) + (n >> 6);
7018                         p0 = repeatedStepFarRight(a, l, r, ka, bounds, m);
7019                     } else {
7020                         // 3/8 : 1/12
7021                         n -= (n >> 2) + (n >> 3);
7022                         p0 = repeatedStepRight(a, l, r, ka, bounds, m, true);
7023                     }
7024                 } else {
7025                     // 1/4 : 1/6
7026                     n -= n >> 2;
7027                     p0 = repeatedStepRight(a, l, r, ka, bounds, m, false);
7028                 }
7029             } else {
7030                 if (m.isSampleMode() && n > subSamplingSize) {
7031                     // Floyd-Rivest sampling. Expect to eliminate the same as QA steps.
7032                     p0 = sampleStep(a, l, r, ka, bounds, m);
7033                 } else {
7034                     p0 = repeatedStep(a, l, r, ka, bounds, m);
7035                 }
7036                 // 2/9 : 2/9 (use 1/4 - 1/32 ~ 0.219)
7037                 n -= (n >> 2) - (n >> 5);
7038             }
7039 
7040             // Note: Here we expect [ka, kb] to be small and splitting is unlikely.
7041             //                   p0 p1
7042             // |l|--|ka|kkkk|kb|--|P|-------------------|r|
7043             // |l|----------------|P|--|ka|kkk|kb|------|r|
7044             // |l|-----------|ka|k|P|k|kb|--------------|r|
7045             final int p1 = bounds[0];
7046             if (kb < p0) {
7047                 // Entirely on left side
7048                 r = p0 - 1;
7049             } else if (ka > p1) {
7050                 // Entirely on right side
7051                 l = p1 + 1;
7052             } else {
7053                 // Pivot splits [ka, kb]. Expect ends to be close to the pivot and finish.
7054                 // Here we set the bounds for use after median-of-medians pivot selection.
7055                 // In the event there are many equal values this allows collecting those
7056                 // known to be equal together when moving around the medians sample.
7057                 if (kb > p1) {
7058                     sortSelectLeft(a, p1 + 1, r, kb);
7059                     bounds[0] = kb;
7060                 }
7061                 if (ka < p0) {
7062                     sortSelectRight(a, l, p0 - 1, ka);
7063                     p0 = ka;
7064                 }
7065                 return p0;
7066             }
7067             // Update sampling mode
7068             m = m.update(n, l, r);
7069         }
7070     }
7071 
7072     /**
7073      * Partition the array such that indices {@code k} correspond to their correctly
7074      * sorted value in the equivalent fully sorted array. For all indices {@code k}
7075      * and any index {@code i}:
7076      *
7077      * <pre>{@code
7078      * data[i < k] <= data[k] <= data[k < i]
7079      * }</pre>
7080      *
7081      * <p>The method assumes all {@code k} are valid indices into the data in {@code [0, length)}.
7082      * It assumes no NaNs or signed zeros in the data. Data must be pre- and post-processed.
7083      *
7084      * <p>Uses the QuickselectAdaptive method of Alexandrescu. This is based on the
7085      * median-of-medians algorithm. The median sample is strategy is chosen based on
7086      * the target index.
7087      *
7088      * <p>Differences to QA
7089      *
7090      * <p>This function is not as configurable as {@link #partitionQA(double[], int[], int)};
7091      * it is composed of the best performing configuration from benchmarking.
7092      *
7093      * <p>A key difference is that this method allows starting with Floyd-Rivest sub-sampling,
7094      * then progression to QuickselectAdaptive sampling, before disabling of sampling. This
7095      * method can thus have two attempts at sampling (FR, then QA) before disabling sampling. The
7096      * method can also be configured to start at QA sampling, or skip QA sampling when starting
7097      * with FR sampling depending on the configured starting mode and increment
7098      * (see {@link #configureQaAdaptive(int, int)}).
7099      *
7100      * @param data Values.
7101      * @param k Indices (may be destructively modified).
7102      * @param n Count of indices.
7103      */
7104     static void partitionQA2(double[] data, int[] k, int n) {
7105         quickSelectAdaptive2(data, k, n, qaMode);
7106     }
7107 
7108     /**
7109      * Partition the array such that indices {@code k} correspond to their correctly
7110      * sorted value in the equivalent fully sorted array. For all indices {@code k} and
7111      * any index {@code i}:
7112      *
7113      * <pre>{@code
7114      * data[i < k] <= data[k] <= data[k < i]
7115      * }</pre>
7116      *
7117      * <p>The method assumes all {@code k} are valid indices into the data. It handles NaN
7118      * and signed zeros in the data.
7119      *
7120      * <p>WARNING: Currently this only supports a single or range of {@code k}. For parity
7121      * with other select methods this accepts an array {@code k} and pre/post processes
7122      * the data for NaN and signed zeros.
7123      *
7124      * @param a Values.
7125      * @param k Indices (may be destructively modified).
7126      * @param count Count of indices.
7127      * @param flags Adaption flags.
7128      */
7129     static void quickSelectAdaptive2(double[] a, int[] k, int count, int flags) {
7130         // Handle NaN / signed zeros
7131         final DoubleDataTransformer t = SORT_TRANSFORMER.get();
7132         // Assume this is in-place
7133         t.preProcess(a);
7134         final int end = t.length();
7135         int n = count;
7136         if (end > 1) {
7137             // Filter indices invalidated by NaN check
7138             if (end < a.length) {
7139                 for (int i = n; --i >= 0;) {
7140                     final int v = k[i];
7141                     if (v >= end) {
7142                         // swap(k, i, --n)
7143                         k[i] = k[--n];
7144                         k[n] = v;
7145                     }
7146                 }
7147             }
7148             if (n != 0) {
7149                 final int ka = Math.min(k[0], k[n - 1]);
7150                 final int kb = Math.max(k[0], k[n - 1]);
7151                 quickSelectAdaptive2(a, 0, end - 1, ka, kb, new int[1], flags);
7152             }
7153         }
7154         // Restore signed zeros
7155         t.postProcess(a, k, n);
7156     }
7157 
7158     /**
7159      * Partition the array such that indices {@code k} correspond to their
7160      * correctly sorted value in the equivalent fully sorted array.
7161      *
7162      * <p>For all indices {@code [ka, kb]} and any index {@code i}:
7163      *
7164      * <pre>{@code
7165      * data[i < ka] <= data[ka] <= data[kb] <= data[kb < i]
7166      * }</pre>
7167      *
7168      * <p>This function accepts indices {@code [ka, kb]} that define the
7169      * range of indices to partition. It is expected that the range is small.
7170      *
7171      * <p>Uses the QuickselectAdaptive method of Alexandrescu. This is based on the
7172      * median-of-medians algorithm. The median sample is strategy is chosen based on
7173      * the target index.
7174      *
7175      * <p>The control {@code flags} are used to control the sampling mode and adaption of
7176      * the index within the sample.
7177      *
7178      * <p>Returns the bounds containing {@code [ka, kb]}. These may be lower/higher
7179      * than the keys if equal values are present in the data.
7180      *
7181      * @param a Values.
7182      * @param left Lower bound of data (inclusive, assumed to be strictly positive).
7183      * @param right Upper bound of data (inclusive, assumed to be strictly positive).
7184      * @param ka First key of interest.
7185      * @param kb Last key of interest.
7186      * @param bounds Upper bound of the range containing {@code [ka, kb]} (inclusive).
7187      * @param flags Adaption flags.
7188      * @return Lower bound of the range containing {@code [ka, kb]} (inclusive).
7189      */
7190     private static int quickSelectAdaptive2(double[] a, int left, int right, int ka, int kb,
7191             int[] bounds, int flags) {
7192         int l = left;
7193         int r = right;
7194         int m = flags;
7195         while (true) {
7196             // Select when ka and kb are close to the same end
7197             // |l|-----|ka|kkkkkkkk|kb|------|r|
7198             if (Math.min(kb - l, r - ka) < LINEAR_SORTSELECT_SIZE) {
7199                 sortSelectRange(a, l, r, ka, kb);
7200                 bounds[0] = kb;
7201                 return ka;
7202             }
7203 
7204             // Only target ka; kb is assumed to be close
7205             int p0;
7206             final int n = r - l;
7207             // f in [0, 1]
7208             final double f = (double) (ka - l) / n;
7209             // Record the larger margin (start at 1/4) to create the estimated size.
7210             // step        L     R
7211             // far left    1/12  1/3   (use 1/4 + 1/32 + 1/64 ~ 0.328)
7212             // left        1/6   1/4
7213             // middle      2/9   2/9   (use 1/4 - 1/32 ~ 0.219)
7214             int margin = n >> 2;
7215             if (m < MODE_SAMPLING && r - l > SELECT_SUB_SAMPLING_SIZE) {
7216                 // Floyd-Rivest sample step uses the same margins
7217                 p0 = sampleStep(a, l, r, ka, bounds, flags);
7218                 if (f <= STEP_FAR_LEFT || f >= STEP_FAR_RIGHT) {
7219                     margin += (n >> 5) + (n >> 6);
7220                 } else if (f > STEP_LEFT && f < STEP_RIGHT) {
7221                     margin -= n >> 5;
7222                 }
7223             } else if (f <= STEP_LEFT) {
7224                 if (f <= STEP_FAR_LEFT) {
7225                     margin += (n >> 5) + (n >> 6);
7226                     p0 = repeatedStepFarLeft(a, l, r, ka, bounds, m);
7227                 } else {
7228                     p0 = repeatedStepLeft(a, l, r, ka, bounds, m);
7229                 }
7230             } else if (f >= STEP_RIGHT) {
7231                 if (f >= STEP_FAR_RIGHT) {
7232                     margin += (n >> 5) + (n >> 6);
7233                     p0 = repeatedStepFarRight(a, l, r, ka, bounds, m);
7234                 } else {
7235                     p0 = repeatedStepRight(a, l, r, ka, bounds, m);
7236                 }
7237             } else {
7238                 margin -= n >> 5;
7239                 p0 = repeatedStep(a, l, r, ka, bounds, m);
7240             }
7241 
7242             // Note: Here we expect [ka, kb] to be small and splitting is unlikely.
7243             //                   p0 p1
7244             // |l|--|ka|kkkk|kb|--|P|-------------------|r|
7245             // |l|----------------|P|--|ka|kkk|kb|------|r|
7246             // |l|-----------|ka|k|P|k|kb|--------------|r|
7247             final int p1 = bounds[0];
7248             if (kb < p0) {
7249                 // Entirely on left side
7250                 r = p0 - 1;
7251             } else if (ka > p1) {
7252                 // Entirely on right side
7253                 l = p1 + 1;
7254             } else {
7255                 // Pivot splits [ka, kb]. Expect ends to be close to the pivot and finish.
7256                 // Here we set the bounds for use after median-of-medians pivot selection.
7257                 // In the event there are many equal values this allows collecting those
7258                 // known to be equal together when moving around the medians sample.
7259                 if (kb > p1) {
7260                     sortSelectLeft(a, p1 + 1, r, kb);
7261                     bounds[0] = kb;
7262                 }
7263                 if (ka < p0) {
7264                     sortSelectRight(a, l, p0 - 1, ka);
7265                     p0 = ka;
7266                 }
7267                 return p0;
7268             }
7269             // Update mode based on target partition size
7270             m += r - l > n - margin ? qaIncrement : 0;
7271         }
7272     }
7273 
7274     /**
7275      * Partition an array slice around a pivot. Partitioning exchanges array elements such
7276      * that all elements smaller than pivot are before it and all elements larger than
7277      * pivot are after it.
7278      *
7279      * <p>Implements {@link SPEPartitionFunction}. This method is not static as the
7280      * pivot strategy and minimum quick select size are used within the method.
7281      *
7282      * <p>Note: Handles signed zeros.
7283      *
7284      * <p>Uses a Bentley-McIlroy quicksort partition method by Sedgewick.
7285      *
7286      * @param data Data array.
7287      * @param left Lower bound (inclusive).
7288      * @param right Upper bound (inclusive).
7289      * @param upper Upper bound (inclusive) of the pivot range.
7290      * @param leftInner Flag to indicate {@code left - 1} is a pivot.
7291      * @param rightInner Flag to indicate {@code right + 1} is a pivot.
7292      * @return Lower bound (inclusive) of the pivot range.
7293      */
7294     private int partitionSBMWithZeros(double[] data, int left, int right, int[] upper,
7295         boolean leftInner, boolean rightInner) {
7296         // Single-pivot Bentley-McIlroy quicksort handling equal keys (Sedgewick's algorithm).
7297         //
7298         // Partition data using pivot P into less-than, greater-than or equal.
7299         // P is placed at the end to act as a sentinel.
7300         // k traverses the unknown region ??? and values moved if equal (l) or greater (g):
7301         //
7302         // left    p       i            j         q    right
7303         // |  ==P  |  <P   |     ???    |   >P    | ==P  |P|
7304         //
7305         // At the end P and additional equal values are swapped back to the centre.
7306         //
7307         // |         <P        | ==P |            >P        |
7308         //
7309         // Adapted from Sedgewick "Quicksort is optimal"
7310         // https://sedgewick.io/wp-content/themes/sedgewick/talks/2002QuicksortIsOptimal.pdf
7311         //
7312         // The algorithm has been changed so that:
7313         // - A pivot point must be provided.
7314         // - An edge case where the search meets in the middle is handled.
7315         // - Equal value data is not swapped to the end. Since the value is fixed then
7316         //   only the less than / greater than value must be moved from the end inwards.
7317         //   The end is then assumed to be the equal value. This would not work with
7318         //   object references. Equivalent swap calls are commented.
7319         // - Added a fast-forward over initial range containing the pivot.
7320 
7321         // Switch to insertion sort for small range
7322         if (right - left <= minQuickSelectSize) {
7323             Sorting.sort(data, left, right, leftInner);
7324             fixContinuousSignedZeros(data, left, right);
7325             upper[0] = right;
7326             return left;
7327         }
7328 
7329         final int l = left;
7330         final int r = right;
7331 
7332         int p = l;
7333         int q = r;
7334 
7335         // Use the pivot index to set the upper sentinel value.
7336         // Pass -1 as the target k (should trigger an IOOBE if strategy uses it).
7337         final int pivot = pivotingStrategy.pivotIndex(data, left, right, -1);
7338         final double v = data[pivot];
7339         data[pivot] = data[r];
7340         data[r] = v;
7341 
7342         // Special case: count signed zeros
7343         int c = 0;
7344         if (v == 0) {
7345             c = countMixedSignedZeros(data, left, right);
7346         }
7347 
7348         // Fast-forward over equal regions to reduce swaps
7349         while (data[p] == v) {
7350             if (++p == q) {
7351                 // Edge-case: constant value
7352                 if (c != 0) {
7353                     sortZero(data, left, right);
7354                 }
7355                 upper[0] = right;
7356                 return left;
7357             }
7358         }
7359         // Cannot overrun as the prior scan using p stopped before the end
7360         while (data[q - 1] == v) {
7361             q--;
7362         }
7363 
7364         int i = p - 1;
7365         int j = q;
7366 
7367         for (;;) {
7368             do {
7369                 ++i;
7370             } while (data[i] < v);
7371             while (v < data[--j]) {
7372                 if (j == l) {
7373                     break;
7374                 }
7375             }
7376             if (i >= j) {
7377                 // Edge-case if search met on an internal pivot value
7378                 // (not at the greater equal region, i.e. i < q).
7379                 // Move this to the lower-equal region.
7380                 if (i == j && v == data[i]) {
7381                     //swap(data, i++, p++)
7382                     data[i] = data[p];
7383                     data[p] = v;
7384                     i++;
7385                     p++;
7386                 }
7387                 break;
7388             }
7389             //swap(data, i, j)
7390             final double vi = data[j];
7391             final double vj = data[i];
7392             data[i] = vi;
7393             data[j] = vj;
7394             // Move the equal values to the ends
7395             if (vi == v) {
7396                 //swap(data, i, p++)
7397                 data[i] = data[p];
7398                 data[p] = v;
7399                 p++;
7400             }
7401             if (vj == v) {
7402                 //swap(data, j, --q)
7403                 data[j] = data[--q];
7404                 data[q] = v;
7405             }
7406         }
7407         // i is at the end (exclusive) of the less-than region
7408 
7409         // Place pivot value in centre
7410         //swap(data, r, i)
7411         data[r] = data[i];
7412         data[i] = v;
7413 
7414         // Move equal regions to the centre.
7415         // Set the pivot range [j, i) and move this outward for equal values.
7416         j = i++;
7417 
7418         // less-equal:
7419         //   for (int k = l; k < p; k++):
7420         //     swap(data, k, --j)
7421         // greater-equal:
7422         //   for (int k = r; k-- > q; i++) {
7423         //     swap(data, k, i)
7424 
7425         // Move the minimum of less-equal or less-than
7426         int move = Math.min(p - l, j - p);
7427         final int lower = j - (p - l);
7428         for (int k = l; move-- > 0; k++) {
7429             data[k] = data[--j];
7430             data[j] = v;
7431         }
7432         // Move the minimum of greater-equal or greater-than
7433         move = Math.min(r - q, q - i);
7434         upper[0] = i + (r - q) - 1;
7435         for (int k = r; move-- > 0; i++) {
7436             data[--k] = data[i];
7437             data[i] = v;
7438         }
7439 
7440         // Special case: fixed signed zeros
7441         if (c != 0) {
7442             p = lower;
7443             while (c-- > 0) {
7444                 data[p++] = -0.0;
7445             }
7446             while (p <= upper[0]) {
7447                 data[p++] = 0.0;
7448             }
7449         }
7450 
7451         // Equal in [lower, upper]
7452         return lower;
7453     }
7454 
7455     /**
7456      * Partition an array slice around a pivot. Partitioning exchanges array elements such
7457      * that all elements smaller than pivot are before it and all elements larger than
7458      * pivot are after it.
7459      *
7460      * <p>Uses a single pivot partition method. This method does not handle equal values
7461      * at the pivot location: {@code lower == upper}. The method conforms to the
7462      * {@link SPEPartition} interface to allow use with the single-pivot introselect method.
7463      *
7464      * @param data Data array.
7465      * @param l Lower bound (inclusive).
7466      * @param r Upper bound (inclusive).
7467      * @param pivot Pivot index.
7468      * @param upper Upper bound (inclusive) of the pivot range.
7469      * @return Lower bound (inclusive) of the pivot range.
7470      */
7471     private static int partitionSP(double[] data, int l, int r, int pivot, int[] upper) {
7472         // Partition data using pivot P into less-than or greater-than.
7473         //
7474         // Adapted from Floyd and Rivest (1975)
7475         // Algorithm 489: The Algorithm SELECT—for Finding the ith Smallest of n elements.
7476         // Comm. ACM. 18 (3): 173.
7477         //
7478         // Sub-script range checking has been eliminated by appropriate placement
7479         // of values at the ends to act as sentinels.
7480         //
7481         // left           i            j               right
7482         // |<=P|     <P   |     ???    |   >P          |>=P|
7483         //
7484         // At the end P is swapped back to the centre.
7485         //
7486         // |         <P          |P|             >P        |
7487         final double v = data[pivot];
7488         // swap(left, pivot)
7489         data[pivot] = data[l];
7490         if (data[r] > v) {
7491             // swap(right, left)
7492             data[l] = data[r];
7493             data[r] = v;
7494             // Here after the first swap: a[l] = v; a[r] > v
7495         } else {
7496             data[l] = v;
7497             // Here after the first swap: a[l] <= v; a[r] = v
7498         }
7499         int i = l;
7500         int j = r;
7501         while (i < j) {
7502             // swap(i, j)
7503             final double temp = data[i];
7504             data[i] = data[j];
7505             data[j] = temp;
7506             do {
7507                 ++i;
7508             } while (data[i] < v);
7509             do {
7510                 --j;
7511             } while (data[j] > v);
7512         }
7513         // Move pivot back to the correct location from either l or r
7514         if (data[l] == v) {
7515             // data[j] <= v : swap(left, j)
7516             data[l] = data[j];
7517             data[j] = v;
7518         } else {
7519             // data[j+1] > v : swap(j+1, right)
7520             data[r] = data[++j];
7521             data[j] = v;
7522         }
7523         upper[0] = j;
7524         return j;
7525     }
7526 
7527     /**
7528      * Partition an array slice around a pivot. Partitioning exchanges array elements such
7529      * that all elements smaller than pivot are before it and all elements larger than
7530      * pivot are after it.
7531      *
7532      * <p>Uses a Bentley-McIlroy quicksort partition method.
7533      *
7534      * @param data Data array.
7535      * @param l Lower bound (inclusive).
7536      * @param r Upper bound (inclusive).
7537      * @param pivot Initial index of the pivot.
7538      * @param upper Upper bound (inclusive) of the pivot range.
7539      * @return Lower bound (inclusive) of the pivot range.
7540      */
7541     private static int partitionBM(double[] data, int l, int r, int pivot, int[] upper) {
7542         // Single-pivot Bentley-McIlroy quicksort handling equal keys.
7543         //
7544         // Adapted from program 7 in Bentley-McIlroy (1993)
7545         // Engineering a sort function
7546         // SOFTWARE—PRACTICE AND EXPERIENCE, VOL.23(11), 1249–1265
7547         //
7548         // 3-way partition of the data using a pivot value into
7549         // less-than, equal or greater-than.
7550         //
7551         // First partition data into 4 reqions by scanning the unknown region from
7552         // left (i) and right (j) and moving equal values to the ends:
7553         //                  i->       <-j
7554         // l        p       |           |         q       r
7555         // |   ==   |   <   |    ???    |    >    |  ==   |
7556         //
7557         //                    <-j
7558         // l        p             i               q       r
7559         // |   ==   |      <      |       >       |  ==   |
7560         //
7561         // Then the equal values are copied from the ends to the centre:
7562         // | less        |        equal      |    greater |
7563 
7564         int i = l;
7565         int j = r;
7566         int p = l;
7567         int q = r;
7568 
7569         final double v = data[pivot];
7570 
7571         for (;;) {
7572             while (i <= j && data[i] <= v) {
7573                 if (data[i] == v) {
7574                     //swap(data, i, p++)
7575                     data[i] = data[p];
7576                     data[p] = v;
7577                     p++;
7578                 }
7579                 i++;
7580             }
7581             while (j >= i && data[j] >= v) {
7582                 if (v == data[j]) {
7583                     //swap(data, j, q--)
7584                     data[j] = data[q];
7585                     data[q] = v;
7586                     q--;
7587                 }
7588                 j--;
7589             }
7590             if (i > j) {
7591                 break;
7592             }
7593             //swap(data, i++, j--)
7594             final double tmp = data[j];
7595             data[j] = data[i];
7596             data[i] = tmp;
7597         }
7598 
7599         // Move equal regions to the centre.
7600         int s = Math.min(p - l, i - p);
7601         for (int k = l; s > 0; k++, s--) {
7602             //swap(data, k, i - s)
7603             data[k] = data[i - s];
7604             data[i - s] = v;
7605         }
7606         s = Math.min(q - j, r - q);
7607         for (int k = i; --s >= 0; k++) {
7608             //swap(data, r - s, k)
7609             data[r - s] = data[k];
7610             data[k] = v;
7611         }
7612 
7613         // Set output range
7614         i = i - p + l;
7615         j = j - q + r;
7616         upper[0] = j;
7617 
7618         return i;
7619     }
7620 
7621     /**
7622      * Partition an array slice around a pivot. Partitioning exchanges array elements such
7623      * that all elements smaller than pivot are before it and all elements larger than
7624      * pivot are after it.
7625      *
7626      * <p>Uses a Bentley-McIlroy quicksort partition method by Sedgewick.
7627      *
7628      * @param data Data array.
7629      * @param l Lower bound (inclusive).
7630      * @param r Upper bound (inclusive).
7631      * @param pivot Pivot index.
7632      * @param upper Upper bound (inclusive) of the pivot range.
7633      * @return Lower bound (inclusive) of the pivot range.
7634      */
7635     static int partitionSBM(double[] data, int l, int r, int pivot, int[] upper) {
7636         // Single-pivot Bentley-McIlroy quicksort handling equal keys (Sedgewick's algorithm).
7637         //
7638         // Partition data using pivot P into less-than, greater-than or equal.
7639         // P is placed at the end to act as a sentinel.
7640         // k traverses the unknown region ??? and values moved if equal (l) or greater (g):
7641         //
7642         // left    p       i            j         q    right
7643         // |  ==P  |  <P   |     ???    |   >P    | ==P  |P|
7644         //
7645         // At the end P and additional equal values are swapped back to the centre.
7646         //
7647         // |         <P        | ==P |            >P        |
7648         //
7649         // Adapted from Sedgewick "Quicksort is optimal"
7650         // https://sedgewick.io/wp-content/themes/sedgewick/talks/2002QuicksortIsOptimal.pdf
7651         //
7652         // Note: The difference between this and the original BM partition is the use of
7653         // < or > rather than <= and >=. This allows the pivot to act as a sentinel and removes
7654         // the requirement for checks on i; and j can be checked against an unlikely condition.
7655         // This method will swap runs of equal values.
7656         //
7657         // The algorithm has been changed so that:
7658         // - A pivot point must be provided.
7659         // - An edge case where the search meets in the middle is handled.
7660         // - Added a fast-forward over any initial range containing the pivot.
7661         // - Changed the final move to perform the minimum moves.
7662 
7663         // Use the pivot index to set the upper sentinel value
7664         final double v = data[pivot];
7665         data[pivot] = data[r];
7666         data[r] = v;
7667 
7668         int p = l;
7669         int q = r;
7670 
7671         // Fast-forward over equal regions to reduce swaps
7672         while (data[p] == v) {
7673             if (++p == q) {
7674                 // Edge-case: constant value
7675                 upper[0] = r;
7676                 return l;
7677             }
7678         }
7679         // Cannot overrun as the prior scan using p stopped before the end
7680         while (data[q - 1] == v) {
7681             q--;
7682         }
7683 
7684         int i = p - 1;
7685         int j = q;
7686 
7687         for (;;) {
7688             do {
7689                 ++i;
7690             } while (data[i] < v);
7691             while (v < data[--j]) {
7692                 // Cannot use j == i in the event that i == q (already passed j)
7693                 if (j == l) {
7694                     break;
7695                 }
7696             }
7697             if (i >= j) {
7698                 // Edge-case if search met on an internal pivot value
7699                 // (not at the greater equal region, i.e. i < q).
7700                 // Move this to the lower-equal region.
7701                 if (i == j && v == data[i]) {
7702                     //swap(data, i++, p++)
7703                     data[i] = data[p];
7704                     data[p] = v;
7705                     i++;
7706                     p++;
7707                 }
7708                 break;
7709             }
7710             //swap(data, i, j)
7711             final double vi = data[j];
7712             final double vj = data[i];
7713             data[i] = vi;
7714             data[j] = vj;
7715             // Move the equal values to the ends
7716             if (vi == v) {
7717                 //swap(data, i, p++)
7718                 data[i] = data[p];
7719                 data[p] = v;
7720                 p++;
7721             }
7722             if (vj == v) {
7723                 //swap(data, j, --q)
7724                 data[j] = data[--q];
7725                 data[q] = v;
7726             }
7727         }
7728         // i is at the end (exclusive) of the less-than region
7729 
7730         // Place pivot value in centre
7731         //swap(data, r, i)
7732         data[r] = data[i];
7733         data[i] = v;
7734 
7735         // Move equal regions to the centre.
7736         // Set the pivot range [j, i) and move this outward for equal values.
7737         j = i++;
7738 
7739         // less-equal:
7740         //   for k = l; k < p; k++
7741         //     swap(data, k, --j)
7742         // greater-equal:
7743         //   for k = r; k-- > q; i++
7744         //     swap(data, k, i)
7745 
7746         // Move the minimum of less-equal or less-than
7747         int move = Math.min(p - l, j - p);
7748         final int lower = j - (p - l);
7749         for (int k = l; --move >= 0; k++) {
7750             data[k] = data[--j];
7751             data[j] = v;
7752         }
7753         // Move the minimum of greater-equal or greater-than
7754         move = Math.min(r - q, q - i);
7755         upper[0] = i + (r - q) - 1;
7756         for (int k = r; --move >= 0; i++) {
7757             data[--k] = data[i];
7758             data[i] = v;
7759         }
7760 
7761         // Equal in [lower, upper]
7762         return lower;
7763     }
7764 
7765     /**
7766      * Partition an array slice around a pivot. Partitioning exchanges array elements such
7767      * that all elements smaller than pivot are before it and all elements larger than
7768      * pivot are after it.
7769      *
7770      * <p>Uses a Bentley-McIlroy quicksort partition method by Kiwiel.
7771      *
7772      * @param x Data array.
7773      * @param l Lower bound (inclusive).
7774      * @param r Upper bound (inclusive).
7775      * @param pivot Pivot index.
7776      * @param upper Upper bound (inclusive) of the pivot range.
7777      * @return Lower bound (inclusive) of the pivot range.
7778      */
7779     static int partitionKBM(double[] x, int l, int r, int pivot, int[] upper) {
7780         // Single-pivot Bentley-McIlroy quicksort handling equal keys.
7781         //
7782         // Partition data using pivot v into less-than, greater-than or equal.
7783         // The basic idea is to work with the 5 inner parts of the array [ll, rr]
7784         // by positioning sentinels at l and r:
7785         //
7786         // |l |ll   p|          |i          j|         |q   rr| r|           (6.1)
7787         // |<v|  ==v |     <v   |     ???    |   >v    | ==v  |>v|
7788         //
7789         // until the middle part is empty or just contains an element equal to the pivot:
7790         //
7791         // |ll   p|              j|   |i          |q   rr|                   (6.2)
7792         // |  ==v |     <v        |==v|     >v    | ==v  |
7793         //
7794         // i.e. j = i-1 or i-2, then swap the ends into the middle:
7795         //
7796         // |ll              |a         d|              rr|                   (6.3)
7797         // |        <v      |     ==v   |      >v        |
7798         //
7799         // Adapted from Kiwiel (2005) "On Floyd and Rivest's SELECT algorithm"
7800         // Theoretical Computer Science 347, 214-238.
7801         // This is the safeguarded ternary partition Scheme E with modification to
7802         // prevent vacuous swaps of equal keys (section 5.6) in Kiwiel (2003)
7803         // Partitioning schemes for quicksort and quickselect,
7804         // Technical report, Systems Research Institute, Warsaw.
7805         // http://arxiv.org/abs/cs.DS/0312054
7806         //
7807         // Note: The difference between this and Sedgewick's BM is the use of sentinels
7808         // at either end to remove index checks at both ends and changing the behaviour
7809         // when i and j meet on a pivot value.
7810         //
7811         // The listing in Kiwiel (2005) has been updated:
7812         // - p and q mark the *inclusive* end of ==v regions.
7813         // - Added a fast-forward over initial range containing the pivot.
7814         // - Vector swap is optimised given one side of the exchange is v.
7815 
7816         final double v = x[pivot];
7817         x[pivot] = x[l];
7818         x[l] = v;
7819 
7820         int ll = l;
7821         int rr = r;
7822 
7823         // Ensure x[l] <= v <= x[r]
7824         if (v < x[r]) {
7825             --rr;
7826         } else if (v > x[r]) {
7827             x[l] = x[r];
7828             x[r] = v;
7829             ++ll;
7830         }
7831 
7832         // Position p and q for pre-in/decrement to write into edge pivot regions
7833         // Fast-forward over equal regions to reduce swaps
7834         int p = l;
7835         while (x[p + 1] == v) {
7836             if (++p == rr) {
7837                 // Edge-case: constant value in [ll, rr]
7838                 // Return the full range [l, r] as a single edge element
7839                 // will also be partitioned.
7840                 upper[0] = r;
7841                 return l;
7842             }
7843         }
7844         // Cannot overrun as the prior scan using p stopped before the end
7845         int q = r;
7846         while (x[q - 1] == v) {
7847             --q;
7848         }
7849 
7850         // [ll, p] and [q, rr] are pivot
7851         // Position for pre-in/decrement
7852         int i = p;
7853         int j = q;
7854 
7855         for (;;) {
7856             do {
7857                 ++i;
7858             } while (x[i] < v);
7859             do {
7860                 --j;
7861             } while (x[j] > v);
7862             // Here x[j] <= v <= x[i]
7863             if (i >= j) {
7864                 if (i == j) {
7865                     // x[i]=x[j]=v; update to leave the pivot in between (j, i)
7866                     ++i;
7867                     --j;
7868                 }
7869                 break;
7870             }
7871             //swap(x, i, j)
7872             final double vi = x[j];
7873             final double vj = x[i];
7874             x[i] = vi;
7875             x[j] = vj;
7876             // Move the equal values to the ends
7877             if (vi == v) {
7878                 x[i] = x[++p];
7879                 x[p] = v;
7880             }
7881             if (vj == v) {
7882                 x[j] = x[--q];
7883                 x[q] = v;
7884             }
7885         }
7886 
7887         // Set [a, d] (p and q are offset by 1 from Kiwiel)
7888         final int a = ll + j - p;
7889         upper[0] = rr - q + i;
7890 
7891         // Vector swap x[a:b] <-> x[b+1:c] means the first m = min(b+1-a, c-b)
7892         // elements of the array x[a:c] are exchanged with its last m elements.
7893         //vectorSwapL(x, ll, p, j, v);
7894         //vectorSwapR(x, i, q - 1, rr, v);
7895         // x[ll:p] <-> x[p+1:j]
7896         for (int m = Math.min(p + 1 - ll, j - p); --m >= 0; ++ll, --j) {
7897             x[ll] = x[j];
7898             x[j] = v;
7899         }
7900         // x[i:q-1] <-> x[q:rr]
7901         for (int m = Math.min(q - i, rr - q + 1); --m >= 0; ++i, --rr) {
7902             x[rr] = x[i];
7903             x[i] = v;
7904         }
7905         return a;
7906     }
7907 
7908     /**
7909      * Partition an array slice around a pivot. Partitioning exchanges array elements such
7910      * that all elements smaller than pivot are before it and all elements larger than
7911      * pivot are after it.
7912      *
7913      * <p>Uses a Dutch-National-Flag method handling equal keys (version 1).
7914      *
7915      * @param data Data array.
7916      * @param left Lower bound (inclusive).
7917      * @param right Upper bound (inclusive).
7918      * @param pivot Pivot index.
7919      * @param upper Upper bound (inclusive) of the pivot range.
7920      * @return Lower bound (inclusive) of the pivot range.
7921      */
7922     private static int partitionDNF1(double[] data, int left, int right, int pivot, int[] upper) {
7923         // Dutch National Flag partitioning:
7924         // https://www.baeldung.com/java-sorting-arrays-with-repeated-entries
7925         // https://en.wikipedia.org/wiki/Dutch_national_flag_problem
7926 
7927         // Partition data using pivot P into less-than, greater-than or equal.
7928         // i traverses the unknown region ??? and values moved to the correct end.
7929         //
7930         // left    lt      i           gt       right
7931         // |  < P  |   P   |     ???    |     > P   |
7932         //
7933         // We can delay filling in [lt, gt) with P until the end and only
7934         // move values in the wrong place.
7935 
7936         final double value = data[pivot];
7937 
7938         // Fast-forward initial less-than region
7939         int lt = left;
7940         while (data[lt] < value) {
7941             lt++;
7942         }
7943 
7944         // Pointers positioned to use pre-increment/decrement
7945         lt--;
7946         int gt = right + 1;
7947 
7948         // DNF partitioning which inspects one position per loop iteration
7949         for (int i = lt; ++i < gt;) {
7950             final double v = data[i];
7951             if (v < value) {
7952                 data[++lt] = v;
7953             } else if (v > value) {
7954                 data[i] = data[--gt];
7955                 data[gt] = v;
7956                 // Ensure data[i] is inspected next time
7957                 i--;
7958             }
7959             // else v == value and is in the central region to fill at the end
7960         }
7961 
7962         // Equal in (lt, gt) so adjust to [lt, gt]
7963         ++lt;
7964         upper[0] = --gt;
7965 
7966         // Fill the equal values gap
7967         for (int i = lt; i <= gt; i++) {
7968             data[i] = value;
7969         }
7970 
7971         return lt;
7972     }
7973 
7974     /**
7975      * Partition an array slice around a pivot. Partitioning exchanges array elements such
7976      * that all elements smaller than pivot are before it and all elements larger than
7977      * pivot are after it.
7978      *
7979      * <p>Uses a Dutch-National-Flag method handling equal keys (version 2).
7980      *
7981      * @param data Data array.
7982      * @param left Lower bound (inclusive).
7983      * @param right Upper bound (inclusive).
7984      * @param pivot Pivot index.
7985      * @param upper Upper bound (inclusive) of the pivot range.
7986      * @return Lower bound (inclusive) of the pivot range.
7987      */
7988     private static int partitionDNF2(double[] data, int left, int right, int pivot, int[] upper) {
7989         // Dutch National Flag partitioning:
7990         // https://www.baeldung.com/java-sorting-arrays-with-repeated-entries
7991         // https://en.wikipedia.org/wiki/Dutch_national_flag_problem
7992 
7993         // Partition data using pivot P into less-than, greater-than or equal.
7994         // i traverses the unknown region ??? and values moved to the correct end.
7995         //
7996         // left    lt      i           gt       right
7997         // |  < P  |   P   |     ???    |     > P   |
7998         //
7999         // We can delay filling in [lt, gt) with P until the end and only
8000         // move values in the wrong place.
8001 
8002         final double value = data[pivot];
8003 
8004         // Fast-forward initial less-than region
8005         int lt = left;
8006         while (data[lt] < value) {
8007             lt++;
8008         }
8009 
8010         // Pointers positioned to use pre-increment/decrement: ++x / --x
8011         lt--;
8012         int gt = right + 1;
8013 
8014         // Modified DNF partitioning with fast-forward of the greater-than
8015         // pointer. Note the fast-forward must check bounds.
8016         for (int i = lt; ++i < gt;) {
8017             final double v = data[i];
8018             if (v < value) {
8019                 data[++lt] = v;
8020             } else if (v > value) {
8021                 // Fast-forward here:
8022                 do {
8023                     --gt;
8024                 } while (gt > i && data[gt] > value);
8025                 // here data[gt] <= value
8026                 // if data[gt] == value we can skip over it
8027                 if (data[gt] < value) {
8028                     data[++lt] = data[gt];
8029                 }
8030                 // Move v to the >P side
8031                 data[gt] = v;
8032             }
8033             // else v == value and is in the central region to fill at the end
8034         }
8035 
8036         // Equal in (lt, gt) so adjust to [lt, gt]
8037         ++lt;
8038         upper[0] = --gt;
8039 
8040         // Fill the equal values gap
8041         for (int i = lt; i <= gt; i++) {
8042             data[i] = value;
8043         }
8044 
8045         return lt;
8046     }
8047 
8048     /**
8049      * Partition an array slice around a pivot. Partitioning exchanges array elements such
8050      * that all elements smaller than pivot are before it and all elements larger than
8051      * pivot are after it.
8052      *
8053      * <p>Uses a Dutch-National-Flag method handling equal keys (version 3).
8054      *
8055      * @param data Data array.
8056      * @param left Lower bound (inclusive).
8057      * @param right Upper bound (inclusive).
8058      * @param pivot Pivot index.
8059      * @param upper Upper bound (inclusive) of the pivot range.
8060      * @return Lower bound (inclusive) of the pivot range.
8061      */
8062     private static int partitionDNF3(double[] data, int left, int right, int pivot, int[] upper) {
8063         // Dutch National Flag partitioning:
8064         // https://www.baeldung.com/java-sorting-arrays-with-repeated-entries
8065         // https://en.wikipedia.org/wiki/Dutch_national_flag_problem
8066 
8067         // Partition data using pivot P into less-than, greater-than or equal.
8068         // i traverses the unknown region ??? and values moved to the correct end.
8069         //
8070         // left    lt      i           gt       right
8071         // |  < P  |   P   |     ???    |     > P   |
8072         //
8073         // This version writes in the value of P as it traverses. Any subsequent
8074         // less-than values will overwrite P values trailing behind i.
8075 
8076         final double value = data[pivot];
8077 
8078         // Fast-forward initial less-than region
8079         int lt = left;
8080         while (data[lt] < value) {
8081             lt++;
8082         }
8083 
8084         // Pointers positioned to use pre-increment/decrement: ++x / --x
8085         lt--;
8086         int gt = right + 1;
8087 
8088         // Note:
8089         // This benchmarks as faster than DNF1 and equal to DNF2 on random data.
8090         // On data with (many) repeat values it is faster than DNF2.
8091         // Both DNF2 & 3 have fast-forward of the gt pointer.
8092 
8093         // Modified DNF partitioning with fast-forward of the greater-than
8094         // pointer. Here we write in the pivot value at i during the sweep.
8095         // This acts as a sentinel when fast-forwarding greater-than.
8096         // It is over-written by any future <P value.
8097         // [begin, lt] < pivot
8098         // (lt, i)    == pivot
8099         // [i, gt)    == ???
8100         // [gt, end)   > pivot
8101         for (int i = lt; ++i < gt;) {
8102             final double v = data[i];
8103             if (v != value) {
8104                 // Overwrite with the pivot value
8105                 data[i] = value;
8106                 if (v < value) {
8107                     // Move v to the <P side
8108                     data[++lt] = v;
8109                 } else {
8110                     // Fast-forward here cannot pass sentinel
8111                     // while (data[--gt] > value)
8112                     do {
8113                         --gt;
8114                     } while (data[gt] > value);
8115                     // Now data[gt] <= value
8116                     // if data[gt] == value we can skip over it
8117                     if (data[gt] < value) {
8118                         data[++lt] = data[gt];
8119                     }
8120                     // Move v to the >P side
8121                     data[gt] = v;
8122                 }
8123             }
8124         }
8125 
8126         // Equal in (lt, gt) so adjust to [lt, gt]
8127         ++lt;
8128         upper[0] = --gt;
8129 
8130         // In contrast to version 1 and 2 there is no requirement to fill the central
8131         // region with the pivot value as it was filled during the sweep
8132 
8133         return lt;
8134     }
8135 
8136     /**
8137      * Partition an array slice around 2 pivots. Partitioning exchanges array elements
8138      * such that all elements smaller than pivot are before it and all elements larger
8139      * than pivot are after it.
8140      *
8141      * <p>Uses a dual-pivot quicksort method by Vladimir Yaroslavskiy.
8142      *
8143      * <p>This method assumes {@code a[pivot1] <= a[pivot2]}.
8144      * If {@code pivot1 == pivot2} this triggers a switch to a single-pivot method.
8145      * It is assumed this indicates that choosing two pivots failed due to many equal
8146      * values. In this case the single-pivot method uses a Dutch National Flag algorithm
8147      * suitable for many equal values.
8148      *
8149      * <p>This method returns 4 points describing the pivot ranges of equal values.
8150      *
8151      * <pre>{@code
8152      *         |k0  k1|                |k2  k3|
8153      * |   <P  | ==P1 |  <P1 && <P2    | ==P2 |   >P   |
8154      * }</pre>
8155      *
8156      * <ul>
8157      * <li>k0: lower pivot1 point
8158      * <li>k1: upper pivot1 point (inclusive)
8159      * <li>k2: lower pivot2 point
8160      * <li>k3: upper pivot2 point (inclusive)
8161      * </ul>
8162      *
8163      * <p>Bounds are set so {@code i < k0},  {@code i > k3} and {@code k1 < i < k2} are
8164      * unsorted. When the range {@code [k0, k3]} contains fully sorted elements the result
8165      * is set to {@code k1 = k3; k2 == k0}. This can occur if
8166      * {@code P1 == P2} or there are zero or 1 value between the pivots
8167      * {@code P1 < v < P2}. Any sort/partition of ranges [left, k0-1], [k1+1, k2-1] and
8168      * [k3+1, right] must check the length is {@code > 1}.
8169      *
8170      * @param a Data array.
8171      * @param left Lower bound (inclusive).
8172      * @param right Upper bound (inclusive).
8173      * @param bounds Points [k1, k2, k3].
8174      * @param pivot1 Pivot1 location.
8175      * @param pivot2 Pivot2 location.
8176      * @return Lower bound (inclusive) of the pivot range [k0].
8177      */
8178     static int partitionDP(double[] a, int left, int right, int pivot1, int pivot2, int[] bounds) {
8179         // Allow caller to choose a single-pivot
8180         if (pivot1 == pivot2) {
8181             // Switch to a single pivot sort. This is used when there are
8182             // estimated to be many equal values so use the fastest equal
8183             // value single pivot method.
8184             final int lower = partitionDNF3(a, left, right, pivot1, bounds);
8185             // Set dual pivot range
8186             bounds[2] = bounds[0];
8187             // No unsorted internal region (set k1 = k3; k2 = k0)
8188             // Note: It is extra work for the caller to detect that this region can be skipped.
8189             bounds[1] = lower;
8190             return lower;
8191         }
8192 
8193         // Dual-pivot quicksort method by Vladimir Yaroslavskiy.
8194         //
8195         // Partition data using pivots P1 and P2 into less-than, greater-than or between.
8196         // Pivot values P1 & P2 are placed at the end. If P1 < P2, P2 acts as a sentinel.
8197         // k traverses the unknown region ??? and values moved if less-than (lt) or
8198         // greater-than (gt):
8199         //
8200         // left        lt                k           gt        right
8201         // |P1|  <P1   |   P1 <= & <= P2 |    ???    |    >P2   |P2|
8202         //
8203         // <P1           (left, lt)
8204         // P1<= & <= P2  [lt, k)
8205         // >P2           (gt, right)
8206         //
8207         // At the end pivots are swapped back to behind the lt and gt pointers.
8208         //
8209         // |  <P1        |P1|     P1<= & <= P2    |P2|      >P2    |
8210         //
8211         // Adapted from Yaroslavskiy
8212         // http://codeblab.com/wp-content/uploads/2009/09/DualPivotQuicksort.pdf
8213         //
8214         // Modified to allow partial sorting (partitioning):
8215         // - Allow the caller to supply the pivot indices
8216         // - Ignore insertion sort for tiny array (handled by calling code)
8217         // - Ignore recursive calls for a full sort (handled by calling code)
8218         // - Change to fast-forward over initial ascending / descending runs
8219         // - Change to a single-pivot partition method if the pivots are equal
8220         // - Change to fast-forward great when v > v2 and either break the sorting
8221         //   loop, or move a[great] direct to the correct location.
8222         // - Change to remove the 'div' parameter used to control the pivot selection
8223         //   using the medians method (div initialises as 3 for 1/3 and 2/3 and increments
8224         //   when the central region is too large).
8225         // - Identify a large central region using ~5/8 of the length.
8226 
8227         final double v1 = a[pivot1];
8228         final double v2 = a[pivot2];
8229 
8230         // Swap ends to the pivot locations.
8231         a[pivot1] = a[left];
8232         a[pivot2] = a[right];
8233         a[left] = v1;
8234         a[right] = v2;
8235 
8236         // pointers
8237         int less = left;
8238         int great = right;
8239 
8240         // Fast-forward ascending / descending runs to reduce swaps.
8241         // Cannot overrun as end pivots (v1 <= v2) act as sentinels.
8242         do {
8243             ++less;
8244         } while (a[less] < v1);
8245         do {
8246             --great;
8247         } while (a[great] > v2);
8248 
8249         // a[less - 1] < P1 : a[great + 1] > P2
8250         // unvisited in [less, great]
8251         SORTING:
8252         for (int k = less - 1; ++k <= great;) {
8253             final double v = a[k];
8254             if (v < v1) {
8255                 // swap(a, k, less++)
8256                 a[k] = a[less];
8257                 a[less] = v;
8258                 less++;
8259             } else if (v > v2) {
8260                 // while k < great and a[great] > v2:
8261                 //   great--
8262                 while (a[great] > v2) {
8263                     if (great-- == k) {
8264                         // Done
8265                         break SORTING;
8266                     }
8267                 }
8268                 // swap(a, k, great--)
8269                 // if a[k] < v1:
8270                 //   swap(a, k, less++)
8271                 final double w = a[great];
8272                 a[great] = v;
8273                 great--;
8274                 // delay a[k] = w
8275                 if (w < v1) {
8276                     a[k] = a[less];
8277                     a[less] = w;
8278                     less++;
8279                 } else {
8280                     a[k] = w;
8281                 }
8282             }
8283         }
8284 
8285         // Change to inclusive ends : a[less] < P1 : a[great] > P2
8286         less--;
8287         great++;
8288         // Move the pivots to correct locations
8289         a[left] = a[less];
8290         a[less] = v1;
8291         a[right] = a[great];
8292         a[great] = v2;
8293 
8294         // Record the pivot locations
8295         final int lower = less;
8296         bounds[2] = great;
8297 
8298         // equal elements
8299         // Original paper: If middle partition is bigger than a threshold
8300         // then check for equal elements.
8301 
8302         // Note: This is extra work. When performing partitioning the region of interest
8303         // may be entirely above or below the central region and this could be skipped.
8304         // Versions that do this are not measurably faster. Skipping this may be faster
8305         // if this step can be skipped on the initial largest region. The 5/8 size occurs
8306         // approximately ~7% of the time on random data (verified using collated statistics).
8307 
8308         // Here we look for equal elements if the centre is more than 5/8 the length.
8309         // 5/8 = 1/2 + 1/8. Pivots must be different.
8310         if ((great - less) > ((right - left) >>> 1) + ((right - left) >>> 3) && v1 != v2) {
8311 
8312             // Fast-forward to reduce swaps. Changes inclusive ends to exclusive ends.
8313             // Since v1 != v2 these act as sentinels to prevent overrun.
8314             do {
8315                 ++less;
8316             } while (a[less] == v1);
8317             do {
8318                 --great;
8319             } while (a[great] == v2);
8320 
8321             // This copies the logic in the sorting loop using == comparisons
8322             EQUAL:
8323             for (int k = less - 1; ++k <= great;) {
8324                 final double v = a[k];
8325                 if (v == v1) {
8326                     a[k] = a[less];
8327                     a[less] = v;
8328                     less++;
8329                 } else if (v == v2) {
8330                     while (a[great] == v2) {
8331                         if (great-- == k) {
8332                             // Done
8333                             break EQUAL;
8334                         }
8335                     }
8336                     final double w = a[great];
8337                     a[great] = v;
8338                     great--;
8339                     if (w == v1) {
8340                         a[k] = a[less];
8341                         a[less] = w;
8342                         less++;
8343                     } else {
8344                         a[k] = w;
8345                     }
8346                 }
8347             }
8348 
8349             // Change to inclusive ends
8350             less--;
8351             great++;
8352         }
8353 
8354         // Between pivots in (less, great)
8355         if (v1 < v2 && less < great - 1) {
8356             // Record the pivot end points
8357             bounds[0] = less;
8358             bounds[1] = great;
8359         } else {
8360             // No unsorted internal region (set k1 = k3; k2 = k0)
8361             bounds[0] = bounds[2];
8362             bounds[1] = lower;
8363         }
8364 
8365         return lower;
8366     }
8367 
8368     /**
8369      * Expand a partition around a single pivot. Partitioning exchanges array
8370      * elements such that all elements smaller than pivot are before it and all
8371      * elements larger than pivot are after it. The central region is already
8372      * partitioned.
8373      *
8374      * <pre>{@code
8375      * |l             |s   |p0 p1|   e|                r|
8376      * |    ???       | <P | ==P | >P |        ???      |
8377      * }</pre>
8378      *
8379      * <p>This method requires that {@code left < start && end < right}. It supports
8380      * {@code start == end}.
8381      *
8382      * @param a Data array.
8383      * @param left Lower bound (inclusive).
8384      * @param right Upper bound (inclusive).
8385      * @param start Start of the partition range (inclusive).
8386      * @param end End of the partitioned range (inclusive).
8387      * @param pivot0 Lower pivot location (inclusive).
8388      * @param pivot1 Upper pivot location (inclusive).
8389      * @param upper Upper bound (inclusive) of the pivot range [k1].
8390      * @return Lower bound (inclusive) of the pivot range [k0].
8391      */
8392     private static int expandPartitionT1(double[] a, int left, int right, int start, int end,
8393         int pivot0, int pivot1, int[] upper) {
8394         // 3-way partition of the data using a pivot value into
8395         // less-than, equal or greater-than.
8396         // Based on Sedgewick's Bentley-McIroy partitioning: always swap i<->j then
8397         // check for equal to the pivot and move again.
8398         //
8399         // Move sentinels from start and end to left and right. Scan towards the
8400         // sentinels until >=,<=. Swap then move == to the pivot region.
8401         //           <-i                           j->
8402         // |l |        |            |p0  p1|       |             | r|
8403         // |>=|   ???  |     <      |  ==  |   >   |     ???     |<=|
8404         //
8405         // When either i or j reach the edge perform finishing loop.
8406         // Finish loop for a[j] <= v replaces j with p1+1, moves value
8407         // to p0 for < and updates the pivot range p1 (and optionally p0):
8408         //                                             j->
8409         // |l                       |p0  p1|           |         | r|
8410         // |         <              |  ==  |       >   |   ???   |<=|
8411 
8412         // Positioned for pre-in/decrement to write to pivot region
8413         int p0 = pivot0;
8414         int p1 = pivot1;
8415         final double v = a[p0];
8416         if (a[left] < v) {
8417             // a[left] is not a sentinel
8418             final double w = a[left];
8419             if (a[right] > v) {
8420                 // Most likely case: ends can be sentinels
8421                 a[left] = a[right];
8422                 a[right] = w;
8423             } else {
8424                 // a[right] is a sentinel; use pivot for left
8425                 a[left] = v;
8426                 a[p0] = w;
8427                 p0++;
8428             }
8429         } else if (a[right] > v) {
8430             // a[right] is not a sentinel; use pivot
8431             a[p1] = a[right];
8432             p1--;
8433             a[right] = v;
8434         }
8435 
8436         // Required to avoid index bound error first use of i/j
8437         assert left < start && end < right;
8438         int i = start;
8439         int j = end;
8440         while (true) {
8441             do {
8442                 --i;
8443             } while (a[i] < v);
8444             do {
8445                 ++j;
8446             } while (a[j] > v);
8447             final double vj = a[i];
8448             final double vi = a[j];
8449             a[i] = vi;
8450             a[j] = vj;
8451             // Move the equal values to pivot region
8452             if (vi == v) {
8453                 a[i] = a[--p0];
8454                 a[p0] = v;
8455             }
8456             if (vj == v) {
8457                 a[j] = a[++p1];
8458                 a[p1] = v;
8459             }
8460             // Termination check and finishing loops.
8461             // Note: this works even if pivot region is zero length (p1 == p0-1)
8462             // due to pivot use as a sentinel on one side because we pre-inc/decrement
8463             // one side and post-inc/decrement the other side.
8464             if (i == left) {
8465                 while (j < right) {
8466                     do {
8467                         ++j;
8468                     } while (a[j] > v);
8469                     final double w = a[j];
8470                     // Move upper bound of pivot region
8471                     a[j] = a[++p1];
8472                     a[p1] = v;
8473                     if (w != v) {
8474                         // Move lower bound of pivot region
8475                         a[p0] = w;
8476                         p0++;
8477                     }
8478                 }
8479                 break;
8480             }
8481             if (j == right) {
8482                 while (i > left) {
8483                     do {
8484                         --i;
8485                     } while (a[i] < v);
8486                     final double w = a[i];
8487                     // Move lower bound of pivot region
8488                     a[i] = a[--p0];
8489                     a[p0] = v;
8490                     if (w != v) {
8491                         // Move upper bound of pivot region
8492                         a[p1] = w;
8493                         p1--;
8494                     }
8495                 }
8496                 break;
8497             }
8498         }
8499 
8500         upper[0] = p1;
8501         return p0;
8502     }
8503 
8504     /**
8505      * Expand a partition around a single pivot. Partitioning exchanges array
8506      * elements such that all elements smaller than pivot are before it and all
8507      * elements larger than pivot are after it. The central region is already
8508      * partitioned.
8509      *
8510      * <pre>{@code
8511      * |l             |s   |p0 p1|   e|                r|
8512      * |    ???       | <P | ==P | >P |        ???      |
8513      * }</pre>
8514      *
8515      * <p>This is similar to {@link #expandPartitionT1(double[], int, int, int, int, int, int, int[])}
8516      * with a change to binary partitioning. It requires that {@code left < start && end < right}.
8517      * It supports {@code start == end}.
8518      *
8519      * @param a Data array.
8520      * @param left Lower bound (inclusive).
8521      * @param right Upper bound (inclusive).
8522      * @param start Start of the partition range (inclusive).
8523      * @param end End of the partitioned range (inclusive).
8524      * @param pivot0 Lower pivot location (inclusive).
8525      * @param pivot1 Upper pivot location (inclusive).
8526      * @param upper Upper bound (inclusive) of the pivot range [k1].
8527      * @return Lower bound (inclusive) of the pivot range [k0].
8528      */
8529     private static int expandPartitionB1(double[] a, int left, int right, int start, int end,
8530         int pivot0, int pivot1, int[] upper) {
8531         // 2-way partition of the data using a pivot value into
8532         // less-than, or greater-than.
8533         //
8534         // Move sentinels from start and end to left and right. Scan towards the
8535         // sentinels until >=,<= then swap.
8536         //           <-i                           j->
8537         // |l |        |              | p|         |             | r|
8538         // |>=|   ???  |     <        |==|     >   |     ???     |<=|
8539         //
8540         // When either i or j reach the edge perform finishing loop.
8541         // Finish loop for a[j] <= v replaces j with p1+1, moves value to p
8542         // and moves the pivot up:
8543         //                                            j->
8544         // |l                         | p|            |         | r|
8545         // |         <                |==|        >   |   ???   |<=|
8546 
8547         // Pivot may be moved to use as a sentinel
8548         int p = pivot0;
8549         final double v = a[p];
8550         if (a[left] < v) {
8551             // a[left] is not a sentinel
8552             final double w = a[left];
8553             if (a[right] > v) {
8554                 // Most likely case: ends can be sentinels
8555                 a[left] = a[right];
8556                 a[right] = w;
8557             } else {
8558                 // a[right] is a sentinel; use pivot for left
8559                 a[left] = v;
8560                 a[p] = w;
8561                 p++;
8562             }
8563         } else if (a[right] > v) {
8564             // a[right] is not a sentinel; use pivot
8565             a[p] = a[right];
8566             p--;
8567             a[right] = v;
8568         }
8569 
8570         // Required to avoid index bound error first use of i/j
8571         assert left < start && end < right;
8572         int i = start;
8573         int j = end;
8574         while (true) {
8575             do {
8576                 --i;
8577             } while (a[i] < v);
8578             do {
8579                 ++j;
8580             } while (a[j] > v);
8581             final double vj = a[i];
8582             final double vi = a[j];
8583             a[i] = vi;
8584             a[j] = vj;
8585             // Termination check and finishing loops.
8586             // These reset the pivot if it was moved then slide it as required.
8587             if (i == left) {
8588                 // Reset the pivot and sentinel
8589                 if (p < pivot0) {
8590                     // Pivot is in right; a[p] <= v
8591                     a[right] = a[p];
8592                     a[p] = v;
8593                 } else if (p > pivot0) {
8594                     // Pivot was in left (now swapped to j); a[p] >= v
8595                     a[j] = a[p];
8596                     a[p] = v;
8597                 }
8598                 if (j == right) {
8599                     break;
8600                 }
8601                 while (j < right) {
8602                     do {
8603                         ++j;
8604                     } while (a[j] > v);
8605                     // Move pivot
8606                     a[p] = a[j];
8607                     a[j] = a[++p];
8608                     a[p] = v;
8609                 }
8610                 break;
8611             }
8612             if (j == right) {
8613                 // Reset the pivot and sentinel
8614                 if (p < pivot0) {
8615                     // Pivot was in right (now swapped to i); a[p] <= v
8616                     a[i] = a[p];
8617                     a[p] = v;
8618                 } else if (p > pivot0) {
8619                     // Pivot is in left; a[p] >= v
8620                     a[left] = a[p];
8621                     a[p] = v;
8622                 }
8623                 if (i == left) {
8624                     break;
8625                 }
8626                 while (i > left) {
8627                     do {
8628                         --i;
8629                     } while (a[i] < v);
8630                     // Move pivot
8631                     a[p] = a[i];
8632                     a[i] = a[--p];
8633                     a[p] = v;
8634                 }
8635                 break;
8636             }
8637         }
8638 
8639         upper[0] = p;
8640         return p;
8641     }
8642 
8643     /**
8644      * Expand a partition around a single pivot. Partitioning exchanges array
8645      * elements such that all elements smaller than pivot are before it and all
8646      * elements larger than pivot are after it. The central region is already
8647      * partitioned.
8648      *
8649      * <pre>{@code
8650      * |l             |s   |p0 p1|   e|                r|
8651      * |    ???       | <P | ==P | >P |        ???      |
8652      * }</pre>
8653      *
8654      * <p>This is similar to {@link #expandPartitionT1(double[], int, int, int, int, int, int, int[])}
8655      * with a change to how the end-point sentinels are created. It does not use the pivot
8656      * but uses values at start and end. This increases the length of the lower/upper ranges
8657      * by 1 for the main scan. It requires that {@code start != end}. However it handles
8658      * {@code left == start} and/or {@code end == right}.
8659      *
8660      * @param a Data array.
8661      * @param left Lower bound (inclusive).
8662      * @param right Upper bound (inclusive).
8663      * @param start Start of the partition range (inclusive).
8664      * @param end End of the partitioned range (inclusive).
8665      * @param pivot0 Lower pivot location (inclusive).
8666      * @param pivot1 Upper pivot location (inclusive).
8667      * @param upper Upper bound (inclusive) of the pivot range [k1].
8668      * @return Lower bound (inclusive) of the pivot range [k0].
8669      */
8670     private static int expandPartitionT2(double[] a, int left, int right, int start, int end,
8671         int pivot0, int pivot1, int[] upper) {
8672         // 3-way partition of the data using a pivot value into
8673         // less-than, equal or greater-than.
8674         // Based on Sedgewick's Bentley-McIroy partitioning: always swap i<->j then
8675         // check for equal to the pivot and move again.
8676         //
8677         // Move sentinels from start and end to left and right. Scan towards the
8678         // sentinels until >=,<=. Swap then move == to the pivot region.
8679         //           <-i                           j->
8680         // |l |        |            |p0  p1|       |             | r|
8681         // |>=|   ???  |     <      |  ==  |   >   |     ???     |<=|
8682         //
8683         // When either i or j reach the edge perform finishing loop.
8684         // Finish loop for a[j] <= v replaces j with p1+1, optionally moves value
8685         // to p0 for < and updates the pivot range p1 (and optionally p0):
8686         //                                             j->
8687         // |l                       |p0  p1|           |         | r|
8688         // |         <              |  ==  |       >   |   ???   |<=|
8689 
8690         final double v = a[pivot0];
8691         // Use start/end as sentinels.
8692         // This requires start != end
8693         assert start != end;
8694         double vi = a[start];
8695         double vj = a[end];
8696         a[start] = a[left];
8697         a[end] = a[right];
8698         a[left] = vj;
8699         a[right] = vi;
8700 
8701         int i = start + 1;
8702         int j = end - 1;
8703 
8704         // Positioned for pre-in/decrement to write to pivot region
8705         int p0 = pivot0 == start ? i : pivot0;
8706         int p1 = pivot1 == end ? j : pivot1;
8707 
8708         while (true) {
8709             do {
8710                 --i;
8711             } while (a[i] < v);
8712             do {
8713                 ++j;
8714             } while (a[j] > v);
8715             vj = a[i];
8716             vi = a[j];
8717             a[i] = vi;
8718             a[j] = vj;
8719             // Move the equal values to pivot region
8720             if (vi == v) {
8721                 a[i] = a[--p0];
8722                 a[p0] = v;
8723             }
8724             if (vj == v) {
8725                 a[j] = a[++p1];
8726                 a[p1] = v;
8727             }
8728             // Termination check and finishing loops.
8729             // Note: this works even if pivot region is zero length (p1 == p0-1
8730             // due to single length pivot region at either start/end) because we
8731             // pre-inc/decrement one side and post-inc/decrement the other side.
8732             if (i == left) {
8733                 while (j < right) {
8734                     do {
8735                         ++j;
8736                     } while (a[j] > v);
8737                     final double w = a[j];
8738                     // Move upper bound of pivot region
8739                     a[j] = a[++p1];
8740                     a[p1] = v;
8741                     // Move lower bound of pivot region
8742                     //p0 += w != v ? 1 : 0;
8743                     if (w != v) {
8744                         a[p0] = w;
8745                         p0++;
8746                     }
8747                 }
8748                 break;
8749             }
8750             if (j == right) {
8751                 while (i > left) {
8752                     do {
8753                         --i;
8754                     } while (a[i] < v);
8755                     final double w = a[i];
8756                     // Move lower bound of pivot region
8757                     a[i] = a[--p0];
8758                     a[p0] = v;
8759                     // Move upper bound of pivot region
8760                     //p1 -= w != v ? 1 : 0;
8761                     if (w != v) {
8762                         a[p1] = w;
8763                         p1--;
8764                     }
8765                 }
8766                 break;
8767             }
8768         }
8769 
8770         upper[0] = p1;
8771         return p0;
8772     }
8773 
8774     /**
8775      * Expand a partition around a single pivot. Partitioning exchanges array
8776      * elements such that all elements smaller than pivot are before it and all
8777      * elements larger than pivot are after it. The central region is already
8778      * partitioned.
8779      *
8780      * <pre>{@code
8781      * |l             |s   |p0 p1|   e|                r|
8782      * |    ???       | <P | ==P | >P |        ???      |
8783      * }</pre>
8784      *
8785      * <p>This is similar to {@link #expandPartitionT2(double[], int, int, int, int, int, int, int[])}
8786      * with a change to binary partitioning. It is simpler than
8787      * {@link #expandPartitionB1(double[], int, int, int, int, int, int, int[])} as the pivot is
8788      * not moved. It requires that {@code start != end}. However it handles
8789      * {@code left == start} and/or {@code end == right}.
8790      *
8791      * @param a Data array.
8792      * @param left Lower bound (inclusive).
8793      * @param right Upper bound (inclusive).
8794      * @param start Start of the partition range (inclusive).
8795      * @param end End of the partitioned range (inclusive).
8796      * @param pivot0 Lower pivot location (inclusive).
8797      * @param pivot1 Upper pivot location (inclusive).
8798      * @param upper Upper bound (inclusive) of the pivot range [k1].
8799      * @return Lower bound (inclusive) of the pivot range [k0].
8800      */
8801     private static int expandPartitionB2(double[] a, int left, int right, int start, int end,
8802         int pivot0, int pivot1, int[] upper) {
8803         // 2-way partition of the data using a pivot value into
8804         // less-than, or greater-than.
8805         //
8806         // Move sentinels from start and end to left and right. Scan towards the
8807         // sentinels until >=,<= then swap.
8808         //           <-i                           j->
8809         // |l |        |              | p|         |             | r|
8810         // |>=|   ???  |     <        |==|     >   |     ???     |<=|
8811         //
8812         // When either i or j reach the edge perform finishing loop.
8813         // Finish loop for a[j] <= v replaces j with p1+1, moves value to p
8814         // and moves the pivot up:
8815         //                                            j->
8816         // |l                         | p|            |         | r|
8817         // |         <                |==|        >   |   ???   |<=|
8818 
8819         // Pivot
8820         int p = pivot0;
8821         final double v = a[p];
8822         // Use start/end as sentinels.
8823         // This requires start != end
8824         assert start != end;
8825         // Note: Must not move pivot as this invalidates the finishing loops.
8826         // See logic in method B1 to see added complexity of pivot location.
8827         // This method is not better than T2 for data with no repeat elements
8828         // and is slower for repeat elements when used with the improved
8829         // versions (e.g. linearBFPRTImproved). So for this edge case just use B1.
8830         if (p == start || p == end) {
8831             return expandPartitionB1(a, left, right, start, end, pivot0, pivot1, upper);
8832         }
8833         double vi = a[start];
8834         double vj = a[end];
8835         a[start] = a[left];
8836         a[end] = a[right];
8837         a[left] = vj;
8838         a[right] = vi;
8839 
8840         int i = start + 1;
8841         int j = end - 1;
8842         while (true) {
8843             do {
8844                 --i;
8845             } while (a[i] < v);
8846             do {
8847                 ++j;
8848             } while (a[j] > v);
8849             vj = a[i];
8850             vi = a[j];
8851             a[i] = vi;
8852             a[j] = vj;
8853             // Termination check and finishing loops
8854             if (i == left) {
8855                 while (j < right) {
8856                     do {
8857                         ++j;
8858                     } while (a[j] > v);
8859                     // Move pivot
8860                     a[p] = a[j];
8861                     a[j] = a[++p];
8862                     a[p] = v;
8863                 }
8864                 break;
8865             }
8866             if (j == right) {
8867                 while (i > left) {
8868                     do {
8869                         --i;
8870                     } while (a[i] < v);
8871                     // Move pivot
8872                     a[p] = a[i];
8873                     a[i] = a[--p];
8874                     a[p] = v;
8875                 }
8876                 break;
8877             }
8878         }
8879 
8880         upper[0] = p;
8881         return p;
8882     }
8883 
8884     /**
8885      * Partition an array slice around a pivot. Partitioning exchanges array elements such
8886      * that all elements smaller than pivot are before it and all elements larger than
8887      * pivot are after it.
8888      *
8889      * <p>The index {@code k} is the target element. This method ignores this value.
8890      * The value is included to match the method signature of the {@link SPEPartition} interface.
8891      * Assumes the range {@code r - l >= 4}; the caller is responsible for selection on a smaller
8892      * range.
8893      *
8894      * <p>Uses the Blum, Floyd, Pratt, Rivest, and Tarjan (BFPRT) median-of-medians algorithm
8895      * with medians of 5 with the sample medians computed in the first quintile.
8896      *
8897      * @param a Data array.
8898      * @param l Lower bound (inclusive).
8899      * @param r Upper bound (inclusive).
8900      * @param k Target index.
8901      * @param upper Upper bound (inclusive) of the pivot range.
8902      * @return Lower bound (inclusive) of the pivot range.
8903      */
8904     private int linearBFPRTBaseline(double[] a, int l, int r, int k, int[] upper) {
8905         // Adapted from Alexandrescu (2016), algorithm 3.
8906         // Moves the responsibility for selection when r-l <= 4 to the caller.
8907         // Compute the median of each contiguous set of 5 to the first quintile.
8908         int rr = l - 1;
8909         for (int e = l + 4; e <= r; e += 5) {
8910             Sorting.median5d(a, e - 4, e - 3, e - 2, e - 1, e);
8911             // Median to first quintile
8912             final double v = a[e - 2];
8913             a[e - 2] = a[++rr];
8914             a[rr] = v;
8915         }
8916         final int m = (l + rr + 1) >>> 1;
8917         // mutual recursion
8918         quickSelect(this::linearBFPRTBaseline, a, l, rr, m, m, upper);
8919         // Note: repartions already partitioned data [l, rr]
8920         return spFunction.partition(a, l, r, m, upper);
8921     }
8922 
8923     /**
8924      * Partition an array slice around a pivot. Partitioning exchanges array elements such
8925      * that all elements smaller than pivot are before it and all elements larger than
8926      * pivot are after it.
8927      *
8928      * <p>The index {@code k} is the target element. This method ignores this value.
8929      * The value is included to match the method signature of the {@link SPEPartition} interface.
8930      * Assumes the range {@code r - l >= 8}; the caller is responsible for selection on a smaller
8931      * range.
8932      *
8933      * <p>Uses the Chen and Dumitrescu repeated step median-of-medians-of-medians algorithm
8934      * with medians of 3 with the samples computed in the first tertile and 9th-tile.
8935      *
8936      * @param a Data array.
8937      * @param l Lower bound (inclusive).
8938      * @param r Upper bound (inclusive).
8939      * @param k Target index.
8940      * @param upper Upper bound (inclusive) of the pivot range.
8941      * @return Lower bound (inclusive) of the pivot range.
8942      */
8943     private int linearRepeatedStepBaseline(double[] a, int l, int r, int k, int[] upper) {
8944         // Adapted from Alexandrescu (2016), algorithm 5.
8945         // Moves the responsibility for selection when r-l <= 8 to the caller.
8946         // Compute the median of each contiguous set of 3 to the first tertile, and repeat.
8947         int j = l - 1;
8948         for (int e = l + 2; e <= r; e += 3) {
8949             Sorting.sort3(a, e - 2, e - 1, e);
8950             // Median to first tertile
8951             final double v = a[e - 1];
8952             a[e - 1] = a[++j];
8953             a[j] = v;
8954         }
8955         int rr = l - 1;
8956         for (int e = l + 2; e <= j; e += 3) {
8957             Sorting.sort3(a, e - 2, e - 1, e);
8958             // Median to first 9th-tile
8959             final double v = a[e - 1];
8960             a[e - 1] = a[++rr];
8961             a[rr] = v;
8962         }
8963         final int m = (l + rr + 1) >>> 1;
8964         // mutual recursion
8965         quickSelect(this::linearRepeatedStepBaseline, a, l, rr, m, m, upper);
8966         // Note: repartions already partitioned data [l, rr]
8967         return spFunction.partition(a, l, r, m, upper);
8968     }
8969 
8970     /**
8971      * Partition an array slice around a pivot. Partitioning exchanges array elements such
8972      * that all elements smaller than pivot are before it and all elements larger than
8973      * pivot are after it.
8974      *
8975      * <p>The index {@code k} is the target element. This method ignores this value.
8976      * The value is included to match the method signature of the {@link SPEPartition} interface.
8977      * Assumes the range {@code r - l >= 4}; the caller is responsible for selection on a smaller
8978      * range.
8979      *
8980      * <p>Uses the Blum, Floyd, Pratt, Rivest, and Tarjan (BFPRT) median-of-medians algorithm
8981      * with medians of 5 with the sample medians computed in the central quintile.
8982      *
8983      * @param a Data array.
8984      * @param l Lower bound (inclusive).
8985      * @param r Upper bound (inclusive).
8986      * @param k Target index.
8987      * @param upper Upper bound (inclusive) of the pivot range.
8988      * @return Lower bound (inclusive) of the pivot range.
8989      */
8990     private int linearBFPRTImproved(double[] a, int l, int r, int k, int[] upper) {
8991         // Adapted from Alexandrescu (2016), algorithm 6.
8992         // Moves the responsibility for selection when r-l <= 4 to the caller.
8993         // Compute the median of each non-contiguous set of 5 to the middle quintile.
8994         final int f = (r - l + 1) / 5;
8995         final int f3 = 3 * f;
8996         // middle quintile: [2f:3f)
8997         final int s = l + (f << 1);
8998         final int e = s + f - 1;
8999         for (int i = l, j = s; i < s; i += 2, j++) {
9000             Sorting.median5d(a, i, i + 1, j, f3 + i, f3 + i + 1);
9001         }
9002         // Adaption to target kf/|A|
9003         //final int p = s + mapDistance(k - l, l, r, f);
9004         final int p = s + noSamplingAdapt.mapDistance(k - l, l, r, f);
9005         // mutual recursion
9006         quickSelect(this::linearBFPRTImproved, a, s, e, p, p, upper);
9007         return expandFunction.partition(a, l, r, s, e, upper[0], upper[1], upper);
9008     }
9009 
9010     /**
9011      * Partition an array slice around a pivot. Partitioning exchanges array elements such
9012      * that all elements smaller than pivot are before it and all elements larger than
9013      * pivot are after it.
9014      *
9015      * <p>The index {@code k} is the target element. This method ignores this value.
9016      * The value is included to match the method signature of the {@link SPEPartition} interface.
9017      * Assumes the range {@code r - l >= 8}; the caller is responsible for selection on a smaller
9018      * range.
9019      *
9020      * <p>Uses the Chen and Dumitrescu repeated step median-of-medians-of-medians algorithm
9021      * with medians of 3 with the samples computed in the middle tertile and 9th-tile.
9022      *
9023      * @param a Data array.
9024      * @param l Lower bound (inclusive).
9025      * @param r Upper bound (inclusive).
9026      * @param k Target index.
9027      * @param upper Upper bound (inclusive) of the pivot range.
9028      * @return Lower bound (inclusive) of the pivot range.
9029      */
9030     private int linearRepeatedStepImproved(double[] a, int l, int r, int k, int[] upper) {
9031         // Adapted from Alexandrescu (2016), algorithm 7.
9032         // Moves the responsibility for selection when r-l <= 8 to the caller.
9033         // Compute the median of each non-contiguous set of 3 to the middle tertile, and repeat.
9034         final int f = (r - l + 1) / 9;
9035         final int f3 = 3 * f;
9036         // i in middle tertile [3f:6f)
9037         for (int i = l + f3, e = l + (f3 << 1); i < e; i++) {
9038             Sorting.sort3(a, i - f3, i, i + f3);
9039         }
9040         // i in middle 9th-tile: [4f:5f)
9041         final int s = l + (f << 2);
9042         final int e = s + f - 1;
9043         for (int i = s; i <= e; i++) {
9044             Sorting.sort3(a, i - f, i, i + f);
9045         }
9046         // Adaption to target kf/|A|
9047         //final int p = s + mapDistance(k - l, l, r, f);
9048         final int p = s + noSamplingAdapt.mapDistance(k - l, l, r, f);
9049         // mutual recursion
9050         quickSelect(this::linearRepeatedStepImproved, a, s, e, p, p, upper);
9051         return expandFunction.partition(a, l, r, s, e, upper[0], upper[1], upper);
9052     }
9053 
9054     /**
9055      * Partition an array slice around a pivot. Partitioning exchanges array elements such
9056      * that all elements smaller than pivot are before it and all elements larger than
9057      * pivot are after it.
9058      *
9059      * <p>Assumes the range {@code r - l >= 8}; the caller is responsible for selection on a smaller
9060      * range. If using a 12th-tile for sampling then assumes {@code r - l >= 11}.
9061      *
9062      * <p>Uses the Chen and Dumitrescu repeated step median-of-medians-of-medians algorithm
9063      * with the median of 3 then median of 3; the final sample is placed in the
9064      * 5th 9th-tile; the pivot chosen from the sample is adaptive using the input {@code k}.
9065      *
9066      * @param a Data array.
9067      * @param l Lower bound (inclusive).
9068      * @param r Upper bound (inclusive).
9069      * @param k Target index.
9070      * @param upper Upper bound (inclusive) of the pivot range.
9071      * @param mode Adaption mode.
9072      * @return Lower bound (inclusive) of the pivot range.
9073      */
9074     private int repeatedStep(double[] a, int l, int r, int k, int[] upper, AdaptMode mode) {
9075         // Adapted from Alexandrescu (2016), algorithm 8.
9076         // Moves the responsibility for selection when r-l <= 8 to the caller.
9077         int f;
9078         int s;
9079         int p;
9080         if (!mode.isSampleMode()) {
9081             // i in tertile [3f:6f)
9082             f = (r - l + 1) / 9;
9083             final int f3 = 3 * f;
9084             for (int i = l + f3, end = l + (f3 << 1); i < end; i++) {
9085                 Sorting.sort3(a, i - f3, i, i + f3);
9086             }
9087             // 5th 9th-tile: [4f:5f)
9088             s = l + (f << 2);
9089             p = s + (mode.isAdapt() ? noSamplingAdapt.mapDistance(k - l, l, r, f) : (f >>> 1));
9090         } else {
9091             if ((controlFlags & FLAG_QA_MIDDLE_12) != 0) {
9092                 // Switch to a 12th-tile as used in the other methods.
9093                 f = (r - l + 1) / 12;
9094                 // middle - f/2
9095                 s = ((r + l) >>> 1) - (f >> 1);
9096             } else {
9097                 f = (r - l + 1) / 9;
9098                 s = l + (f << 2);
9099             }
9100             // Adaption to target kf'/|A|
9101             int kp = mode.isAdapt() ? samplingAdapt.mapDistance(k - l, l, r, f) : (f >>> 1);
9102             // Centre the sample at k
9103             if ((controlFlags & FLAG_QA_SAMPLE_K) != 0) {
9104                 s = k - kp;
9105             }
9106             p = s + kp;
9107         }
9108         final int e = s + f - 1;
9109         for (int i = s; i <= e; i++) {
9110             Sorting.sort3(a, i - f, i, i + f);
9111         }
9112         p = quickSelectAdaptive(a, s, e, p, p, upper,
9113             (controlFlags & FLAG_QA_PROPAGATE) != 0 ? mode : adaptMode);
9114         return expandFunction.partition(a, l, r, s, e, p, upper[0], upper);
9115     }
9116 
9117     /**
9118      * Partition an array slice around a pivot. Partitioning exchanges array elements such
9119      * that all elements smaller than pivot are before it and all elements larger than
9120      * pivot are after it.
9121      *
9122      * <p>Assumes the range {@code r - l >= 11}; the caller is responsible for selection on a smaller
9123      * range.
9124      *
9125      * <p>Uses the Chen and Dumitrescu repeated step median-of-medians-of-medians algorithm
9126      * with the lower median of 4 then either median of 3 with the final sample placed in the
9127      * 5th 12th-tile, or min of 3 with the final sample in the 4th 12th-tile;
9128      * the pivot chosen from the sample is adaptive using the input {@code k}.
9129      *
9130      * @param a Data array.
9131      * @param l Lower bound (inclusive).
9132      * @param r Upper bound (inclusive).
9133      * @param k Target index.
9134      * @param upper Upper bound (inclusive) of the pivot range.
9135      * @param mode Adaption mode.
9136      * @param far Set to {@code true} to perform repeatedStepFarLeft.
9137      * @return Lower bound (inclusive) of the pivot range.
9138      */
9139     private int repeatedStepLeft(double[] a, int l, int r, int k, int[] upper, AdaptMode mode,
9140         boolean far) {
9141         // Adapted from Alexandrescu (2016), algorithm 9 and 10.
9142         // Moves the responsibility for selection when r-l <= 11 to the caller.
9143         final int f = (r - l + 1) >> 2;
9144         if (!mode.isSampleMode()) {
9145             // i in 2nd quartile
9146             final int f2 = f + f;
9147             for (int i = l + f, e = l + f2; i < e; i++) {
9148                 Sorting.lowerMedian4(a, i - f, i, i + f, i + f2);
9149             }
9150         }
9151         int fp = f / 3;
9152         int s;
9153         int e;
9154         int p;
9155         if (far) {
9156             // i in 4th 12th-tile
9157             s = l + f;
9158             // Variable adaption
9159             int kp;
9160             if (!mode.isSampleMode()) {
9161                 kp = mode.isAdapt() ? noSamplingEdgeAdapt.mapDistance(k - l, l, r, fp) : fp >>> 1;
9162             } else {
9163                 kp = mode.isAdapt() ? samplingEdgeAdapt.mapDistance(k - l, l, r, fp) : fp >>> 1;
9164                 // Note: Not possible to centre the sample at k on the far step
9165             }
9166             e = s + fp - 1;
9167             p = s + kp;
9168             final int fp2 = fp << 1;
9169             for (int i = s; i <= e; i++) {
9170                 // min into i
9171                 if (a[i] > a[i + fp]) {
9172                     final double u = a[i];
9173                     a[i] = a[i + fp];
9174                     a[i + fp] = u;
9175                 }
9176                 if (a[i] > a[i + fp2]) {
9177                     final double v = a[i];
9178                     a[i] = a[i + fp2];
9179                     a[i + fp2] = v;
9180                 }
9181             }
9182         } else {
9183             // i in 5th 12th-tile
9184             s = l + f + fp;
9185             // Variable adaption
9186             int kp;
9187             if (!mode.isSampleMode()) {
9188                 kp = mode.isAdapt() ? noSamplingAdapt.mapDistance(k - l, l, r, fp) : fp >>> 1;
9189             } else {
9190                 kp = mode.isAdapt() ? samplingAdapt.mapDistance(k - l, l, r, fp) : fp >>> 1;
9191                 // Centre the sample at k
9192                 if ((controlFlags & FLAG_QA_SAMPLE_K) != 0) {
9193                     // Avoid bounds error due to rounding as (k-l)/(r-l) -> 1/12
9194                     s = Math.max(k - kp, l + fp);
9195                 }
9196             }
9197             e = s + fp - 1;
9198             p = s + kp;
9199             for (int i = s; i <= e; i++) {
9200                 Sorting.sort3(a, i - fp, i, i + fp);
9201             }
9202         }
9203         p = quickSelectAdaptive(a, s, e, p, p, upper,
9204             (controlFlags & FLAG_QA_PROPAGATE) != 0 ? mode : adaptMode);
9205         return expandFunction.partition(a, l, r, s, e, p, upper[0], upper);
9206     }
9207 
9208     /**
9209      * Partition an array slice around a pivot. Partitioning exchanges array elements such
9210      * that all elements smaller than pivot are before it and all elements larger than
9211      * pivot are after it.
9212      *
9213      * <p>Assumes the range {@code r - l >= 11}; the caller is responsible for selection on a smaller
9214      * range.
9215      *
9216      * <p>Uses the Chen and Dumitrescu repeated step median-of-medians-of-medians algorithm
9217      * with the upper median of 4 then either median of 3 with the final sample placed in the
9218      * 8th 12th-tile, or max of 3 with the final sample in the 9th 12th-tile;
9219      * the pivot chosen from the sample is adaptive using the input {@code k}.
9220      *
9221      * @param a Data array.
9222      * @param l Lower bound (inclusive).
9223      * @param r Upper bound (inclusive).
9224      * @param k Target index.
9225      * @param upper Upper bound (inclusive) of the pivot range.
9226      * @param mode Adaption mode.
9227      * @param far Set to {@code true} to perform repeatedStepFarRight.
9228      * @return Lower bound (inclusive) of the pivot range.
9229      */
9230     private int repeatedStepRight(double[] a, int l, int r, int k, int[] upper, AdaptMode mode,
9231         boolean far) {
9232         // Mirror image repeatedStepLeft using upper median into 3rd quartile
9233         final int f = (r - l + 1) >> 2;
9234         if (!mode.isSampleMode()) {
9235             // i in 3rd quartile
9236             final int f2 = f + f;
9237             for (int i = r - f, e = r - f2; i > e; i--) {
9238                 Sorting.upperMedian4(a, i - f2, i - f, i, i + f);
9239             }
9240         }
9241         int fp = f / 3;
9242         int s;
9243         int e;
9244         int p;
9245         if (far) {
9246             // i in 9th 12th-tile
9247             e = r - f;
9248             // Variable adaption
9249             int kp;
9250             if (!mode.isSampleMode()) {
9251                 kp = mode.isAdapt() ? noSamplingEdgeAdapt.mapDistance(r - k, l, r, fp) : fp >>> 1;
9252             } else {
9253                 kp = mode.isAdapt() ? samplingEdgeAdapt.mapDistance(r - k, l, r, fp) : fp >>> 1;
9254                 // Note: Not possible to centre the sample at k on the far step
9255             }
9256             s = e - fp + 1;
9257             p = e - kp;
9258             final int fp2 = fp << 1;
9259             for (int i = s; i <= e; i++) {
9260                 // max into i
9261                 if (a[i] < a[i - fp]) {
9262                     final double u = a[i];
9263                     a[i] = a[i - fp];
9264                     a[i - fp] = u;
9265                 }
9266                 if (a[i] < a[i - fp2]) {
9267                     final double v = a[i];
9268                     a[i] = a[i - fp2];
9269                     a[i - fp2] = v;
9270                 }
9271             }
9272         } else {
9273             // i in 8th 12th-tile
9274             e = r - f - fp;
9275             // Variable adaption
9276             int kp;
9277             if (!mode.isSampleMode()) {
9278                 kp = mode.isAdapt() ? noSamplingAdapt.mapDistance(r - k, l, r, fp) : fp >>> 1;
9279             } else {
9280                 kp = mode.isAdapt() ? samplingAdapt.mapDistance(r - k, l, r, fp) : fp >>> 1;
9281                 // Centre the sample at k
9282                 if ((controlFlags & FLAG_QA_SAMPLE_K) != 0) {
9283                     // Avoid bounds error due to rounding as (r-k)/(r-l) -> 11/12
9284                     e = Math.min(k + kp, r - fp);
9285                 }
9286             }
9287             s = e - fp + 1;
9288             p = e - kp;
9289             for (int i = s; i <= e; i++) {
9290                 Sorting.sort3(a, i - fp, i, i + fp);
9291             }
9292         }
9293         p = quickSelectAdaptive(a, s, e, p, p, upper,
9294             (controlFlags & FLAG_QA_PROPAGATE) != 0 ? mode : adaptMode);
9295         return expandFunction.partition(a, l, r, s, e, p, upper[0], upper);
9296     }
9297 
9298     /**
9299      * Partition an array slice around a pivot. Partitioning exchanges array elements such
9300      * that all elements smaller than pivot are before it and all elements larger than
9301      * pivot are after it.
9302      *
9303      * <p>Assumes the range {@code r - l >= 11}; the caller is responsible for selection on a smaller
9304      * range.
9305      *
9306      * <p>Uses the Chen and Dumitrescu repeated step median-of-medians-of-medians algorithm
9307      * with the minimum of 4 then median of 3; the final sample is placed in the
9308      * 2nd 12th-tile; the pivot chosen from the sample is adaptive using the input {@code k}.
9309      *
9310      * <p>Given a pivot in the middle of the sample this has margins of 1/12 and 1/3.
9311      *
9312      * @param a Data array.
9313      * @param l Lower bound (inclusive).
9314      * @param r Upper bound (inclusive).
9315      * @param k Target index.
9316      * @param upper Upper bound (inclusive) of the pivot range.
9317      * @param mode Adaption mode.
9318      * @return Lower bound (inclusive) of the pivot range.
9319      */
9320     private int repeatedStepFarLeft(double[] a, int l, int r, int k, int[] upper, AdaptMode mode) {
9321         // Moves the responsibility for selection when r-l <= 11 to the caller.
9322         final int f = (r - l + 1) >> 2;
9323         int fp = f / 3;
9324         // 2nd 12th-tile
9325         int s = l + fp;
9326         final int e = s + fp - 1;
9327         int p;
9328         if (!mode.isSampleMode()) {
9329             p = s + (mode.isAdapt() ? noSamplingEdgeAdapt.mapDistance(k - l, l, r, fp) : fp >>> 1);
9330             // i in 2nd quartile; min into i-f (1st quartile)
9331             final int f2 = f + f;
9332             for (int i = l + f, end = l + f2; i < end; i++) {
9333                 if (a[i + f] < a[i - f]) {
9334                     final double u = a[i + f];
9335                     a[i + f] = a[i - f];
9336                     a[i - f] = u;
9337                 }
9338                 if (a[i + f2] < a[i]) {
9339                     final double v = a[i + f2];
9340                     a[i + f2] = a[i];
9341                     a[i] = v;
9342                 }
9343                 if (a[i] < a[i - f]) {
9344                     final double u = a[i];
9345                     a[i] = a[i - f];
9346                     a[i - f] = u;
9347                 }
9348             }
9349         } else {
9350             int kp = mode.isAdapt() ? samplingEdgeAdapt.mapDistance(k - l, l, r, fp) : fp >>> 1;
9351             p = s + kp;
9352         }
9353         for (int i = s; i <= e; i++) {
9354             Sorting.sort3(a, i - fp, i, i + fp);
9355         }
9356         p = quickSelectAdaptive(a, s, e, p, p, upper,
9357             (controlFlags & FLAG_QA_PROPAGATE) != 0 ? mode : adaptMode);
9358         return expandFunction.partition(a, l, r, s, e, p, upper[0], upper);
9359     }
9360 
9361     /**
9362      * Partition an array slice around a pivot. Partitioning exchanges array elements such
9363      * that all elements smaller than pivot are before it and all elements larger than
9364      * pivot are after it.
9365      *
9366      * <p>Assumes the range {@code r - l >= 11}; the caller is responsible for selection on a smaller
9367      * range.
9368      *
9369      * <p>Uses the Chen and Dumitrescu repeated step median-of-medians-of-medians algorithm
9370      * with the maximum of 4 then median of 3; the final sample is placed in the
9371      * 11th 12th-tile; the pivot chosen from the sample is adaptive using the input {@code k}.
9372      *
9373      * <p>Given a pivot in the middle of the sample this has margins of 1/3 and 1/12.
9374      *
9375      * @param a Data array.
9376      * @param l Lower bound (inclusive).
9377      * @param r Upper bound (inclusive).
9378      * @param k Target index.
9379      * @param upper Upper bound (inclusive) of the pivot range.
9380      * @param mode Adaption mode.
9381      * @return Lower bound (inclusive) of the pivot range.
9382      */
9383     private int repeatedStepFarRight(double[] a, int l, int r, int k, int[] upper, AdaptMode mode) {
9384         // Mirror image repeatedStepFarLeft
9385         final int f = (r - l + 1) >> 2;
9386         int fp = f / 3;
9387         // 11th 12th-tile
9388         int e = r - fp;
9389         final int s = e - fp + 1;
9390         int p;
9391         if (!mode.isSampleMode()) {
9392             p = e - (mode.isAdapt() ? noSamplingEdgeAdapt.mapDistance(r - k, l, r, fp) : fp >>> 1);
9393             // i in 3rd quartile; max into i+f (4th quartile)
9394             final int f2 = f + f;
9395             for (int i = r - f, end = r - f2; i > end; i--) {
9396                 if (a[i - f] > a[i + f]) {
9397                     final double u = a[i - f];
9398                     a[i - f] = a[i + f];
9399                     a[i + f] = u;
9400                 }
9401                 if (a[i - f2] > a[i]) {
9402                     final double v = a[i - f2];
9403                     a[i - f2] = a[i];
9404                     a[i] = v;
9405                 }
9406                 if (a[i] > a[i + f]) {
9407                     final double u = a[i];
9408                     a[i] = a[i + f];
9409                     a[i + f] = u;
9410                 }
9411             }
9412         } else {
9413             int kp = mode.isAdapt() ? samplingEdgeAdapt.mapDistance(r - k, l, r, fp) : fp >>> 1;
9414             p = e - kp;
9415         }
9416         for (int i = s; i <= e; i++) {
9417             Sorting.sort3(a, i - fp, i, i + fp);
9418         }
9419         p = quickSelectAdaptive(a, s, e, p, p, upper,
9420             (controlFlags & FLAG_QA_PROPAGATE) != 0 ? mode : adaptMode);
9421         return expandFunction.partition(a, l, r, s, e, p, upper[0], upper);
9422     }
9423 
9424     /**
9425      * Partition an array slice around a pivot. Partitioning exchanges array elements such
9426      * that all elements smaller than pivot are before it and all elements larger than
9427      * pivot are after it.
9428      *
9429      * <p>Partitions a Floyd-Rivest sample around a pivot offset so that the input {@code k} will
9430      * fall in the smaller partition when the entire range is partitioned.
9431      *
9432      * <p>Assumes the range {@code r - l} is large; the original Floyd-Rivest size for sampling
9433      * was 600.
9434      *
9435      * @param a Data array.
9436      * @param l Lower bound (inclusive).
9437      * @param r Upper bound (inclusive).
9438      * @param k Target index.
9439      * @param upper Upper bound (inclusive) of the pivot range.
9440      * @param mode Adaption mode.
9441      * @return Lower bound (inclusive) of the pivot range.
9442      */
9443     private int sampleStep(double[] a, int l, int r, int k, int[] upper, AdaptMode mode) {
9444         // Floyd-Rivest: use SELECT recursively on a sample of size S to get an estimate
9445         // for the (k-l+1)-th smallest element into a[k], biased slightly so that the
9446         // (k-l+1)-th element is expected to lie in the smaller set after partitioning.
9447         final int n = r - l + 1;
9448         final int ith = k - l + 1;
9449         final double z = Math.log(n);
9450         // sample size = 0.5 * n^(2/3)
9451         final double s = 0.5 * Math.exp(0.6666666666666666 * z);
9452         final double sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * Integer.signum(ith - (n >> 1));
9453         final int ll = Math.max(l, (int) (k - ith * s / n + sd));
9454         final int rr = Math.min(r, (int) (k + (n - ith) * s / n + sd));
9455         // Optional random sampling.
9456         // Support two variants.
9457         if ((controlFlags & FLAG_QA_RANDOM_SAMPLING) != 0) {
9458             final IntUnaryOperator rng = createRNG(n, k);
9459             if (ll == l) {
9460                 // Shuffle [l, rr] from [l, r]
9461                 for (int i = l - 1; i < rr;) {
9462                     // r - rand [0, r - i] : i is currently i-1
9463                     final int j = r - rng.applyAsInt(r - i);
9464                     final double t = a[++i];
9465                     a[i] = a[j];
9466                     a[j] = t;
9467                 }
9468             } else if (rr == r) {
9469                 // Shuffle [ll, r] from [l, r]
9470                 for (int i = r + 1; i > ll;) {
9471                     // l + rand [0, i - l] : i is currently i+1
9472                     final int j = l + rng.applyAsInt(i - l);
9473                     final double t = a[--i];
9474                     a[i] = a[j];
9475                     a[j] = t;
9476                 }
9477             } else {
9478                 // Sample range [ll, rr] is internal
9479                 // Shuffle [ll, k) from [l, k)
9480                 for (int i = k; i > ll;) {
9481                     // l + rand [0, i - l + 1) : i is currently i+1
9482                     final int j = l + rng.applyAsInt(i - l);
9483                     final double t = a[--i];
9484                     a[i] = a[j];
9485                     a[j] = t;
9486                 }
9487                 // Shuffle (k, rr] from (k, r]
9488                 for (int i = k; i < rr;) {
9489                     // r - rand [0, r - i + 1) : i is currently i-1
9490                     final int j = r - rng.applyAsInt(r - i);
9491                     final double t = a[++i];
9492                     a[i] = a[j];
9493                     a[j] = t;
9494                 }
9495             }
9496         } else if ((controlFlags & FLAG_RANDOM_SAMPLING) != 0) {
9497             final IntUnaryOperator rng = createRNG(n, k);
9498             // Shuffle [ll, k) from [l, k)
9499             if (ll > l) {
9500                 for (int i = k; i > ll;) {
9501                     // l + rand [0, i - l + 1) : i is currently i+1
9502                     final int j = l + rng.applyAsInt(i - l);
9503                     final double t = a[--i];
9504                     a[i] = a[j];
9505                     a[j] = t;
9506                 }
9507             }
9508             // Shuffle (k, rr] from (k, r]
9509             if (rr < r) {
9510                 for (int i = k; i < rr;) {
9511                     // r - rand [0, r - i + 1) : i is currently i-1
9512                     final int j = r - rng.applyAsInt(r - i);
9513                     final double t = a[++i];
9514                     a[i] = a[j];
9515                     a[j] = t;
9516                 }
9517             }
9518         }
9519         // Sample recursion restarts from [ll, rr]
9520         final int p = quickSelectAdaptive(a, ll, rr, k, k, upper,
9521             (controlFlags & FLAG_QA_PROPAGATE) != 0 ? mode : adaptMode);
9522 
9523         // Expect a small sample and repartition the entire range...
9524         // Does not support a pivot range so use the centre
9525         //return spFunction.partition(a, l, r, (p + upper[0]) >>> 1, upper);
9526 
9527         return expandFunction.partition(a, l, r, ll, rr, p, upper[0], upper);
9528     }
9529 
9530     /**
9531      * Map the distance from the edge of {@code [l, r]} to a new distance in {@code [0, n)}.
9532      *
9533      * <p>The provides the adaption {@code kf'/|A|} from Alexandrescu (2016) where
9534      * {@code k == d}, {@code f' == n} and {@code |A| == r-l+1}.
9535      *
9536      * <p>For convenience this accepts the input range {@code [l, r]}.
9537      *
9538      * @param d Distance from the edge in {@code [0, r - l]}.
9539      * @param l Lower bound (inclusive).
9540      * @param r Upper bound (inclusive).
9541      * @param n Size of the new range.
9542      * @return the mapped distance in [0, n)
9543      */
9544     private static int mapDistance(int d, int l, int r, int n) {
9545         return (int) (d * (n - 1.0) / (r - l));
9546     }
9547 
9548     /**
9549      * Partition an array slice around a pivot. Partitioning exchanges array elements such
9550      * that all elements smaller than pivot are before it and all elements larger than
9551      * pivot are after it.
9552      *
9553      * <p>Assumes the range {@code r - l >= 8}; the caller is responsible for selection on a smaller
9554      * range. If using a 12th-tile for sampling then assumes {@code r - l >= 11}.
9555      *
9556      * <p>Uses the Chen and Dumitrescu repeated step median-of-medians-of-medians algorithm
9557      * with the median of 3 then median of 3; the final sample is placed in the
9558      * 5th 9th-tile; the pivot chosen from the sample is adaptive using the input {@code k}.
9559      *
9560      * <p>Given a pivot in the middle of the sample this has margins of 2/9 and 2/9.
9561      *
9562      * @param a Data array.
9563      * @param l Lower bound (inclusive).
9564      * @param r Upper bound (inclusive).
9565      * @param k Target index.
9566      * @param upper Upper bound (inclusive) of the pivot range.
9567      * @param flags Control flags.
9568      * @return Lower bound (inclusive) of the pivot range.
9569      */
9570     private static int repeatedStep(double[] a, int l, int r, int k, int[] upper, int flags) {
9571         // Adapted from Alexandrescu (2016), algorithm 8.
9572         int fp;
9573         int s;
9574         int p;
9575         if (flags <= MODE_SAMPLING) {
9576             // Median into a 12th-tile
9577             fp = (r - l + 1) / 12;
9578             // Position the sample around the target k
9579             s = k - mapDistance(k - l, l, r, fp);
9580             p = k;
9581         } else {
9582             // i in tertile [3f':6f')
9583             fp = (r - l + 1) / 9;
9584             final int f3 = 3 * fp;
9585             for (int i = l + f3, end = l + (f3 << 1); i < end; i++) {
9586                 Sorting.sort3(a, i - f3, i, i + f3);
9587             }
9588             // 5th 9th-tile: [4f':5f')
9589             s = l + (fp << 2);
9590             // No adaption uses the middle to enforce strict margins
9591             p = s + (flags == MODE_ADAPTION ? mapDistance(k - l, l, r, fp) : (fp >>> 1));
9592         }
9593         final int e = s + fp - 1;
9594         for (int i = s; i <= e; i++) {
9595             Sorting.sort3(a, i - fp, i, i + fp);
9596         }
9597         p = quickSelectAdaptive2(a, s, e, p, p, upper, qaMode);
9598         return expandPartitionT2(a, l, r, s, e, p, upper[0], upper);
9599     }
9600 
9601     /**
9602      * Partition an array slice around a pivot. Partitioning exchanges array elements such
9603      * that all elements smaller than pivot are before it and all elements larger than
9604      * pivot are after it.
9605      *
9606      * <p>Assumes the range {@code r - l >= 11}; the caller is responsible for selection on a smaller
9607      * range.
9608      *
9609      * <p>Uses the Chen and Dumitrescu repeated step median-of-medians-of-medians algorithm
9610      * with the lower median of 4 then either median of 3 with the final sample placed in the
9611      * 5th 12th-tile, or min of 3 with the final sample in the 4th 12th-tile;
9612      * the pivot chosen from the sample is adaptive using the input {@code k}.
9613      *
9614      * <p>Given a pivot in the middle of the sample this has margins of 1/6 and 1/4.
9615      *
9616      * @param a Data array.
9617      * @param l Lower bound (inclusive).
9618      * @param r Upper bound (inclusive).
9619      * @param k Target index.
9620      * @param upper Upper bound (inclusive) of the pivot range.
9621      * @param flags Control flags.
9622      * @return Lower bound (inclusive) of the pivot range.
9623      */
9624     private static int repeatedStepLeft(double[] a, int l, int r, int k, int[] upper, int flags) {
9625         // Adapted from Alexandrescu (2016), algorithm 9.
9626         int fp;
9627         int s;
9628         int p;
9629         if (flags <= MODE_SAMPLING) {
9630             // Median into a 12th-tile
9631             fp = (r - l + 1) / 12;
9632             // Position the sample around the target k
9633             // Avoid bounds error due to rounding as (k-l)/(r-l) -> 1/12
9634             s = Math.max(k - mapDistance(k - l, l, r, fp), l + fp);
9635             p = k;
9636         } else {
9637             // i in 2nd quartile
9638             final int f = (r - l + 1) >> 2;
9639             final int f2 = f + f;
9640             for (int i = l + f, end = l + f2; i < end; i++) {
9641                 Sorting.lowerMedian4(a, i - f, i, i + f, i + f2);
9642             }
9643             // i in 5th 12th-tile
9644             fp = f / 3;
9645             s = l + f + fp;
9646             // No adaption uses the middle to enforce strict margins
9647             p = s + (flags == MODE_ADAPTION ? mapDistance(k - l, l, r, fp) : (fp >>> 1));
9648         }
9649         final int e = s + fp - 1;
9650         for (int i = s; i <= e; i++) {
9651             Sorting.sort3(a, i - fp, i, i + fp);
9652         }
9653         p = quickSelectAdaptive2(a, s, e, p, p, upper, qaMode);
9654         return expandPartitionT2(a, l, r, s, e, p, upper[0], upper);
9655     }
9656 
9657     /**
9658      * Partition an array slice around a pivot. Partitioning exchanges array elements such
9659      * that all elements smaller than pivot are before it and all elements larger than
9660      * pivot are after it.
9661      *
9662      * <p>Assumes the range {@code r - l >= 11}; the caller is responsible for selection on a smaller
9663      * range.
9664      *
9665      * <p>Uses the Chen and Dumitrescu repeated step median-of-medians-of-medians algorithm
9666      * with the upper median of 4 then either median of 3 with the final sample placed in the
9667      * 8th 12th-tile, or max of 3 with the final sample in the 9th 12th-tile;
9668      * the pivot chosen from the sample is adaptive using the input {@code k}.
9669      *
9670      * <p>Given a pivot in the middle of the sample this has margins of 1/4 and 1/6.
9671      *
9672      * @param a Data array.
9673      * @param l Lower bound (inclusive).
9674      * @param r Upper bound (inclusive).
9675      * @param k Target index.
9676      * @param upper Upper bound (inclusive) of the pivot range.
9677      * @param flags Control flags.
9678      * @return Lower bound (inclusive) of the pivot range.
9679      */
9680     private static int repeatedStepRight(double[] a, int l, int r, int k, int[] upper, int flags) {
9681         // Mirror image repeatedStepLeft using upper median into 3rd quartile
9682         int fp;
9683         int e;
9684         int p;
9685         if (flags <= MODE_SAMPLING) {
9686             // Median into a 12th-tile
9687             fp = (r - l + 1) / 12;
9688             // Position the sample around the target k
9689             // Avoid bounds error due to rounding as (r-k)/(r-l) -> 11/12
9690             e = Math.min(k + mapDistance(r - k, l, r, fp), r - fp);
9691             p = k;
9692         } else {
9693             // i in 3rd quartile
9694             final int f = (r - l + 1) >> 2;
9695             final int f2 = f + f;
9696             for (int i = r - f, end = r - f2; i > end; i--) {
9697                 Sorting.upperMedian4(a, i - f2, i - f, i, i + f);
9698             }
9699             // i in 8th 12th-tile
9700             fp = f / 3;
9701             e = r - f - fp;
9702             // No adaption uses the middle to enforce strict margins
9703             p = e - (flags == MODE_ADAPTION ? mapDistance(r - k, l, r, fp) : (fp >>> 1));
9704         }
9705         final int s = e - fp + 1;
9706         for (int i = s; i <= e; i++) {
9707             Sorting.sort3(a, i - fp, i, i + fp);
9708         }
9709         p = quickSelectAdaptive2(a, s, e, p, p, upper, qaMode);
9710         return expandPartitionT2(a, l, r, s, e, p, upper[0], upper);
9711     }
9712 
9713     /**
9714      * Partition an array slice around a pivot. Partitioning exchanges array elements such
9715      * that all elements smaller than pivot are before it and all elements larger than
9716      * pivot are after it.
9717      *
9718      * <p>Assumes the range {@code r - l >= 11}; the caller is responsible for selection on a smaller
9719      * range.
9720      *
9721      * <p>Uses the Chen and Dumitrescu repeated step median-of-medians-of-medians algorithm
9722      * with the minimum of 4 then median of 3; the final sample is placed in the
9723      * 2nd 12th-tile; the pivot chosen from the sample is adaptive using the input {@code k}.
9724      *
9725      * <p>Given a pivot in the middle of the sample this has margins of 1/12 and 1/3.
9726      *
9727      * @param a Data array.
9728      * @param l Lower bound (inclusive).
9729      * @param r Upper bound (inclusive).
9730      * @param k Target index.
9731      * @param upper Upper bound (inclusive) of the pivot range.
9732      * @param flags Control flags.
9733      * @return Lower bound (inclusive) of the pivot range.
9734      */
9735     private static int repeatedStepFarLeft(double[] a, int l, int r, int k, int[] upper, int flags) {
9736         // Far step has been changed from the Alexandrescu (2016) step of lower-median-of-4, min-of-3
9737         // into the 4th 12th-tile to a min-of-4, median-of-3 into the 2nd 12th-tile.
9738         // The differences are:
9739         // - The upper margin when not sampling is 8/24 vs. 9/24; the lower margin remains at 1/12.
9740         // - The position of the sample is closer to the expected location of k < |A| / 12.
9741         // - Sampling mode uses a median-of-3 with adaptive k, matching the other step methods.
9742         //   A min-of-3 sample can create a pivot too small if used with adaption of k leaving
9743         //   k in the larger partition and a wasted iteration.
9744         // - Adaption is adjusted to force use of the lower margin when not sampling.
9745         int fp;
9746         int s;
9747         int p;
9748         if (flags <= MODE_SAMPLING) {
9749             // 2nd 12th-tile
9750             fp = (r - l + 1) / 12;
9751             s = l + fp;
9752             // Use adaption
9753             p = s + mapDistance(k - l, l, r, fp);
9754         } else {
9755             // i in 2nd quartile; min into i-f (1st quartile)
9756             final int f = (r - l + 1) >> 2;
9757             final int f2 = f + f;
9758             for (int i = l + f, end = l + f2; i < end; i++) {
9759                 if (a[i + f] < a[i - f]) {
9760                     final double u = a[i + f];
9761                     a[i + f] = a[i - f];
9762                     a[i - f] = u;
9763                 }
9764                 if (a[i + f2] < a[i]) {
9765                     final double v = a[i + f2];
9766                     a[i + f2] = a[i];
9767                     a[i] = v;
9768                 }
9769                 if (a[i] < a[i - f]) {
9770                     final double u = a[i];
9771                     a[i] = a[i - f];
9772                     a[i - f] = u;
9773                 }
9774             }
9775             // 2nd 12th-tile
9776             fp = f / 3;
9777             s = l + fp;
9778             // Lower margin has 2(d+1) elements; d == (position in sample) - s
9779             // Force k into the lower margin
9780             p = s + ((k - l) >>> 1);
9781         }
9782         final int e = s + fp - 1;
9783         for (int i = s; i <= e; i++) {
9784             Sorting.sort3(a, i - fp, i, i + fp);
9785         }
9786         p = quickSelectAdaptive2(a, s, e, p, p, upper, qaMode);
9787         return expandPartitionT2(a, l, r, s, e, p, upper[0], upper);
9788     }
9789 
9790     /**
9791      * Partition an array slice around a pivot. Partitioning exchanges array elements such
9792      * that all elements smaller than pivot are before it and all elements larger than
9793      * pivot are after it.
9794      *
9795      * <p>Assumes the range {@code r - l >= 11}; the caller is responsible for selection on a smaller
9796      * range.
9797      *
9798      * <p>Uses the Chen and Dumitrescu repeated step median-of-medians-of-medians algorithm
9799      * with the maximum of 4 then median of 3; the final sample is placed in the
9800      * 11th 12th-tile; the pivot chosen from the sample is adaptive using the input {@code k}.
9801      *
9802      * <p>Given a pivot in the middle of the sample this has margins of 1/3 and 1/12.
9803      *
9804      * @param a Data array.
9805      * @param l Lower bound (inclusive).
9806      * @param r Upper bound (inclusive).
9807      * @param k Target index.
9808      * @param upper Upper bound (inclusive) of the pivot range.
9809      * @param flags Control flags.
9810      * @return Lower bound (inclusive) of the pivot range.
9811      */
9812     private static int repeatedStepFarRight(double[] a, int l, int r, int k, int[] upper, int flags) {
9813         // Mirror image repeatedStepFarLeft
9814         int fp;
9815         int e;
9816         int p;
9817         if (flags <= MODE_SAMPLING) {
9818             // 11th 12th-tile
9819             fp = (r - l + 1) / 12;
9820             e = r - fp;
9821             // Use adaption
9822             p = e - mapDistance(r - k, l, r, fp);
9823         } else {
9824             // i in 3rd quartile; max into i+f (4th quartile)
9825             final int f = (r - l + 1) >> 2;
9826             final int f2 = f + f;
9827             for (int i = r - f, end = r - f2; i > end; i--) {
9828                 if (a[i - f] > a[i + f]) {
9829                     final double u = a[i - f];
9830                     a[i - f] = a[i + f];
9831                     a[i + f] = u;
9832                 }
9833                 if (a[i - f2] > a[i]) {
9834                     final double v = a[i - f2];
9835                     a[i - f2] = a[i];
9836                     a[i] = v;
9837                 }
9838                 if (a[i] > a[i + f]) {
9839                     final double u = a[i];
9840                     a[i] = a[i + f];
9841                     a[i + f] = u;
9842                 }
9843             }
9844             // 11th 12th-tile
9845             fp = f / 3;
9846             e = r - fp;
9847             // Upper margin has 2(d+1) elements; d == e - (position in sample)
9848             // Force k into the upper margin
9849             p = e - ((r - k) >>> 1);
9850         }
9851         final int s = e - fp + 1;
9852         for (int i = s; i <= e; i++) {
9853             Sorting.sort3(a, i - fp, i, i + fp);
9854         }
9855         p = quickSelectAdaptive2(a, s, e, p, p, upper, qaMode);
9856         return expandPartitionT2(a, l, r, s, e, p, upper[0], upper);
9857     }
9858 
9859     /**
9860      * Partition an array slice around a pivot. Partitioning exchanges array elements such
9861      * that all elements smaller than pivot are before it and all elements larger than
9862      * pivot are after it.
9863      *
9864      * <p>Partitions a Floyd-Rivest sample around a pivot offset so that the input {@code k} will
9865      * fall in the smaller partition when the entire range is partitioned.
9866      *
9867      * <p>Assumes the range {@code r - l} is large.
9868      *
9869      * @param a Data array.
9870      * @param l Lower bound (inclusive).
9871      * @param r Upper bound (inclusive).
9872      * @param k Target index.
9873      * @param upper Upper bound (inclusive) of the pivot range.
9874      * @param flags Control flags.
9875      * @return Lower bound (inclusive) of the pivot range.
9876      */
9877     private static int sampleStep(double[] a, int l, int r, int k, int[] upper, int flags) {
9878         // Floyd-Rivest: use SELECT recursively on a sample of size S to get an estimate
9879         // for the (k-l+1)-th smallest element into a[k], biased slightly so that the
9880         // (k-l+1)-th element is expected to lie in the smaller set after partitioning.
9881         final int n = r - l + 1;
9882         final int ith = k - l + 1;
9883         final double z = Math.log(n);
9884         // sample size = 0.5 * n^(2/3)
9885         final double s = 0.5 * Math.exp(0.6666666666666666 * z);
9886         final double sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * Integer.signum(ith - (n >> 1));
9887         final int ll = Math.max(l, (int) (k - ith * s / n + sd));
9888         final int rr = Math.min(r, (int) (k + (n - ith) * s / n + sd));
9889         // Note: Random sampling is not supported.
9890         // Sample recursion restarts from [ll, rr]
9891         final int p = quickSelectAdaptive2(a, ll, rr, k, k, upper, qaMode);
9892         return expandPartitionT2(a, l, r, ll, rr, p, upper[0], upper);
9893     }
9894 
9895     /**
9896      * Move NaN values to the end of the array.
9897      * This allows all other values to be compared using {@code <, ==, >} operators (with
9898      * the exception of signed zeros).
9899      *
9900      * @param data Values.
9901      * @return index of last non-NaN value (or -1)
9902      */
9903     static int sortNaN(double[] data) {
9904         int end = data.length;
9905         // Find first non-NaN
9906         while (--end >= 0) {
9907             if (!Double.isNaN(data[end])) {
9908                 break;
9909             }
9910         }
9911         for (int i = end; --i >= 0;) {
9912             final double v = data[i];
9913             if (Double.isNaN(v)) {
9914                 // swap(data, i, end--)
9915                 data[i] = data[end];
9916                 data[end] = v;
9917                 end--;
9918             }
9919         }
9920         return end;
9921     }
9922 
9923     /**
9924      * Move invalid indices to the end of the array.
9925      *
9926      * @param indices Values.
9927      * @param right Upper bound of data (inclusive).
9928      * @param count Count of indices.
9929      * @return count of valid indices
9930      */
9931     static int countIndices(int[] indices, int count, int right) {
9932         int end = count;
9933         // Find first valid index
9934         while (--end >= 0) {
9935             if (indices[end] <= right) {
9936                 break;
9937             }
9938         }
9939         for (int i = end; --i >= 0;) {
9940             final int k = indices[i];
9941             if (k > right) {
9942                 // swap(indices, i, end--)
9943                 indices[i] = indices[end];
9944                 indices[end] = k;
9945                 end--;
9946             }
9947         }
9948         return end + 1;
9949     }
9950 
9951     /**
9952      * Count the number of signed zeros (-0.0) if the range contains a mix of positive and
9953      * negative zeros. If all positive, or all negative then this returns 0.
9954      *
9955      * <p>This method can be used when a pivot value is zero during partitioning when the
9956      * method uses the pivot value to replace values matched as equal using {@code ==}.
9957      * This may destroy a mixture of signed zeros by overwriting them as all 0.0 or -0.0
9958      * depending on the pivot value.
9959      *
9960      * @param data Values.
9961      * @param left Lower bound (inclusive).
9962      * @param right Upper bound (inclusive).
9963      * @return the count of signed zeros if some positive zeros are also present
9964      */
9965     static int countMixedSignedZeros(double[] data, int left, int right) {
9966         // Count negative zeros
9967         int c = 0;
9968         int cn = 0;
9969         for (int i = left; i <= right; i++) {
9970             if (data[i] == 0) {
9971                 c++;
9972                 if (Double.doubleToRawLongBits(data[i]) < 0) {
9973                     cn++;
9974                 }
9975             }
9976         }
9977         return c == cn ? 0 : cn;
9978     }
9979 
9980     /**
9981      * Sort a range of all zero values.
9982      * This orders -0.0 before 0.0.
9983      *
9984      * <p>Warning: The range must contain only zeros.
9985      *
9986      * @param data Values.
9987      * @param left Lower bound (inclusive).
9988      * @param right Upper bound (inclusive).
9989      */
9990     static void sortZero(double[] data, int left, int right) {
9991         // Count negative zeros
9992         int c = 0;
9993         for (int i = left; i <= right; i++) {
9994             if (Double.doubleToRawLongBits(data[i]) < 0) {
9995                 c++;
9996             }
9997         }
9998         // Replace
9999         if (c != 0) {
10000             int i = left;
10001             while (c-- > 0) {
10002                 data[i++] = -0.0;
10003             }
10004             while (i <= right) {
10005                 data[i++] = 0.0;
10006             }
10007         }
10008     }
10009 
10010     /**
10011      * Detect and fix the sort order of signed zeros. Assumes the data may have been
10012      * partially ordered around zero.
10013      *
10014      * <p>Searches for zeros if {@code data[left] <= 0} and {@code data[right] >= 0}.
10015      * If zeros are discovered in the range then they are assumed to be continuous.
10016      *
10017      * @param data Values.
10018      * @param left Lower bound (inclusive).
10019      * @param right Upper bound (inclusive).
10020      */
10021     private static void fixContinuousSignedZeros(double[] data, int left, int right) {
10022         int j;
10023         if (data[left] <= 0 && data[right] >= 0) {
10024             int i = left;
10025             while (data[i] < 0) {
10026                 i++;
10027             }
10028             j = right;
10029             while (data[j] > 0) {
10030                 j--;
10031             }
10032             sortZero(data, i, j);
10033         }
10034     }
10035 
10036     /**
10037      * Creates the maximum recursion depth for single-pivot quickselect recursion.
10038      *
10039      * <p>Warning: A length of zero will create a negative recursion depth.
10040      * In practice this does not matter as the sort / partition of a length
10041      * zero array should ignore the data.
10042      *
10043      * @param n Length of data (must be strictly positive).
10044      * @return the maximum recursion depth
10045      */
10046     private int createMaxDepthSinglePivot(int n) {
10047         // Ideal single pivot recursion will take log2(n) steps as data is
10048         // divided into length (n/2) at each iteration.
10049         final int maxDepth = floorLog2(n);
10050         // This factor should be tuned for practical performance
10051         return (int) Math.floor(maxDepth * recursionMultiple) + recursionConstant;
10052     }
10053 
10054     /**
10055      * Compute the maximum recursion depth for single pivot recursion.
10056      * Uses {@code 2 * floor(log2 (x))}.
10057      *
10058      * @param x Value.
10059      * @return {@code log3(x))}
10060      */
10061     private static int singlePivotMaxDepth(int x) {
10062         return (31 - Integer.numberOfLeadingZeros(x)) << 1;
10063     }
10064 
10065     /**
10066      * Compute {@code floor(log 2 (x))}. This is valid for all strictly positive {@code x}.
10067      *
10068      * <p>Returns -1 for {@code x = 0} in place of -infinity.
10069      *
10070      * @param x Value.
10071      * @return {@code floor(log 2 (x))}
10072      */
10073     static int floorLog2(int x) {
10074         return 31 - Integer.numberOfLeadingZeros(x);
10075     }
10076 
10077     /**
10078      * Convert {@code ln(n)} to the single-pivot max depth.
10079      *
10080      * @param x ln(n)
10081      * @return the maximum recursion depth
10082      */
10083     private int lnNtoMaxDepthSinglePivot(double x) {
10084         final double maxDepth = x * LOG2_E;
10085         return (int) Math.floor(maxDepth * recursionMultiple) + recursionConstant;
10086     }
10087 
10088     /**
10089      * Creates the maximum recursion depth for dual-pivot quickselect recursion.
10090      *
10091      * <p>Warning: A length of zero will create a high recursion depth.
10092      * In practice this does not matter as the sort / partition of a length
10093      * zero array should ignore the data.
10094      *
10095      * @param n Length of data (must be strictly positive).
10096      * @return the maximum recursion depth
10097      */
10098     private int createMaxDepthDualPivot(int n) {
10099         // Ideal dual pivot recursion will take log3(n) steps as data is
10100         // divided into length (n/3) at each iteration.
10101         final int maxDepth = log3(n);
10102         // This factor should be tuned for practical performance
10103         return (int) Math.floor(maxDepth * recursionMultiple) + recursionConstant;
10104     }
10105 
10106     /**
10107      * Compute an approximation to {@code log3 (x)}.
10108      *
10109      * <p>The result is between {@code floor(log3(x))} and {@code ceil(log3(x))}.
10110      * The result is correctly rounded when {@code x +/- 1} is a power of 3.
10111      *
10112      * @param x Value.
10113      * @return {@code log3(x))}
10114      */
10115     static int log3(int x) {
10116         // log3(2) ~ 1.5849625
10117         // log3(x) ~ log2(x) * 0.630929753... ~ log2(x) * 323 / 512 (0.630859375)
10118         // Use (floor(log2(x))+1) * 323 / 512
10119         // This result is always between floor(log3(x)) and ceil(log3(x)).
10120         // It is correctly rounded when x +/- 1 is a power of 3.
10121         return ((32 - Integer.numberOfLeadingZeros(x)) * 323) >>> 9;
10122     }
10123 
10124     /**
10125      * Search the data for the largest index {@code i} where {@code a[i]} is
10126      * less-than-or-equal to the {@code key}; else return {@code left - 1}.
10127      * <pre>
10128      * a[i] <= k    :   left <= i <= right, or (left - 1)
10129      * </pre>
10130      *
10131      * <p>The data is assumed to be in ascending order, otherwise the behaviour is undefined.
10132      * If the range contains multiple elements with the {@code key} value, the result index
10133      * may be any that match.
10134      *
10135      * <p>This is similar to using {@link java.util.Arrays#binarySearch(int[], int, int, int)
10136      * Arrays.binarySearch}. The method differs in:
10137      * <ul>
10138      * <li>use of an inclusive upper bound;
10139      * <li>returning the closest index with a value below {@code key} if no match was not found;
10140      * <li>performing no range checks: it is assumed {@code left <= right} and they are valid
10141      * indices into the array.
10142      * </ul>
10143      *
10144      * <p>An equivalent use of binary search is:
10145      * <pre>{@code
10146      * int i = Arrays.binarySearch(a, left, right + 1, k);
10147      * if (i < 0) {
10148      *     i = ~i - 1;
10149      * }
10150      * }</pre>
10151      *
10152      * <p>This specialisation avoids the caller checking the binary search result for the use
10153      * case when the presence or absence of a key is not important; only that the returned
10154      * index for an absence of a key is the largest index. When used on unique keys this
10155      * method can be used to update an upper index so all keys are known to be below a key:
10156      *
10157      * <pre>{@code
10158      * int[] keys = ...
10159      * // [i0, i1] contains all keys
10160      * int i0 = 0;
10161      * int i1 = keys.length - 1;
10162      * // Update: [i0, i1] contains all keys <= k
10163      * i1 = searchLessOrEqual(keys, i0, i1, k);
10164      * }</pre>
10165      *
10166      * @param a Data.
10167      * @param left Lower bound (inclusive).
10168      * @param right Upper bound (inclusive).
10169      * @param k Key.
10170      * @return largest index {@code i} such that {@code a[i] <= k}, or {@code left - 1} if no
10171      * such index exists
10172      */
10173     static int searchLessOrEqual(int[] a, int left, int right, int k) {
10174         int l = left;
10175         int r = right;
10176         while (l <= r) {
10177             // Middle value
10178             final int m = (l + r) >>> 1;
10179             final int v = a[m];
10180             // Test:
10181             // l------m------r
10182             //        v  k      update left
10183             //     k  v         update right
10184 
10185             // Full binary search
10186             // Run time is up to log2(n) (fast exit on a match) but has more comparisons
10187             if (v < k) {
10188                 l = m + 1;
10189             } else if (v > k) {
10190                 r = m - 1;
10191             } else {
10192                 // Equal
10193                 return m;
10194             }
10195 
10196             // Modified search that does not expect a match
10197             // Run time is log2(n). Benchmarks as the same speed.
10198             //if (v > k) {
10199             //    r = m - 1;
10200             //} else {
10201             //    l = m + 1;
10202             //}
10203         }
10204         // Return largest known value below:
10205         // r is always moved downward when a middle index value is too high
10206         return r;
10207     }
10208 
10209     /**
10210      * Search the data for the smallest index {@code i} where {@code a[i]} is
10211      * greater-than-or-equal to the {@code key}; else return {@code right + 1}.
10212      * <pre>
10213      * a[i] >= k      :   left <= i <= right, or (right + 1)
10214      * </pre>
10215      *
10216      * <p>The data is assumed to be in ascending order, otherwise the behaviour is undefined.
10217      * If the range contains multiple elements with the {@code key} value, the result index
10218      * may be any that match.
10219      *
10220      * <p>This is similar to using {@link java.util.Arrays#binarySearch(int[], int, int, int)
10221      * Arrays.binarySearch}. The method differs in:
10222      * <ul>
10223      * <li>use of an inclusive upper bound;
10224      * <li>returning the closest index with a value above {@code key} if no match was not found;
10225      * <li>performing no range checks: it is assumed {@code left <= right} and they are valid
10226      * indices into the array.
10227      * </ul>
10228      *
10229      * <p>An equivalent use of binary search is:
10230      * <pre>{@code
10231      * int i = Arrays.binarySearch(a, left, right + 1, k);
10232      * if (i < 0) {
10233      *     i = ~i;
10234      * }
10235      * }</pre>
10236      *
10237      * <p>This specialisation avoids the caller checking the binary search result for the use
10238      * case when the presence or absence of a key is not important; only that the returned
10239      * index for an absence of a key is the smallest index. When used on unique keys this
10240      * method can be used to update a lower index so all keys are known to be above a key:
10241      *
10242      * <pre>{@code
10243      * int[] keys = ...
10244      * // [i0, i1] contains all keys
10245      * int i0 = 0;
10246      * int i1 = keys.length - 1;
10247      * // Update: [i0, i1] contains all keys >= k
10248      * i0 = searchGreaterOrEqual(keys, i0, i1, k);
10249      * }</pre>
10250      *
10251      * @param a Data.
10252      * @param left Lower bound (inclusive).
10253      * @param right Upper bound (inclusive).
10254      * @param k Key.
10255      * @return largest index {@code i} such that {@code a[i] >= k}, or {@code right + 1} if no
10256      * such index exists
10257      */
10258     static int searchGreaterOrEqual(int[] a, int left, int right, int k) {
10259         int l = left;
10260         int r = right;
10261         while (l <= r) {
10262             // Middle value
10263             final int m = (l + r) >>> 1;
10264             final int v = a[m];
10265             // Test:
10266             // l------m------r
10267             //        v  k      update left
10268             //     k  v         update right
10269 
10270             // Full binary search
10271             // Run time is up to log2(n) (fast exit on a match) but has more comparisons
10272             if (v < k) {
10273                 l = m + 1;
10274             } else if (v > k) {
10275                 r = m - 1;
10276             } else {
10277                 // Equal
10278                 return m;
10279             }
10280 
10281             // Modified search that does not expect a match
10282             // Run time is log2(n). Benchmarks as the same speed.
10283             //if (v < k) {
10284             //    l = m + 1;
10285             //} else {
10286             //    r = m - 1;
10287             //}
10288         }
10289         // Smallest known value above
10290         // l is always moved upward when a middle index value is too low
10291         return l;
10292     }
10293 
10294     /**
10295      * Creates the source of random numbers in {@code [0, n)}.
10296      * This is configurable via the control flags.
10297      *
10298      * @param n Data length.
10299      * @param k Target index.
10300      * @return the RNG
10301      */
10302     private IntUnaryOperator createRNG(int n, int k) {
10303         // Configurable
10304         if ((controlFlags & FLAG_MSWS) != 0) {
10305             // Middle-Square Weyl Sequence is fastest int generator
10306             final UniformRandomProvider rng = RandomSource.MSWS.create(n * 31L + k);
10307             if ((controlFlags & FLAG_BIASED_RANDOM) != 0) {
10308                 // result = i * [0, 2^32) / 2^32
10309                 return i -> (int) ((i * Integer.toUnsignedLong(rng.nextInt())) >>> Integer.SIZE);
10310             }
10311             return rng::nextInt;
10312         }
10313         if ((controlFlags & FLAG_SPLITTABLE_RANDOM) != 0) {
10314             final SplittableRandom rng = new SplittableRandom(n * 31L + k);
10315             if ((controlFlags & FLAG_BIASED_RANDOM) != 0) {
10316                 // result = i * [0, 2^32) / 2^32
10317                 return i -> (int) ((i * Integer.toUnsignedLong(rng.nextInt())) >>> Integer.SIZE);
10318             }
10319             return rng::nextInt;
10320         }
10321         return createFastRNG(n, k);
10322     }
10323 
10324     /**
10325      * Creates the source of random numbers in {@code [0, n)}.
10326      *
10327      * <p>This uses a RNG based on a linear congruential generator with biased numbers
10328      * in {@code [0, n)}, favouring speed over statitsical robustness.
10329      *
10330      * @param n Data length.
10331      * @param k Target index.
10332      * @return the RNG
10333      */
10334     static IntUnaryOperator createFastRNG(int n, int k) {
10335         return new Gen(n * 31L + k);
10336     }
10337 
10338     /**
10339      * Random generator for numbers in {@code [0, n)}.
10340      * The random sample should be fast in preference to statistically robust.
10341      * Here we implement a biased sampler for the range [0, n)
10342      * as n * f with f a fraction with base 2^32.
10343      * Source of randomness is a 64-bit LCG using the constants from MMIX by Donald Knuth.
10344      * https://en.wikipedia.org/wiki/Linear_congruential_generator
10345      */
10346     private static final class Gen implements IntUnaryOperator {
10347         /** LCG state. */
10348         private long s;
10349 
10350         /**
10351          * @param seed Seed.
10352          */
10353         Gen(long seed) {
10354             // Update state
10355             this.s = seed * 6364136223846793005L + 1442695040888963407L;
10356         }
10357 
10358         @Override
10359         public int applyAsInt(int n) {
10360             final long x = s;
10361             // Update state
10362             s = s * 6364136223846793005L + 1442695040888963407L;
10363             // Use the upper 32-bits from the state as the random 32-bit sample
10364             // result = n * [0, 2^32) / 2^32
10365             return (int) ((n * (x >>> Integer.SIZE)) >>> Integer.SIZE);
10366         }
10367     }
10368 }