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 }