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