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 }