po) {
+ return po.partition(a, low, high, pivotIndex1, pivotIndex2);
}
/**
* Sorts the specified range of the array using parallel merge
- * sort and/or Dual-Pivot Quicksort.
+ * sort and/or Dual-Pivot Quicksort.
*
* To balance the faster splitting and parallelism of merge sort
* with the faster element partitioning of Quicksort, ranges are
@@ -228,36 +221,33 @@ private static int getDepth(int parallelism, int size) {
* @param high the index of the last element, exclusive, to be sorted
*/
static void sort(int[] a, int parallelism, int low, int high) {
- int size = high - low;
-
- if (parallelism > 1 && size > MIN_PARALLEL_SORT_SIZE) {
- int depth = getDepth(parallelism, size >> 12);
- int[] b = depth == 0 ? null : new int[size];
- new Sorter(null, a, b, low, size, low, depth).invoke();
+ if (parallelism > 1 && high - low > MIN_PARALLEL_SORT_SIZE) {
+ new Sorter<>(a, parallelism, low, high - low).invoke();
} else {
sort(null, a, 0, low, high);
}
}
/**
- * Sorts the specified array using the Dual-Pivot Quicksort and/or
- * other sorts in special-cases, possibly with parallel partitions.
+ * Sorts the specified range of the array using Dual-Pivot Quicksort.
*
- * @param sorter parallel context
+ * @param sorter the parallel context
* @param a the array to be sorted
* @param bits the combination of recursion depth and bit flag, where
- * the right bit "0" indicates that array is the leftmost part
+ * the right bit "0" indicates that range is the leftmost part
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
- static void sort(Sorter sorter, int[] a, int bits, int low, int high) {
+ static void sort(Sorter sorter, int[] a, int bits, int low, int high) {
while (true) {
- int end = high - 1, size = high - low;
+ int size = high - low;
+
/*
- * Run mixed insertion sort on small non-leftmost parts.
+ * Run adaptive mixed insertion sort on small non-leftmost parts.
*/
- if (size < MAX_MIXED_INSERTION_SORT_SIZE + bits && (bits & 1) > 0) {
- sort(int.class, a, Unsafe.ARRAY_INT_BASE_OFFSET, low, high, DualPivotQuicksort::mixedInsertionSort);
+ if (size < MAX_INSERTION_SORT_SIZE + bits && (bits & 1) > 0) {
+ sort(int.class, a, Unsafe.ARRAY_INT_BASE_OFFSET,
+ low, high, DualPivotQuicksort::mixedInsertionSort);
return;
}
@@ -265,33 +255,25 @@ static void sort(Sorter sorter, int[] a, int bits, int low, int high) {
* Invoke insertion sort on small leftmost part.
*/
if (size < MAX_INSERTION_SORT_SIZE) {
- sort(int.class, a, Unsafe.ARRAY_INT_BASE_OFFSET, low, high, DualPivotQuicksort::insertionSort);
- return;
- }
-
- /*
- * Check if the whole array or large non-leftmost
- * parts are nearly sorted and then merge runs.
- */
- if ((bits == 0 || size > MIN_TRY_MERGE_SIZE && (bits & 1) > 0)
- && tryMergeRuns(sorter, a, low, size)) {
+ sort(int.class, a, Unsafe.ARRAY_INT_BASE_OFFSET,
+ low, high, DualPivotQuicksort::insertionSort);
return;
}
/*
- * Switch to heap sort if execution
- * time is becoming quadratic.
+ * Try merging sort on large part.
*/
- if ((bits += DELTA) > MAX_RECURSION_DEPTH) {
- heapSort(a, low, high);
+ if (size > MIN_MERGING_SORT_SIZE * bits
+ && tryMergingSort(sorter, a, low, high)) {
return;
}
/*
- * Use an inexpensive approximation of the golden ratio
- * to select five sample elements and determine pivots.
+ * Divide the given array into the golden ratio using
+ * an inexpensive approximation to select five sample
+ * elements and determine pivots.
*/
- int step = (size >> 3) * 3 + 3;
+ int step = (size >> 2) + (size >> 3) + (size >> 7);
/*
* Five elements around (and including) the central element
@@ -300,119 +282,127 @@ && tryMergeRuns(sorter, a, low, size)) {
* determined to work well on a wide variety of inputs.
*/
int e1 = low + step;
- int e5 = end - step;
+ int e5 = high - step;
int e3 = (e1 + e5) >>> 1;
int e2 = (e1 + e3) >>> 1;
int e4 = (e3 + e5) >>> 1;
- int a3 = a[e3];
/*
- * Sort these elements in place by the combination
+ * Sort these elements in-place by the combination
* of 4-element sorting network and insertion sort.
*
- * 5 ------o-----------o------------
- * | |
- * 4 ------|-----o-----o-----o------
- * | | |
- * 2 ------o-----|-----o-----o------
- * | |
- * 1 ------------o-----o------------
- */
- if (a[e5] < a[e2]) { int t = a[e5]; a[e5] = a[e2]; a[e2] = t; }
- if (a[e4] < a[e1]) { int t = a[e4]; a[e4] = a[e1]; a[e1] = t; }
- if (a[e5] < a[e4]) { int t = a[e5]; a[e5] = a[e4]; a[e4] = t; }
- if (a[e2] < a[e1]) { int t = a[e2]; a[e2] = a[e1]; a[e1] = t; }
- if (a[e4] < a[e2]) { int t = a[e4]; a[e4] = a[e2]; a[e2] = t; }
-
- if (a3 < a[e2]) {
- if (a3 < a[e1]) {
- a[e3] = a[e2]; a[e2] = a[e1]; a[e1] = a3;
+ * 1 ---------o---------------o-----------------
+ * | |
+ * 2 ---------|-------o-------o-------o---------
+ * | | |
+ * 3 ---------|-------|---------------|---------
+ * | | |
+ * 4 ---------o-------|-------o-------o---------
+ * | |
+ * 5 -----------------o-------o-----------------
+ */
+ if (a[e1] > a[e4]) { int t = a[e1]; a[e1] = a[e4]; a[e4] = t; }
+ if (a[e2] > a[e5]) { int t = a[e2]; a[e2] = a[e5]; a[e5] = t; }
+ if (a[e4] > a[e5]) { int t = a[e4]; a[e4] = a[e5]; a[e5] = t; }
+ if (a[e1] > a[e2]) { int t = a[e1]; a[e1] = a[e2]; a[e2] = t; }
+ if (a[e2] > a[e4]) { int t = a[e2]; a[e2] = a[e4]; a[e4] = t; }
+
+ /*
+ * Insert the third element.
+ */
+ if (a[e3] < a[e2]) {
+ if (a[e3] < a[e1]) {
+ int t = a[e3]; a[e3] = a[e2]; a[e2] = a[e1]; a[e1] = t;
} else {
- a[e3] = a[e2]; a[e2] = a3;
+ int t = a[e3]; a[e3] = a[e2]; a[e2] = t;
}
- } else if (a3 > a[e4]) {
- if (a3 > a[e5]) {
- a[e3] = a[e4]; a[e4] = a[e5]; a[e5] = a3;
+ } else if (a[e3] > a[e4]) {
+ if (a[e3] > a[e5]) {
+ int t = a[e3]; a[e3] = a[e4]; a[e4] = a[e5]; a[e5] = t;
} else {
- a[e3] = a[e4]; a[e4] = a3;
+ int t = a[e3]; a[e3] = a[e4]; a[e4] = t;
}
}
- // Pointers
- int lower; // The index of the last element of the left part
- int upper; // The index of the first element of the right part
+ /*
+ * Switch to heap sort to avoid quadratic time.
+ */
+ if ((bits += 2) > MAX_RECURSION_DEPTH) {
+ heapSort(a, low, high);
+ return;
+ }
/*
- * Partitioning with 2 pivots in case of different elements.
+ * indices[0] - the index of the last element of the left part
+ * indices[1] - the index of the first element of the right part
*/
- if (a[e1] < a[e2] && a[e2] < a[e3] && a[e3] < a[e4] && a[e4] < a[e5]) {
- /*
- * Use the first and fifth of the five sorted elements as
- * the pivots. These values are inexpensive approximation
- * of tertiles. Note, that pivot1 < pivot2.
- */
- int[] pivotIndices = partition(int.class, a, Unsafe.ARRAY_INT_BASE_OFFSET, low, high, e1, e5, DualPivotQuicksort::partitionDualPivot);
- lower = pivotIndices[0];
- upper = pivotIndices[1];
+ int[] indices;
+ /*
+ * Partitioning with two pivots on array of fully random elements.
+ */
+ if (a[e1] < a[e2] && a[e2] < a[e3] && a[e3] < a[e4] && a[e4] < a[e5]) {
+ indices = partition(int.class, a, Unsafe.ARRAY_INT_BASE_OFFSET,
+ low, high, e1, e5, DualPivotQuicksort::partitionWithTwoPivots);
/*
* Sort non-left parts recursively (possibly in parallel),
* excluding known pivots.
*/
if (size > MIN_PARALLEL_SORT_SIZE && sorter != null) {
- sorter.forkSorter(bits | 1, lower + 1, upper);
- sorter.forkSorter(bits | 1, upper + 1, high);
+ sorter.fork(bits | 1, indices[0] + 1, indices[1]);
+ sorter.fork(bits | 1, indices[1] + 1, high);
} else {
- sort(sorter, a, bits | 1, lower + 1, upper);
- sort(sorter, a, bits | 1, upper + 1, high);
+ sort(sorter, a, bits | 1, indices[0] + 1, indices[1]);
+ sort(sorter, a, bits | 1, indices[1] + 1, high);
}
- } else { // Use single pivot in case of many equal elements
+ } else { // Partitioning with one pivot
+
+ indices = partition(int.class, a, Unsafe.ARRAY_INT_BASE_OFFSET,
+ low, high, e3, e3, DualPivotQuicksort::partitionWithOnePivot);
- /*
- * Use the third of the five sorted elements as the pivot.
- * This value is inexpensive approximation of the median.
- */
- int[] pivotIndices = partition(int.class, a, Unsafe.ARRAY_INT_BASE_OFFSET, low, high, e3, e3, DualPivotQuicksort::partitionSinglePivot);
- lower = pivotIndices[0];
- upper = pivotIndices[1];
/*
* Sort the right part (possibly in parallel), excluding
* known pivot. All elements from the central part are
* equal and therefore already sorted.
*/
if (size > MIN_PARALLEL_SORT_SIZE && sorter != null) {
- sorter.forkSorter(bits | 1, upper, high);
+ sorter.fork(bits | 1, indices[1], high);
} else {
- sort(sorter, a, bits | 1, upper, high);
+ sort(sorter, a, bits | 1, indices[1], high);
}
}
- high = lower; // Iterate along the left part
+ high = indices[0]; // Iterate along the left part
}
}
/**
- * Partitions the specified range of the array using the two pivots provided.
+ * Partitions the specified range of the array using two given pivots.
*
- * @param array the array to be partitioned
+ * @param a the array for partitioning
* @param low the index of the first element, inclusive, for partitioning
* @param high the index of the last element, exclusive, for partitioning
* @param pivotIndex1 the index of pivot1, the first pivot
* @param pivotIndex2 the index of pivot2, the second pivot
- *
+ * @return indices of parts after partitioning
*/
- @ForceInline
- private static int[] partitionDualPivot(int[] a, int low, int high, int pivotIndex1, int pivotIndex2) {
- int end = high - 1;
+ private static int[] partitionWithTwoPivots(
+ int[] a, int low, int high, int pivotIndex1, int pivotIndex2) {
+ /*
+ * Pointers to the right and left parts.
+ */
+ int upper = --high;
int lower = low;
- int upper = end;
- int e1 = pivotIndex1;
- int e5 = pivotIndex2;
- int pivot1 = a[e1];
- int pivot2 = a[e5];
+ /*
+ * Use the first and fifth of the five sorted elements as
+ * the pivots. These values are inexpensive approximation
+ * of tertiles. Note, that pivot1 < pivot2.
+ */
+ int pivot1 = a[pivotIndex1];
+ int pivot2 = a[pivotIndex2];
/*
* The first and the last elements to be sorted are moved
@@ -421,8 +411,8 @@ private static int[] partitionDualPivot(int[] a, int low, int high, int pivotInd
* into their final positions, and excluded from the next
* subsequent sorting.
*/
- a[e1] = a[lower];
- a[e5] = a[upper];
+ a[pivotIndex1] = a[lower];
+ a[pivotIndex2] = a[upper];
/*
* Skip elements, which are less or greater than the pivots.
@@ -433,39 +423,36 @@ private static int[] partitionDualPivot(int[] a, int low, int high, int pivotInd
/*
* Backward 3-interval partitioning
*
- * left part central part right part
- * +------------------------------------------------------------+
- * | < pivot1 | ? | pivot1 <= && <= pivot2 | > pivot2 |
- * +------------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * lower k upper
- *
- * Invariants:
- *
- * all in (low, lower] < pivot1
- * pivot1 <= all in (k, upper) <= pivot2
- * all in [upper, end) > pivot2
+ * left part central part right part
+ * +--------------+----------+--------------------------+--------------+
+ * | < pivot1 | ? | pivot1 <= .. <= pivot2 | > pivot2 |
+ * +--------------+----------+--------------------------+--------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
*
* Pointer k is the last index of ?-part
+ * Pointer lower is the last index of left part
+ * Pointer upper is the first index of right part
*/
for (int unused = --lower, k = ++upper; --k > lower; ) {
int ak = a[k];
- if (ak < pivot1) { // Move a[k] to the left side
- while (lower < k) {
- if (a[++lower] >= pivot1) {
- if (a[lower] > pivot2) {
- a[k] = a[--upper];
- a[upper] = a[lower];
- } else {
- a[k] = a[lower];
- }
- a[lower] = ak;
- break;
- }
+ if (ak < pivot1) { // Move a[k] to the left part
+ while (a[++lower] < pivot1);
+
+ if (lower > k) {
+ lower = k;
+ break;
+ }
+ if (a[lower] > pivot2) {
+ a[k] = a[--upper];
+ a[upper] = a[lower];
+ } else {
+ a[k] = a[lower];
}
- } else if (ak > pivot2) { // Move a[k] to the right side
+ a[lower] = ak;
+ } else if (ak > pivot2) { // Move a[k] to the right part
a[k] = a[--upper];
a[upper] = ak;
}
@@ -474,30 +461,35 @@ private static int[] partitionDualPivot(int[] a, int low, int high, int pivotInd
/*
* Swap the pivots into their final positions.
*/
- a[low] = a[lower]; a[lower] = pivot1;
- a[end] = a[upper]; a[upper] = pivot2;
+ a[low] = a[lower]; a[lower] = pivot1;
+ a[high] = a[upper]; a[upper] = pivot2;
- return new int[] {lower, upper};
+ return new int[] { lower, upper };
}
/**
- * Partitions the specified range of the array using a single pivot provided.
+ * Partitions the specified range of the array using one given pivot.
*
- * @param array the array to be partitioned
+ * @param a the array for partitioning
* @param low the index of the first element, inclusive, for partitioning
* @param high the index of the last element, exclusive, for partitioning
- * @param pivotIndex1 the index of pivot1, the first pivot
- * @param pivotIndex2 the index of pivot2, the second pivot
- *
+ * @param pivotIndex1 the index of single pivot
+ * @param pivotIndex2 the index of single pivot
+ * @return indices of parts after partitioning
*/
- @ForceInline
- private static int[] partitionSinglePivot(int[] a, int low, int high, int pivotIndex1, int pivotIndex2) {
-
- int end = high - 1;
+ private static int[] partitionWithOnePivot(
+ int[] a, int low, int high, int pivotIndex1, int pivotIndex2) {
+ /*
+ * Pointers to the right and left parts.
+ */
+ int upper = high;
int lower = low;
- int upper = end;
- int e3 = pivotIndex1;
- int pivot = a[e3];
+
+ /*
+ * Use the third of the five sorted elements as the pivot.
+ * This value is inexpensive approximation of the median.
+ */
+ int pivot = a[pivotIndex1];
/*
* The first element to be sorted is moved to the
@@ -506,43 +498,40 @@ private static int[] partitionSinglePivot(int[] a, int low, int high, int pivotI
* back into its final position, and excluded from
* the next subsequent sorting.
*/
- a[e3] = a[lower];
+ a[pivotIndex1] = a[lower];
/*
- * Traditional 3-way (Dutch National Flag) partitioning
- *
- * left part central part right part
- * +------------------------------------------------------+
- * | < pivot | ? | == pivot | > pivot |
- * +------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * lower k upper
+ * Dutch National Flag partitioning
*
- * Invariants:
- *
- * all in (low, lower] < pivot
- * all in (k, upper) == pivot
- * all in [upper, end] > pivot
+ * left part central part right part
+ * +--------------+----------+--------------+-------------+
+ * | < pivot | ? | == pivot | > pivot |
+ * +--------------+----------+--------------+-------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
*
* Pointer k is the last index of ?-part
+ * Pointer lower is the last index of left part
+ * Pointer upper is the first index of right part
*/
- for (int k = ++upper; --k > lower; ) {
+ for (int k = upper; --k > lower; ) {
int ak = a[k];
- if (ak != pivot) {
- a[k] = pivot;
+ if (ak == pivot) {
+ continue;
+ }
+ a[k] = pivot;
- if (ak < pivot) { // Move a[k] to the left side
- while (a[++lower] < pivot);
+ if (ak < pivot) { // Move a[k] to the left part
+ while (a[++lower] < pivot);
- if (a[lower] > pivot) {
- a[--upper] = a[lower];
- }
- a[lower] = ak;
- } else { // ak > pivot - Move a[k] to the right side
- a[--upper] = ak;
+ if (a[lower] > pivot) {
+ a[--upper] = a[lower];
}
+ a[lower] = ak;
+ } else { // ak > pivot - Move a[k] to the right part
+ a[--upper] = ak;
}
}
@@ -550,14 +539,15 @@ private static int[] partitionSinglePivot(int[] a, int low, int high, int pivotI
* Swap the pivot into its final position.
*/
a[low] = a[lower]; a[lower] = pivot;
- return new int[] {lower, upper};
+
+ return new int[] { lower, upper };
}
/**
- * Sorts the specified range of the array using mixed insertion sort.
+ * Sorts the specified range of the array using mixed insertion sort.
*
- * Mixed insertion sort is combination of simple insertion sort,
- * pin insertion sort and pair insertion sort.
+ * Mixed insertion sort is combination of pin insertion sort,
+ * simple insertion sort and pair insertion sort.
*
* In the context of Dual-Pivot Quicksort, the pivot element
* from the left part plays the role of sentinel, because it
@@ -569,110 +559,84 @@ private static int[] partitionSinglePivot(int[] a, int low, int high, int pivotI
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
- private static void mixedInsertionSort(int[] a, int low, int high) {
- int size = high - low;
- int end = high - 3 * ((size >> 5) << 3);
- if (end == high) {
+ static void mixedInsertionSort(int[] a, int low, int high) {
+ /*
+ * Split part for pin and pair insertion sorts.
+ */
+ int end = high - 3 * ((high - low) >> 3 << 1);
- /*
- * Invoke simple insertion sort on tiny array.
- */
- for (int i; ++low < end; ) {
+ /*
+ * Invoke simple insertion sort on small part.
+ */
+ if (end == high) {
+ for (int i; ++low < high; ) {
int ai = a[i = low];
- while (ai < a[--i]) {
- a[i + 1] = a[i];
+ while (ai < a[i - 1]) {
+ a[i] = a[--i];
}
- a[i + 1] = ai;
+ a[i] = ai;
}
- } else {
+ return;
+ }
+
+ /*
+ * Start with pin insertion sort.
+ */
+ for (int i, p = high; ++low < end; ) {
+ int ai = a[i = low], pin = a[--p];
/*
- * Start with pin insertion sort on small part.
- *
- * Pin insertion sort is extended simple insertion sort.
- * The main idea of this sort is to put elements larger
- * than an element called pin to the end of array (the
- * proper area for such elements). It avoids expensive
- * movements of these elements through the whole array.
+ * Swap larger element with pin.
*/
- int pin = a[end];
-
- for (int i, p = high; ++low < end; ) {
- int ai = a[i = low];
-
- if (ai < a[i - 1]) { // Small element
-
- /*
- * Insert small element into sorted part.
- */
- a[i] = a[--i];
-
- while (ai < a[--i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = ai;
-
- } else if (p > i && ai > pin) { // Large element
-
- /*
- * Find element smaller than pin.
- */
- while (a[--p] > pin);
-
- /*
- * Swap it with large element.
- */
- if (p > i) {
- ai = a[p];
- a[p] = a[i];
- }
-
- /*
- * Insert small element into sorted part.
- */
- while (ai < a[--i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = ai;
- }
+ if (ai > pin) {
+ ai = pin;
+ a[p] = a[i];
}
/*
- * Continue with pair insertion sort on remain part.
+ * Insert element into sorted part.
*/
- for (int i; low < high; ++low) {
- int a1 = a[i = low], a2 = a[++low];
+ while (ai < a[i - 1]) {
+ a[i] = a[--i];
+ }
+ a[i] = ai;
+ }
- /*
- * Insert two elements per iteration: at first, insert the
- * larger element and then insert the smaller element, but
- * from the position where the larger element was inserted.
- */
- if (a1 > a2) {
+ /*
+ * Finish with pair insertion sort.
+ */
+ for (int i; low < high; ++low) {
+ int a1 = a[i = low], a2 = a[++low];
- while (a1 < a[--i]) {
- a[i + 2] = a[i];
- }
- a[++i + 1] = a1;
+ /*
+ * Insert two elements per iteration: at first, insert the
+ * larger element and then insert the smaller element, but
+ * from the position where the larger element was inserted.
+ */
+ if (a1 > a2) {
- while (a2 < a[--i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = a2;
+ while (a1 < a[--i]) {
+ a[i + 2] = a[i];
+ }
+ a[++i + 1] = a1;
+
+ while (a2 < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = a2;
- } else if (a1 < a[i - 1]) {
+ } else if (a1 < a[i - 1]) {
- while (a2 < a[--i]) {
- a[i + 2] = a[i];
- }
- a[++i + 1] = a2;
+ while (a2 < a[--i]) {
+ a[i + 2] = a[i];
+ }
+ a[++i + 1] = a2;
- while (a1 < a[--i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = a1;
+ while (a1 < a[--i]) {
+ a[i + 1] = a[i];
}
+ a[i + 1] = a1;
}
}
}
@@ -684,90 +648,43 @@ private static void mixedInsertionSort(int[] a, int low, int high) {
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
- private static void insertionSort(int[] a, int low, int high) {
+ static void insertionSort(int[] a, int low, int high) {
for (int i, k = low; ++k < high; ) {
int ai = a[i = k];
if (ai < a[i - 1]) {
- while (--i >= low && ai < a[i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = ai;
+ do {
+ a[i] = a[--i];
+ } while (i > low && ai < a[i - 1]);
+
+ a[i] = ai;
}
}
}
/**
- * Sorts the specified range of the array using heap sort.
+ * Tries to sort the specified range of the array using merging sort.
*
+ * @param sorter the parallel context
* @param a the array to be sorted
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
+ * @return {@code true} if the array is finally sorted, otherwise {@code false}
*/
- private static void heapSort(int[] a, int low, int high) {
- for (int k = (low + high) >>> 1; k > low; ) {
- pushDown(a, --k, a[k], low, high);
- }
- while (--high > low) {
- int max = a[low];
- pushDown(a, low, a[high], low, high);
- a[high] = max;
- }
- }
-
- /**
- * Pushes specified element down during heap sort.
- *
- * @param a the given array
- * @param p the start index
- * @param value the given element
- * @param low the index of the first element, inclusive, to be sorted
- * @param high the index of the last element, exclusive, to be sorted
- */
- private static void pushDown(int[] a, int p, int value, int low, int high) {
- for (int k ;; a[p] = a[p = k]) {
- k = (p << 1) - low + 2; // Index of the right child
-
- if (k > high) {
- break;
- }
- if (k == high || a[k] < a[k - 1]) {
- --k;
- }
- if (a[k] <= value) {
- break;
- }
- }
- a[p] = value;
- }
-
- /**
- * Tries to sort the specified range of the array.
- *
- * @param sorter parallel context
- * @param a the array to be sorted
- * @param low the index of the first element to be sorted
- * @param size the array size
- * @return true if finally sorted, false otherwise
- */
- private static boolean tryMergeRuns(Sorter sorter, int[] a, int low, int size) {
-
+ static boolean tryMergingSort(Sorter sorter, int[] a, int low, int high) {
/*
- * The run array is constructed only if initial runs are
- * long enough to continue, run[i] then holds start index
- * of the i-th sequence of elements in non-descending order.
+ * The element run[i] holds the start index
+ * of i-th sequence in non-descending order.
*/
+ int count = 1;
int[] run = null;
- int high = low + size;
- int count = 1, last = low;
/*
* Identify all possible runs.
*/
- for (int k = low + 1; k < high; ) {
-
+ for (int k = low + 1, last = low; k < high; ) {
/*
- * Find the end index of the current run.
+ * Find the next run.
*/
if (a[k - 1] < a[k]) {
@@ -783,81 +700,79 @@ private static boolean tryMergeRuns(Sorter sorter, int[] a, int low, int size) {
for (int i = last - 1, j = k; ++i < --j && a[i] > a[j]; ) {
int ai = a[i]; a[i] = a[j]; a[j] = ai;
}
+
+ // Check the next sequence
+ if (k < high && a[k - 1] < a[k]) {
+ continue;
+ }
+
} else { // Identify constant sequence
for (int ak = a[k]; ++k < high && ak == a[k]; );
+ // Check the next sequence
if (k < high) {
continue;
}
}
/*
- * Check special cases.
+ * Process the current run.
*/
if (run == null) {
- if (k == high) {
+ if (k == high) {
/*
- * The array is monotonous sequence,
+ * Array is monotonous sequence
* and therefore already sorted.
*/
return true;
}
-
- if (k - low < MIN_FIRST_RUN_SIZE) {
-
- /*
- * The first run is too small
- * to proceed with scanning.
- */
- return false;
- }
-
- run = new int[((size >> 10) | 0x7F) & 0x3FF];
+ run = new int[Math.min((high - low) >> 6, MAX_RUN_CAPACITY) | 8];
run[0] = low;
- } else if (a[last - 1] > a[last]) {
-
- if (count > (k - low) >> MIN_FIRST_RUNS_FACTOR) {
+ } else if (a[last - 1] > a[last]) { // Start the new run
+ if (k - low < count * MIN_RUN_SIZE) {
/*
- * The first runs are not long
- * enough to continue scanning.
+ * Terminate the scanning,
+ * if the runs are too small.
*/
return false;
}
- if (++count == MAX_RUN_CAPACITY) {
-
+ if (++count == run.length) {
/*
* Array is not highly structured.
*/
return false;
}
+ }
- if (count == run.length) {
+ /*
+ * Save the current run.
+ */
+ run[count] = (last = k);
- /*
- * Increase capacity of index array.
- */
- run = Arrays.copyOf(run, count << 1);
- }
+ /*
+ * Check single-element run at the end.
+ */
+ if (++k == high) {
+ --k;
}
- run[count] = (last = k);
}
/*
- * Merge runs of highly structured array.
+ * Merge all runs.
*/
if (count > 1) {
int[] b; int offset = low;
- if (sorter == null || (b = (int[]) sorter.b) == null) {
- b = new int[size];
- } else {
+ if (sorter != null && (b = sorter.b) != null) {
offset = sorter.offset;
+ } else if ((b = tryAllocate(int[].class, high - low)) == null) {
+ return false;
}
- mergeRuns(a, b, offset, 1, sorter != null, run, 0, count);
+ mergeRuns(sorter, a, b, offset, true, run, 0, count);
}
return true;
}
@@ -865,158 +780,240 @@ private static boolean tryMergeRuns(Sorter sorter, int[] a, int low, int size) {
/**
* Merges the specified runs.
*
+ * @param sorter the parallel context
* @param a the source array
- * @param b the temporary buffer used in merging
+ * @param b the buffer for merging
* @param offset the start index in the source, inclusive
- * @param aim specifies merging: to source ( > 0), buffer ( < 0) or any ( == 0)
- * @param parallel indicates whether merging is performed in parallel
+ * @param aim whether the original array is used for merging
* @param run the start indexes of the runs, inclusive
* @param lo the start index of the first run, inclusive
* @param hi the start index of the last run, inclusive
- * @return the destination where runs are merged
*/
- private static int[] mergeRuns(int[] a, int[] b, int offset,
- int aim, boolean parallel, int[] run, int lo, int hi) {
+ private static void mergeRuns(Sorter sorter, int[] a, int[] b, int offset,
+ boolean aim, int[] run, int lo, int hi) {
if (hi - lo == 1) {
- if (aim >= 0) {
- return a;
+ if (!aim) {
+ System.arraycopy(a, run[lo], b, run[lo] - offset, run[hi] - run[lo]);
}
- for (int i = run[hi], j = i - offset, low = run[lo]; i > low;
- b[--j] = a[--i]
- );
- return b;
+ return;
}
/*
- * Split into approximately equal parts.
+ * Split the array into two approximately equal parts.
*/
- int mi = lo, rmi = (run[lo] + run[hi]) >>> 1;
- while (run[++mi + 1] <= rmi);
+ int mi = lo, key = (run[lo] + run[hi]) >>> 1;
+ while (run[++mi + 1] <= key);
/*
- * Merge the left and right parts.
+ * Merge the runs of all parts.
*/
- int[] a1, a2;
-
- if (parallel && hi - lo > MIN_RUN_COUNT) {
- RunMerger merger = new RunMerger(a, b, offset, 0, run, mi, hi).forkMe();
- a1 = mergeRuns(a, b, offset, -aim, true, run, lo, mi);
- a2 = (int[]) merger.getDestination();
- } else {
- a1 = mergeRuns(a, b, offset, -aim, false, run, lo, mi);
- a2 = mergeRuns(a, b, offset, 0, false, run, mi, hi);
- }
+ mergeRuns(sorter, a, b, offset, !aim, run, lo, mi);
+ mergeRuns(sorter, a, b, offset, !aim, run, mi, hi);
- int[] dst = a1 == a ? b : a;
+ int[] dst = aim ? a : b;
+ int[] src = aim ? b : a;
- int k = a1 == a ? run[lo] - offset : run[lo];
- int lo1 = a1 == b ? run[lo] - offset : run[lo];
- int hi1 = a1 == b ? run[mi] - offset : run[mi];
- int lo2 = a2 == b ? run[mi] - offset : run[mi];
- int hi2 = a2 == b ? run[hi] - offset : run[hi];
+ int k = !aim ? run[lo] - offset : run[lo];
+ int lo1 = aim ? run[lo] - offset : run[lo];
+ int hi1 = aim ? run[mi] - offset : run[mi];
+ int lo2 = aim ? run[mi] - offset : run[mi];
+ int hi2 = aim ? run[hi] - offset : run[hi];
- if (parallel) {
- new Merger(null, dst, k, a1, lo1, hi1, a2, lo2, hi2).invoke();
+ /*
+ * Merge the left and right parts.
+ */
+ if (hi1 - lo1 > MIN_PARALLEL_SORT_SIZE && sorter != null) {
+ new Merger<>(null, dst, k, src, lo1, hi1, lo2, hi2).invoke();
} else {
- mergeParts(null, dst, k, a1, lo1, hi1, a2, lo2, hi2);
+ mergeParts(dst, k, src, lo1, hi1, lo2, hi2);
}
- return dst;
}
/**
- * Merges the sorted parts.
+ * Merges the sorted parts in parallel.
*
- * @param merger parallel context
+ * @param merger the parallel context
* @param dst the destination where parts are merged
* @param k the start index of the destination, inclusive
- * @param a1 the first part
+ * @param src the source array
* @param lo1 the start index of the first part, inclusive
* @param hi1 the end index of the first part, exclusive
- * @param a2 the second part
* @param lo2 the start index of the second part, inclusive
* @param hi2 the end index of the second part, exclusive
*/
- private static void mergeParts(Merger merger, int[] dst, int k,
- int[] a1, int lo1, int hi1, int[] a2, int lo2, int hi2) {
-
- if (merger != null && a1 == a2) {
+ private static void mergeParts(Merger merger, int[] dst, int k,
+ int[] src, int lo1, int hi1, int lo2, int hi2) {
- while (true) {
-
- /*
- * The first part must be larger.
- */
- if (hi1 - lo1 < hi2 - lo2) {
- int lo = lo1; lo1 = lo2; lo2 = lo;
- int hi = hi1; hi1 = hi2; hi2 = hi;
- }
+ while (true) {
+ /*
+ * The first part must be larger.
+ */
+ if (hi1 - lo1 < hi2 - lo2) {
+ int lo = lo1; lo1 = lo2; lo2 = lo;
+ int hi = hi1; hi1 = hi2; hi2 = hi;
+ }
- /*
- * Small parts will be merged sequentially.
- */
- if (hi1 - lo1 < MIN_PARALLEL_MERGE_PARTS_SIZE) {
- break;
- }
+ /*
+ * Merge the small parts sequentially.
+ */
+ if (hi1 - lo1 < MIN_PARALLEL_SORT_SIZE) {
+ break;
+ }
- /*
- * Find the median of the larger part.
- */
- int mi1 = (lo1 + hi1) >>> 1;
- int key = a1[mi1];
- int mi2 = hi2;
+ /*
+ * Find the median of the larger part.
+ */
+ int mi1 = (lo1 + hi1) >>> 1;
+ int mi2 = hi2;
+ int key = src[mi1];
- /*
- * Partition the smaller part.
- */
- for (int loo = lo2; loo < mi2; ) {
- int t = (loo + mi2) >>> 1;
+ /*
+ * Split the smaller part.
+ */
+ for (int mi0 = lo2; mi0 < mi2; ) {
+ int mid = (mi0 + mi2) >>> 1;
- if (key > a2[t]) {
- loo = t + 1;
- } else {
- mi2 = t;
- }
+ if (key > src[mid]) {
+ mi0 = mid + 1;
+ } else {
+ mi2 = mid;
}
+ }
- int d = mi2 - lo2 + mi1 - lo1;
+ /*
+ * Merge the first parts in parallel.
+ */
+ merger.fork(k, lo1, mi1, lo2, mi2);
- /*
- * Merge the right sub-parts in parallel.
- */
- merger.forkMerger(dst, k + d, a1, mi1, hi1, a2, mi2, hi2);
+ /*
+ * Reserve space for the second parts.
+ */
+ k += mi2 - lo2 + mi1 - lo1;
- /*
- * Process the sub-left parts.
- */
- hi1 = mi1;
- hi2 = mi2;
- }
+ /*
+ * Iterate along the second parts.
+ */
+ lo1 = mi1;
+ lo2 = mi2;
}
/*
- * Merge small parts sequentially.
+ * Check if the array is already ordered and then merge the parts.
*/
- while (lo1 < hi1 && lo2 < hi2) {
- dst[k++] = a1[lo1] < a2[lo2] ? a1[lo1++] : a2[lo2++];
+ if (lo1 < hi1 && lo2 < hi2 && src[hi1 - 1] > src[lo2]) {
+ mergeParts(dst, k, src, lo1, hi1, lo2, hi2);
+ } else {
+ System.arraycopy(src, lo1, dst, k, hi1 - lo1);
+ System.arraycopy(src, lo2, dst, k + hi1 - lo1, hi2 - lo2);
}
- if (dst != a1 || k < lo1) {
+ }
+
+ /**
+ * Merges the sorted parts sequentially.
+ *
+ * @param dst the destination where parts are merged
+ * @param k the start index of the destination, inclusive
+ * @param src the source array
+ * @param lo1 the start index of the first part, inclusive
+ * @param hi1 the end index of the first part, exclusive
+ * @param lo2 the start index of the second part, inclusive
+ * @param hi2 the end index of the second part, exclusive
+ */
+ private static void mergeParts(int[] dst, int k,
+ int[] src, int lo1, int hi1, int lo2, int hi2) {
+
+ if (src[hi1 - 1] < src[hi2 - 1]) {
while (lo1 < hi1) {
- dst[k++] = a1[lo1++];
+ int next = src[lo1];
+
+ if (next <= src[lo2]) {
+ dst[k++] = src[lo1++];
+ }
+ if (next >= src[lo2]) {
+ dst[k++] = src[lo2++];
+ }
}
- }
- if (dst != a2 || k < lo2) {
+ } else if (src[hi1 - 1] > src[hi2 - 1]) {
while (lo2 < hi2) {
- dst[k++] = a2[lo2++];
+ int next = src[lo1];
+
+ if (next <= src[lo2]) {
+ dst[k++] = src[lo1++];
+ }
+ if (next >= src[lo2]) {
+ dst[k++] = src[lo2++];
+ }
+ }
+ } else {
+ while (lo1 < hi1 && lo2 < hi2) {
+ int next = src[lo1];
+
+ if (next <= src[lo2]) {
+ dst[k++] = src[lo1++];
+ }
+ if (next >= src[lo2]) {
+ dst[k++] = src[lo2++];
+ }
+ }
+ }
+
+ /*
+ * Copy the tail of the left and right parts.
+ */
+ System.arraycopy(src, lo1, dst, k, hi1 - lo1);
+ System.arraycopy(src, lo2, dst, k, hi2 - lo2);
+ }
+
+ /**
+ * Sorts the specified range of the array using heap sort.
+ *
+ * @param a the array to be sorted
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ static void heapSort(int[] a, int low, int high) {
+ for (int k = (low + high) >>> 1; k > low; ) {
+ pushDown(a, --k, a[k], low, high);
+ }
+ while (--high > low) {
+ int max = a[low];
+ pushDown(a, low, a[high], low, high);
+ a[high] = max;
+ }
+ }
+
+ /**
+ * Pushes specified element down during heap sort.
+ *
+ * @param a the given array
+ * @param p the start index
+ * @param value the given element
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ private static void pushDown(int[] a, int p, int value, int low, int high) {
+ for (int k ;; a[p] = a[p = k]) {
+ k = (p << 1) - low + 2; // Index of the right child
+
+ if (k > high) {
+ break;
+ }
+ if (k == high || a[k] < a[k - 1]) {
+ --k;
+ }
+ if (a[k] <= value) {
+ break;
}
}
+ a[p] = value;
}
-// [long]
+// #[long]
/**
* Sorts the specified range of the array using parallel merge
- * sort and/or Dual-Pivot Quicksort.
+ * sort and/or Dual-Pivot Quicksort.
*
* To balance the faster splitting and parallelism of merge sort
* with the faster element partitioning of Quicksort, ranges are
@@ -1030,37 +1027,33 @@ private static void mergeParts(Merger merger, int[] dst, int k,
* @param high the index of the last element, exclusive, to be sorted
*/
static void sort(long[] a, int parallelism, int low, int high) {
- int size = high - low;
-
- if (parallelism > 1 && size > MIN_PARALLEL_SORT_SIZE) {
- int depth = getDepth(parallelism, size >> 12);
- long[] b = depth == 0 ? null : new long[size];
- new Sorter(null, a, b, low, size, low, depth).invoke();
+ if (parallelism > 1 && high - low > MIN_PARALLEL_SORT_SIZE) {
+ new Sorter<>(a, parallelism, low, high - low).invoke();
} else {
sort(null, a, 0, low, high);
}
}
/**
- * Sorts the specified array using the Dual-Pivot Quicksort and/or
- * other sorts in special-cases, possibly with parallel partitions.
+ * Sorts the specified range of the array using Dual-Pivot Quicksort.
*
- * @param sorter parallel context
+ * @param sorter the parallel context
* @param a the array to be sorted
* @param bits the combination of recursion depth and bit flag, where
- * the right bit "0" indicates that array is the leftmost part
+ * the right bit "0" indicates that range is the leftmost part
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
- static void sort(Sorter sorter, long[] a, int bits, int low, int high) {
+ static void sort(Sorter sorter, long[] a, int bits, int low, int high) {
while (true) {
- int end = high - 1, size = high - low;
+ int size = high - low;
/*
- * Run mixed insertion sort on small non-leftmost parts.
+ * Run adaptive mixed insertion sort on small non-leftmost parts.
*/
- if (size < MAX_MIXED_INSERTION_SORT_SIZE + bits && (bits & 1) > 0) {
- sort(long.class, a, Unsafe.ARRAY_LONG_BASE_OFFSET, low, high, DualPivotQuicksort::mixedInsertionSort);
+ if (size < MAX_INSERTION_SORT_SIZE + bits && (bits & 1) > 0) {
+ sort(long.class, a, Unsafe.ARRAY_LONG_BASE_OFFSET,
+ low, high, DualPivotQuicksort::mixedInsertionSort);
return;
}
@@ -1068,33 +1061,25 @@ static void sort(Sorter sorter, long[] a, int bits, int low, int high) {
* Invoke insertion sort on small leftmost part.
*/
if (size < MAX_INSERTION_SORT_SIZE) {
- sort(long.class, a, Unsafe.ARRAY_LONG_BASE_OFFSET, low, high, DualPivotQuicksort::insertionSort);
- return;
- }
-
- /*
- * Check if the whole array or large non-leftmost
- * parts are nearly sorted and then merge runs.
- */
- if ((bits == 0 || size > MIN_TRY_MERGE_SIZE && (bits & 1) > 0)
- && tryMergeRuns(sorter, a, low, size)) {
+ sort(long.class, a, Unsafe.ARRAY_LONG_BASE_OFFSET,
+ low, high, DualPivotQuicksort::insertionSort);
return;
}
/*
- * Switch to heap sort if execution
- * time is becoming quadratic.
+ * Try merging sort on large part.
*/
- if ((bits += DELTA) > MAX_RECURSION_DEPTH) {
- heapSort(a, low, high);
+ if (size > MIN_MERGING_SORT_SIZE * bits
+ && tryMergingSort(sorter, a, low, high)) {
return;
}
/*
- * Use an inexpensive approximation of the golden ratio
- * to select five sample elements and determine pivots.
+ * Divide the given array into the golden ratio using
+ * an inexpensive approximation to select five sample
+ * elements and determine pivots.
*/
- int step = (size >> 3) * 3 + 3;
+ int step = (size >> 2) + (size >> 3) + (size >> 7);
/*
* Five elements around (and including) the central element
@@ -1103,117 +1088,127 @@ && tryMergeRuns(sorter, a, low, size)) {
* determined to work well on a wide variety of inputs.
*/
int e1 = low + step;
- int e5 = end - step;
+ int e5 = high - step;
int e3 = (e1 + e5) >>> 1;
int e2 = (e1 + e3) >>> 1;
int e4 = (e3 + e5) >>> 1;
- long a3 = a[e3];
/*
- * Sort these elements in place by the combination
+ * Sort these elements in-place by the combination
* of 4-element sorting network and insertion sort.
*
- * 5 ------o-----------o------------
- * | |
- * 4 ------|-----o-----o-----o------
- * | | |
- * 2 ------o-----|-----o-----o------
- * | |
- * 1 ------------o-----o------------
- */
- if (a[e5] < a[e2]) { long t = a[e5]; a[e5] = a[e2]; a[e2] = t; }
- if (a[e4] < a[e1]) { long t = a[e4]; a[e4] = a[e1]; a[e1] = t; }
- if (a[e5] < a[e4]) { long t = a[e5]; a[e5] = a[e4]; a[e4] = t; }
- if (a[e2] < a[e1]) { long t = a[e2]; a[e2] = a[e1]; a[e1] = t; }
- if (a[e4] < a[e2]) { long t = a[e4]; a[e4] = a[e2]; a[e2] = t; }
-
- if (a3 < a[e2]) {
- if (a3 < a[e1]) {
- a[e3] = a[e2]; a[e2] = a[e1]; a[e1] = a3;
+ * 1 ---------o---------------o-----------------
+ * | |
+ * 2 ---------|-------o-------o-------o---------
+ * | | |
+ * 3 ---------|-------|---------------|---------
+ * | | |
+ * 4 ---------o-------|-------o-------o---------
+ * | |
+ * 5 -----------------o-------o-----------------
+ */
+ if (a[e1] > a[e4]) { long t = a[e1]; a[e1] = a[e4]; a[e4] = t; }
+ if (a[e2] > a[e5]) { long t = a[e2]; a[e2] = a[e5]; a[e5] = t; }
+ if (a[e4] > a[e5]) { long t = a[e4]; a[e4] = a[e5]; a[e5] = t; }
+ if (a[e1] > a[e2]) { long t = a[e1]; a[e1] = a[e2]; a[e2] = t; }
+ if (a[e2] > a[e4]) { long t = a[e2]; a[e2] = a[e4]; a[e4] = t; }
+
+ /*
+ * Insert the third element.
+ */
+ if (a[e3] < a[e2]) {
+ if (a[e3] < a[e1]) {
+ long t = a[e3]; a[e3] = a[e2]; a[e2] = a[e1]; a[e1] = t;
} else {
- a[e3] = a[e2]; a[e2] = a3;
+ long t = a[e3]; a[e3] = a[e2]; a[e2] = t;
}
- } else if (a3 > a[e4]) {
- if (a3 > a[e5]) {
- a[e3] = a[e4]; a[e4] = a[e5]; a[e5] = a3;
+ } else if (a[e3] > a[e4]) {
+ if (a[e3] > a[e5]) {
+ long t = a[e3]; a[e3] = a[e4]; a[e4] = a[e5]; a[e5] = t;
} else {
- a[e3] = a[e4]; a[e4] = a3;
+ long t = a[e3]; a[e3] = a[e4]; a[e4] = t;
}
}
- // Pointers
- int lower; // The index of the last element of the left part
- int upper; // The index of the first element of the right part
+ /*
+ * Switch to heap sort to avoid quadratic time.
+ */
+ if ((bits += 2) > MAX_RECURSION_DEPTH) {
+ heapSort(a, low, high);
+ return;
+ }
+
+ /*
+ * indices[0] - the index of the last element of the left part
+ * indices[1] - the index of the first element of the right part
+ */
+ int[] indices;
/*
- * Partitioning with 2 pivots in case of different elements.
+ * Partitioning with two pivots on array of fully random elements.
*/
if (a[e1] < a[e2] && a[e2] < a[e3] && a[e3] < a[e4] && a[e4] < a[e5]) {
- /*
- * Use the first and fifth of the five sorted elements as
- * the pivots. These values are inexpensive approximation
- * of tertiles. Note, that pivot1 < pivot2.
- */
- int[] pivotIndices = partition(long.class, a, Unsafe.ARRAY_LONG_BASE_OFFSET, low, high, e1, e5, DualPivotQuicksort::partitionDualPivot);
- lower = pivotIndices[0];
- upper = pivotIndices[1];
+ indices = partition(long.class, a, Unsafe.ARRAY_LONG_BASE_OFFSET,
+ low, high, e1, e5, DualPivotQuicksort::partitionWithTwoPivots);
+
/*
* Sort non-left parts recursively (possibly in parallel),
* excluding known pivots.
*/
if (size > MIN_PARALLEL_SORT_SIZE && sorter != null) {
- sorter.forkSorter(bits | 1, lower + 1, upper);
- sorter.forkSorter(bits | 1, upper + 1, high);
+ sorter.fork(bits | 1, indices[0] + 1, indices[1]);
+ sorter.fork(bits | 1, indices[1] + 1, high);
} else {
- sort(sorter, a, bits | 1, lower + 1, upper);
- sort(sorter, a, bits | 1, upper + 1, high);
+ sort(sorter, a, bits | 1, indices[0] + 1, indices[1]);
+ sort(sorter, a, bits | 1, indices[1] + 1, high);
}
- } else { // Use single pivot in case of many equal elements
+ } else { // Partitioning with one pivot
+
+ indices = partition(long.class, a, Unsafe.ARRAY_LONG_BASE_OFFSET,
+ low, high, e3, e3, DualPivotQuicksort::partitionWithOnePivot);
- /*
- * Use the third of the five sorted elements as the pivot.
- * This value is inexpensive approximation of the median.
- */
- int[] pivotIndices = partition(long.class, a, Unsafe.ARRAY_LONG_BASE_OFFSET, low, high, e3, e3, DualPivotQuicksort::partitionSinglePivot);
- lower = pivotIndices[0];
- upper = pivotIndices[1];
/*
* Sort the right part (possibly in parallel), excluding
* known pivot. All elements from the central part are
* equal and therefore already sorted.
*/
if (size > MIN_PARALLEL_SORT_SIZE && sorter != null) {
- sorter.forkSorter(bits | 1, upper, high);
+ sorter.fork(bits | 1, indices[1], high);
} else {
- sort(sorter, a, bits | 1, upper, high);
+ sort(sorter, a, bits | 1, indices[1], high);
}
}
- high = lower; // Iterate along the left part
+ high = indices[0]; // Iterate along the left part
}
}
/**
- * Partitions the specified range of the array using the two pivots provided.
+ * Partitions the specified range of the array using two given pivots.
*
- * @param array the array to be partitioned
+ * @param a the array for partitioning
* @param low the index of the first element, inclusive, for partitioning
* @param high the index of the last element, exclusive, for partitioning
* @param pivotIndex1 the index of pivot1, the first pivot
* @param pivotIndex2 the index of pivot2, the second pivot
- *
+ * @return indices of parts after partitioning
*/
- @ForceInline
- private static int[] partitionDualPivot(long[] a, int low, int high, int pivotIndex1, int pivotIndex2) {
- int end = high - 1;
+ private static int[] partitionWithTwoPivots(
+ long[] a, int low, int high, int pivotIndex1, int pivotIndex2) {
+ /*
+ * Pointers to the right and left parts.
+ */
+ int upper = --high;
int lower = low;
- int upper = end;
- int e1 = pivotIndex1;
- int e5 = pivotIndex2;
- long pivot1 = a[e1];
- long pivot2 = a[e5];
+ /*
+ * Use the first and fifth of the five sorted elements as
+ * the pivots. These values are inexpensive approximation
+ * of tertiles. Note, that pivot1 < pivot2.
+ */
+ long pivot1 = a[pivotIndex1];
+ long pivot2 = a[pivotIndex2];
/*
* The first and the last elements to be sorted are moved
@@ -1222,8 +1217,8 @@ private static int[] partitionDualPivot(long[] a, int low, int high, int pivotIn
* into their final positions, and excluded from the next
* subsequent sorting.
*/
- a[e1] = a[lower];
- a[e5] = a[upper];
+ a[pivotIndex1] = a[lower];
+ a[pivotIndex2] = a[upper];
/*
* Skip elements, which are less or greater than the pivots.
@@ -1234,39 +1229,36 @@ private static int[] partitionDualPivot(long[] a, int low, int high, int pivotIn
/*
* Backward 3-interval partitioning
*
- * left part central part right part
- * +------------------------------------------------------------+
- * | < pivot1 | ? | pivot1 <= && <= pivot2 | > pivot2 |
- * +------------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * lower k upper
- *
- * Invariants:
- *
- * all in (low, lower] < pivot1
- * pivot1 <= all in (k, upper) <= pivot2
- * all in [upper, end) > pivot2
+ * left part central part right part
+ * +--------------+----------+--------------------------+--------------+
+ * | < pivot1 | ? | pivot1 <= .. <= pivot2 | > pivot2 |
+ * +--------------+----------+--------------------------+--------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
*
* Pointer k is the last index of ?-part
+ * Pointer lower is the last index of left part
+ * Pointer upper is the first index of right part
*/
for (int unused = --lower, k = ++upper; --k > lower; ) {
long ak = a[k];
- if (ak < pivot1) { // Move a[k] to the left side
- while (lower < k) {
- if (a[++lower] >= pivot1) {
- if (a[lower] > pivot2) {
- a[k] = a[--upper];
- a[upper] = a[lower];
- } else {
- a[k] = a[lower];
- }
- a[lower] = ak;
- break;
- }
+ if (ak < pivot1) { // Move a[k] to the left part
+ while (a[++lower] < pivot1);
+
+ if (lower > k) {
+ lower = k;
+ break;
+ }
+ if (a[lower] > pivot2) {
+ a[k] = a[--upper];
+ a[upper] = a[lower];
+ } else {
+ a[k] = a[lower];
}
- } else if (ak > pivot2) { // Move a[k] to the right side
+ a[lower] = ak;
+ } else if (ak > pivot2) { // Move a[k] to the right part
a[k] = a[--upper];
a[upper] = ak;
}
@@ -1275,31 +1267,35 @@ private static int[] partitionDualPivot(long[] a, int low, int high, int pivotIn
/*
* Swap the pivots into their final positions.
*/
- a[low] = a[lower]; a[lower] = pivot1;
- a[end] = a[upper]; a[upper] = pivot2;
+ a[low] = a[lower]; a[lower] = pivot1;
+ a[high] = a[upper]; a[upper] = pivot2;
- return new int[] {lower, upper};
+ return new int[] { lower, upper };
}
/**
- * Partitions the specified range of the array using a single pivot provided.
+ * Partitions the specified range of the array using one given pivot.
*
- * @param array the array to be partitioned
+ * @param a the array for partitioning
* @param low the index of the first element, inclusive, for partitioning
* @param high the index of the last element, exclusive, for partitioning
- * @param pivotIndex1 the index of pivot1, the first pivot
- * @param pivotIndex2 the index of pivot2, the second pivot
- *
+ * @param pivotIndex1 the index of single pivot
+ * @param pivotIndex2 the index of single pivot
+ * @return indices of parts after partitioning
*/
- @ForceInline
- private static int[] partitionSinglePivot(long[] a, int low, int high, int pivotIndex1, int pivotIndex2) {
-
- int end = high - 1;
+ private static int[] partitionWithOnePivot(
+ long[] a, int low, int high, int pivotIndex1, int pivotIndex2) {
+ /*
+ * Pointers to the right and left parts.
+ */
+ int upper = high;
int lower = low;
- int upper = end;
- int e3 = pivotIndex1;
- long pivot = a[e3];
+ /*
+ * Use the third of the five sorted elements as the pivot.
+ * This value is inexpensive approximation of the median.
+ */
+ long pivot = a[pivotIndex1];
/*
* The first element to be sorted is moved to the
@@ -1308,43 +1304,40 @@ private static int[] partitionSinglePivot(long[] a, int low, int high, int pivot
* back into its final position, and excluded from
* the next subsequent sorting.
*/
- a[e3] = a[lower];
+ a[pivotIndex1] = a[lower];
/*
- * Traditional 3-way (Dutch National Flag) partitioning
+ * Dutch National Flag partitioning
*
- * left part central part right part
- * +------------------------------------------------------+
- * | < pivot | ? | == pivot | > pivot |
- * +------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * lower k upper
- *
- * Invariants:
- *
- * all in (low, lower] < pivot
- * all in (k, upper) == pivot
- * all in [upper, end] > pivot
+ * left part central part right part
+ * +--------------+----------+--------------+-------------+
+ * | < pivot | ? | == pivot | > pivot |
+ * +--------------+----------+--------------+-------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
*
* Pointer k is the last index of ?-part
+ * Pointer lower is the last index of left part
+ * Pointer upper is the first index of right part
*/
- for (int k = ++upper; --k > lower; ) {
+ for (int k = upper; --k > lower; ) {
long ak = a[k];
- if (ak != pivot) {
- a[k] = pivot;
+ if (ak == pivot) {
+ continue;
+ }
+ a[k] = pivot;
- if (ak < pivot) { // Move a[k] to the left side
- while (a[++lower] < pivot);
+ if (ak < pivot) { // Move a[k] to the left part
+ while (a[++lower] < pivot);
- if (a[lower] > pivot) {
- a[--upper] = a[lower];
- }
- a[lower] = ak;
- } else { // ak > pivot - Move a[k] to the right side
- a[--upper] = ak;
+ if (a[lower] > pivot) {
+ a[--upper] = a[lower];
}
+ a[lower] = ak;
+ } else { // ak > pivot - Move a[k] to the right part
+ a[--upper] = ak;
}
}
@@ -1352,14 +1345,15 @@ private static int[] partitionSinglePivot(long[] a, int low, int high, int pivot
* Swap the pivot into its final position.
*/
a[low] = a[lower]; a[lower] = pivot;
- return new int[] {lower, upper};
+
+ return new int[] { lower, upper };
}
/**
- * Sorts the specified range of the array using mixed insertion sort.
+ * Sorts the specified range of the array using mixed insertion sort.
*
- * Mixed insertion sort is combination of simple insertion sort,
- * pin insertion sort and pair insertion sort.
+ * Mixed insertion sort is combination of pin insertion sort,
+ * simple insertion sort and pair insertion sort.
*
* In the context of Dual-Pivot Quicksort, the pivot element
* from the left part plays the role of sentinel, because it
@@ -1371,110 +1365,84 @@ private static int[] partitionSinglePivot(long[] a, int low, int high, int pivot
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
- private static void mixedInsertionSort(long[] a, int low, int high) {
- int size = high - low;
- int end = high - 3 * ((size >> 5) << 3);
- if (end == high) {
+ static void mixedInsertionSort(long[] a, int low, int high) {
+ /*
+ * Split part for pin and pair insertion sorts.
+ */
+ int end = high - 3 * ((high - low) >> 3 << 1);
- /*
- * Invoke simple insertion sort on tiny array.
- */
- for (int i; ++low < end; ) {
+ /*
+ * Invoke simple insertion sort on small part.
+ */
+ if (end == high) {
+ for (int i; ++low < high; ) {
long ai = a[i = low];
- while (ai < a[--i]) {
- a[i + 1] = a[i];
+ while (ai < a[i - 1]) {
+ a[i] = a[--i];
}
- a[i + 1] = ai;
+ a[i] = ai;
}
- } else {
+ return;
+ }
+
+ /*
+ * Start with pin insertion sort.
+ */
+ for (int i, p = high; ++low < end; ) {
+ long ai = a[i = low], pin = a[--p];
/*
- * Start with pin insertion sort on small part.
- *
- * Pin insertion sort is extended simple insertion sort.
- * The main idea of this sort is to put elements larger
- * than an element called pin to the end of array (the
- * proper area for such elements). It avoids expensive
- * movements of these elements through the whole array.
+ * Swap larger element with pin.
*/
- long pin = a[end];
-
- for (int i, p = high; ++low < end; ) {
- long ai = a[i = low];
-
- if (ai < a[i - 1]) { // Small element
-
- /*
- * Insert small element into sorted part.
- */
- a[i] = a[--i];
-
- while (ai < a[--i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = ai;
-
- } else if (p > i && ai > pin) { // Large element
-
- /*
- * Find element smaller than pin.
- */
- while (a[--p] > pin);
-
- /*
- * Swap it with large element.
- */
- if (p > i) {
- ai = a[p];
- a[p] = a[i];
- }
-
- /*
- * Insert small element into sorted part.
- */
- while (ai < a[--i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = ai;
- }
+ if (ai > pin) {
+ ai = pin;
+ a[p] = a[i];
}
/*
- * Continue with pair insertion sort on remain part.
+ * Insert element into sorted part.
*/
- for (int i; low < high; ++low) {
- long a1 = a[i = low], a2 = a[++low];
+ while (ai < a[i - 1]) {
+ a[i] = a[--i];
+ }
+ a[i] = ai;
+ }
- /*
- * Insert two elements per iteration: at first, insert the
- * larger element and then insert the smaller element, but
- * from the position where the larger element was inserted.
- */
- if (a1 > a2) {
+ /*
+ * Finish with pair insertion sort.
+ */
+ for (int i; low < high; ++low) {
+ long a1 = a[i = low], a2 = a[++low];
- while (a1 < a[--i]) {
- a[i + 2] = a[i];
- }
- a[++i + 1] = a1;
+ /*
+ * Insert two elements per iteration: at first, insert the
+ * larger element and then insert the smaller element, but
+ * from the position where the larger element was inserted.
+ */
+ if (a1 > a2) {
- while (a2 < a[--i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = a2;
+ while (a1 < a[--i]) {
+ a[i + 2] = a[i];
+ }
+ a[++i + 1] = a1;
- } else if (a1 < a[i - 1]) {
+ while (a2 < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = a2;
- while (a2 < a[--i]) {
- a[i + 2] = a[i];
- }
- a[++i + 1] = a2;
+ } else if (a1 < a[i - 1]) {
- while (a1 < a[--i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = a1;
+ while (a2 < a[--i]) {
+ a[i + 2] = a[i];
}
+ a[++i + 1] = a2;
+
+ while (a1 < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = a1;
}
}
}
@@ -1486,90 +1454,43 @@ private static void mixedInsertionSort(long[] a, int low, int high) {
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
- private static void insertionSort(long[] a, int low, int high) {
+ static void insertionSort(long[] a, int low, int high) {
for (int i, k = low; ++k < high; ) {
long ai = a[i = k];
if (ai < a[i - 1]) {
- while (--i >= low && ai < a[i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = ai;
+ do {
+ a[i] = a[--i];
+ } while (i > low && ai < a[i - 1]);
+
+ a[i] = ai;
}
}
}
/**
- * Sorts the specified range of the array using heap sort.
+ * Tries to sort the specified range of the array using merging sort.
*
+ * @param sorter the parallel context
* @param a the array to be sorted
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
+ * @return {@code true} if the array is finally sorted, otherwise {@code false}
*/
- private static void heapSort(long[] a, int low, int high) {
- for (int k = (low + high) >>> 1; k > low; ) {
- pushDown(a, --k, a[k], low, high);
- }
- while (--high > low) {
- long max = a[low];
- pushDown(a, low, a[high], low, high);
- a[high] = max;
- }
- }
-
- /**
- * Pushes specified element down during heap sort.
- *
- * @param a the given array
- * @param p the start index
- * @param value the given element
- * @param low the index of the first element, inclusive, to be sorted
- * @param high the index of the last element, exclusive, to be sorted
- */
- private static void pushDown(long[] a, int p, long value, int low, int high) {
- for (int k ;; a[p] = a[p = k]) {
- k = (p << 1) - low + 2; // Index of the right child
-
- if (k > high) {
- break;
- }
- if (k == high || a[k] < a[k - 1]) {
- --k;
- }
- if (a[k] <= value) {
- break;
- }
- }
- a[p] = value;
- }
-
- /**
- * Tries to sort the specified range of the array.
- *
- * @param sorter parallel context
- * @param a the array to be sorted
- * @param low the index of the first element to be sorted
- * @param size the array size
- * @return true if finally sorted, false otherwise
- */
- private static boolean tryMergeRuns(Sorter sorter, long[] a, int low, int size) {
-
+ static boolean tryMergingSort(Sorter sorter, long[] a, int low, int high) {
/*
- * The run array is constructed only if initial runs are
- * long enough to continue, run[i] then holds start index
- * of the i-th sequence of elements in non-descending order.
+ * The element run[i] holds the start index
+ * of i-th sequence in non-descending order.
*/
+ int count = 1;
int[] run = null;
- int high = low + size;
- int count = 1, last = low;
/*
* Identify all possible runs.
*/
- for (int k = low + 1; k < high; ) {
-
+ for (int k = low + 1, last = low; k < high; ) {
/*
- * Find the end index of the current run.
+ * Find the next run.
*/
if (a[k - 1] < a[k]) {
@@ -1585,81 +1506,79 @@ private static boolean tryMergeRuns(Sorter sorter, long[] a, int low, int size)
for (int i = last - 1, j = k; ++i < --j && a[i] > a[j]; ) {
long ai = a[i]; a[i] = a[j]; a[j] = ai;
}
+
+ // Check the next sequence
+ if (k < high && a[k - 1] < a[k]) {
+ continue;
+ }
+
} else { // Identify constant sequence
for (long ak = a[k]; ++k < high && ak == a[k]; );
+ // Check the next sequence
if (k < high) {
continue;
}
}
/*
- * Check special cases.
+ * Process the current run.
*/
if (run == null) {
- if (k == high) {
+ if (k == high) {
/*
- * The array is monotonous sequence,
+ * Array is monotonous sequence
* and therefore already sorted.
*/
return true;
}
-
- if (k - low < MIN_FIRST_RUN_SIZE) {
-
- /*
- * The first run is too small
- * to proceed with scanning.
- */
- return false;
- }
-
- run = new int[((size >> 10) | 0x7F) & 0x3FF];
+ run = new int[Math.min((high - low) >> 6, MAX_RUN_CAPACITY) | 8];
run[0] = low;
- } else if (a[last - 1] > a[last]) {
-
- if (count > (k - low) >> MIN_FIRST_RUNS_FACTOR) {
+ } else if (a[last - 1] > a[last]) { // Start the new run
+ if (k - low < count * MIN_RUN_SIZE) {
/*
- * The first runs are not long
- * enough to continue scanning.
+ * Terminate the scanning,
+ * if the runs are too small.
*/
return false;
}
- if (++count == MAX_RUN_CAPACITY) {
-
+ if (++count == run.length) {
/*
* Array is not highly structured.
*/
return false;
}
+ }
- if (count == run.length) {
-
- /*
- * Increase capacity of index array.
- */
- run = Arrays.copyOf(run, count << 1);
- }
- }
+ /*
+ * Save the current run.
+ */
run[count] = (last = k);
+
+ /*
+ * Check single-element run at the end.
+ */
+ if (++k == high) {
+ --k;
+ }
}
/*
- * Merge runs of highly structured array.
+ * Merge all runs.
*/
if (count > 1) {
long[] b; int offset = low;
- if (sorter == null || (b = (long[]) sorter.b) == null) {
- b = new long[size];
- } else {
+ if (sorter != null && (b = sorter.b) != null) {
offset = sorter.offset;
+ } else if ((b = tryAllocate(long[].class, high - low)) == null) {
+ return false;
}
- mergeRuns(a, b, offset, 1, sorter != null, run, 0, count);
+ mergeRuns(sorter, a, b, offset, true, run, 0, count);
}
return true;
}
@@ -1667,168 +1586,249 @@ private static boolean tryMergeRuns(Sorter sorter, long[] a, int low, int size)
/**
* Merges the specified runs.
*
+ * @param sorter the parallel context
* @param a the source array
- * @param b the temporary buffer used in merging
+ * @param b the buffer for merging
* @param offset the start index in the source, inclusive
- * @param aim specifies merging: to source ( > 0), buffer ( < 0) or any ( == 0)
- * @param parallel indicates whether merging is performed in parallel
+ * @param aim whether the original array is used for merging
* @param run the start indexes of the runs, inclusive
* @param lo the start index of the first run, inclusive
* @param hi the start index of the last run, inclusive
- * @return the destination where runs are merged
*/
- private static long[] mergeRuns(long[] a, long[] b, int offset,
- int aim, boolean parallel, int[] run, int lo, int hi) {
+ private static void mergeRuns(Sorter sorter, long[] a, long[] b, int offset,
+ boolean aim, int[] run, int lo, int hi) {
if (hi - lo == 1) {
- if (aim >= 0) {
- return a;
+ if (!aim) {
+ System.arraycopy(a, run[lo], b, run[lo] - offset, run[hi] - run[lo]);
}
- for (int i = run[hi], j = i - offset, low = run[lo]; i > low;
- b[--j] = a[--i]
- );
- return b;
+ return;
}
/*
- * Split into approximately equal parts.
+ * Split the array into two approximately equal parts.
*/
- int mi = lo, rmi = (run[lo] + run[hi]) >>> 1;
- while (run[++mi + 1] <= rmi);
+ int mi = lo, key = (run[lo] + run[hi]) >>> 1;
+ while (run[++mi + 1] <= key);
/*
- * Merge the left and right parts.
+ * Merge the runs of all parts.
*/
- long[] a1, a2;
+ mergeRuns(sorter, a, b, offset, !aim, run, lo, mi);
+ mergeRuns(sorter, a, b, offset, !aim, run, mi, hi);
- if (parallel && hi - lo > MIN_RUN_COUNT) {
- RunMerger merger = new RunMerger(a, b, offset, 0, run, mi, hi).forkMe();
- a1 = mergeRuns(a, b, offset, -aim, true, run, lo, mi);
- a2 = (long[]) merger.getDestination();
- } else {
- a1 = mergeRuns(a, b, offset, -aim, false, run, lo, mi);
- a2 = mergeRuns(a, b, offset, 0, false, run, mi, hi);
- }
+ long[] dst = aim ? a : b;
+ long[] src = aim ? b : a;
- long[] dst = a1 == a ? b : a;
+ int k = !aim ? run[lo] - offset : run[lo];
+ int lo1 = aim ? run[lo] - offset : run[lo];
+ int hi1 = aim ? run[mi] - offset : run[mi];
+ int lo2 = aim ? run[mi] - offset : run[mi];
+ int hi2 = aim ? run[hi] - offset : run[hi];
- int k = a1 == a ? run[lo] - offset : run[lo];
- int lo1 = a1 == b ? run[lo] - offset : run[lo];
- int hi1 = a1 == b ? run[mi] - offset : run[mi];
- int lo2 = a2 == b ? run[mi] - offset : run[mi];
- int hi2 = a2 == b ? run[hi] - offset : run[hi];
-
- if (parallel) {
- new Merger(null, dst, k, a1, lo1, hi1, a2, lo2, hi2).invoke();
+ /*
+ * Merge the left and right parts.
+ */
+ if (hi1 - lo1 > MIN_PARALLEL_SORT_SIZE && sorter != null) {
+ new Merger<>(null, dst, k, src, lo1, hi1, lo2, hi2).invoke();
} else {
- mergeParts(null, dst, k, a1, lo1, hi1, a2, lo2, hi2);
+ mergeParts(dst, k, src, lo1, hi1, lo2, hi2);
}
- return dst;
}
/**
- * Merges the sorted parts.
+ * Merges the sorted parts in parallel.
*
- * @param merger parallel context
+ * @param merger the parallel context
* @param dst the destination where parts are merged
* @param k the start index of the destination, inclusive
- * @param a1 the first part
+ * @param src the source array
* @param lo1 the start index of the first part, inclusive
* @param hi1 the end index of the first part, exclusive
- * @param a2 the second part
* @param lo2 the start index of the second part, inclusive
* @param hi2 the end index of the second part, exclusive
*/
- private static void mergeParts(Merger merger, long[] dst, int k,
- long[] a1, int lo1, int hi1, long[] a2, int lo2, int hi2) {
+ private static void mergeParts(Merger merger, long[] dst, int k,
+ long[] src, int lo1, int hi1, int lo2, int hi2) {
- if (merger != null && a1 == a2) {
+ while (true) {
+ /*
+ * The first part must be larger.
+ */
+ if (hi1 - lo1 < hi2 - lo2) {
+ int lo = lo1; lo1 = lo2; lo2 = lo;
+ int hi = hi1; hi1 = hi2; hi2 = hi;
+ }
- while (true) {
+ /*
+ * Merge the small parts sequentially.
+ */
+ if (hi1 - lo1 < MIN_PARALLEL_SORT_SIZE) {
+ break;
+ }
- /*
- * The first part must be larger.
- */
- if (hi1 - lo1 < hi2 - lo2) {
- int lo = lo1; lo1 = lo2; lo2 = lo;
- int hi = hi1; hi1 = hi2; hi2 = hi;
- }
+ /*
+ * Find the median of the larger part.
+ */
+ int mi1 = (lo1 + hi1) >>> 1;
+ int mi2 = hi2;
+ long key = src[mi1];
- /*
- * Small parts will be merged sequentially.
- */
- if (hi1 - lo1 < MIN_PARALLEL_MERGE_PARTS_SIZE) {
- break;
+ /*
+ * Split the smaller part.
+ */
+ for (int mi0 = lo2; mi0 < mi2; ) {
+ int mid = (mi0 + mi2) >>> 1;
+
+ if (key > src[mid]) {
+ mi0 = mid + 1;
+ } else {
+ mi2 = mid;
}
+ }
- /*
- * Find the median of the larger part.
- */
- int mi1 = (lo1 + hi1) >>> 1;
- long key = a1[mi1];
- int mi2 = hi2;
+ /*
+ * Merge the first parts in parallel.
+ */
+ merger.fork(k, lo1, mi1, lo2, mi2);
- /*
- * Partition the smaller part.
- */
- for (int loo = lo2; loo < mi2; ) {
- int t = (loo + mi2) >>> 1;
+ /*
+ * Reserve space for the second parts.
+ */
+ k += mi2 - lo2 + mi1 - lo1;
- if (key > a2[t]) {
- loo = t + 1;
- } else {
- mi2 = t;
- }
- }
+ /*
+ * Iterate along the second parts.
+ */
+ lo1 = mi1;
+ lo2 = mi2;
+ }
+
+ /*
+ * Check if the array is already ordered and then merge the parts.
+ */
+ if (lo1 < hi1 && lo2 < hi2 && src[hi1 - 1] > src[lo2]) {
+ mergeParts(dst, k, src, lo1, hi1, lo2, hi2);
+ } else {
+ System.arraycopy(src, lo1, dst, k, hi1 - lo1);
+ System.arraycopy(src, lo2, dst, k + hi1 - lo1, hi2 - lo2);
+ }
+ }
+
+ /**
+ * Merges the sorted parts sequentially.
+ *
+ * @param dst the destination where parts are merged
+ * @param k the start index of the destination, inclusive
+ * @param src the source array
+ * @param lo1 the start index of the first part, inclusive
+ * @param hi1 the end index of the first part, exclusive
+ * @param lo2 the start index of the second part, inclusive
+ * @param hi2 the end index of the second part, exclusive
+ */
+ private static void mergeParts(long[] dst, int k,
+ long[] src, int lo1, int hi1, int lo2, int hi2) {
- int d = mi2 - lo2 + mi1 - lo1;
+ if (src[hi1 - 1] < src[hi2 - 1]) {
+ while (lo1 < hi1) {
+ long next = src[lo1];
- /*
- * Merge the right sub-parts in parallel.
- */
- merger.forkMerger(dst, k + d, a1, mi1, hi1, a2, mi2, hi2);
+ if (next <= src[lo2]) {
+ dst[k++] = src[lo1++];
+ }
+ if (next >= src[lo2]) {
+ dst[k++] = src[lo2++];
+ }
+ }
+ } else if (src[hi1 - 1] > src[hi2 - 1]) {
+ while (lo2 < hi2) {
+ long next = src[lo1];
- /*
- * Process the sub-left parts.
- */
- hi1 = mi1;
- hi2 = mi2;
+ if (next <= src[lo2]) {
+ dst[k++] = src[lo1++];
+ }
+ if (next >= src[lo2]) {
+ dst[k++] = src[lo2++];
+ }
+ }
+ } else {
+ while (lo1 < hi1 && lo2 < hi2) {
+ long next = src[lo1];
+
+ if (next <= src[lo2]) {
+ dst[k++] = src[lo1++];
+ }
+ if (next >= src[lo2]) {
+ dst[k++] = src[lo2++];
+ }
}
}
/*
- * Merge small parts sequentially.
+ * Copy the tail of the left and right parts.
*/
- while (lo1 < hi1 && lo2 < hi2) {
- dst[k++] = a1[lo1] < a2[lo2] ? a1[lo1++] : a2[lo2++];
+ System.arraycopy(src, lo1, dst, k, hi1 - lo1);
+ System.arraycopy(src, lo2, dst, k, hi2 - lo2);
+ }
+
+ /**
+ * Sorts the specified range of the array using heap sort.
+ *
+ * @param a the array to be sorted
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ static void heapSort(long[] a, int low, int high) {
+ for (int k = (low + high) >>> 1; k > low; ) {
+ pushDown(a, --k, a[k], low, high);
}
- if (dst != a1 || k < lo1) {
- while (lo1 < hi1) {
- dst[k++] = a1[lo1++];
- }
+ while (--high > low) {
+ long max = a[low];
+ pushDown(a, low, a[high], low, high);
+ a[high] = max;
}
- if (dst != a2 || k < lo2) {
- while (lo2 < hi2) {
- dst[k++] = a2[lo2++];
+ }
+
+ /**
+ * Pushes specified element down during heap sort.
+ *
+ * @param a the given array
+ * @param p the start index
+ * @param value the given element
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ private static void pushDown(long[] a, int p, long value, int low, int high) {
+ for (int k ;; a[p] = a[p = k]) {
+ k = (p << 1) - low + 2; // Index of the right child
+
+ if (k > high) {
+ break;
+ }
+ if (k == high || a[k] < a[k - 1]) {
+ --k;
+ }
+ if (a[k] <= value) {
+ break;
}
}
+ a[p] = value;
}
-// [byte]
+// #[byte]
/**
- * Sorts the specified range of the array using
- * counting sort or insertion sort.
+ * Sorts the specified range of the array using insertion sort or counting sort.
*
* @param a the array to be sorted
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
static void sort(byte[] a, int low, int high) {
- if (high - low > MIN_BYTE_COUNTING_SORT_SIZE) {
- countingSort(a, low, high);
- } else {
+ if (high - low < MAX_INSERTION_SORT_SIZE) {
insertionSort(a, low, high);
+ } else {
+ countingSort(a, low, high);
}
}
@@ -1839,29 +1839,20 @@ static void sort(byte[] a, int low, int high) {
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
- private static void insertionSort(byte[] a, int low, int high) {
+ static void insertionSort(byte[] a, int low, int high) {
for (int i, k = low; ++k < high; ) {
byte ai = a[i = k];
if (ai < a[i - 1]) {
- while (--i >= low && ai < a[i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = ai;
+ do {
+ a[i] = a[--i];
+ } while (i > low && ai < a[i - 1]);
+
+ a[i] = ai;
}
}
}
- /**
- * The number of distinct byte values.
- */
- private static final int NUM_BYTE_VALUES = 1 << 8;
-
- /**
- * Max index of byte counter.
- */
- private static final int MAX_BYTE_INDEX = Byte.MAX_VALUE + NUM_BYTE_VALUES + 1;
-
/**
* Sorts the specified range of the array using counting sort.
*
@@ -1869,51 +1860,42 @@ private static void insertionSort(byte[] a, int low, int high) {
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
- private static void countingSort(byte[] a, int low, int high) {
- int[] count = new int[NUM_BYTE_VALUES];
+ static void countingSort(byte[] a, int low, int high) {
+ /*
+ * Count the number of all values.
+ */
+ int[] count = new int[1 << 8];
/*
- * Compute a histogram with the number of each values.
+ * Compute the histogram.
*/
for (int i = high; i > low; ++count[a[--i] & 0xFF]);
/*
* Place values on their final positions.
*/
- if (high - low > NUM_BYTE_VALUES) {
- for (int i = MAX_BYTE_INDEX; --i > Byte.MAX_VALUE; ) {
- int value = i & 0xFF;
+ for (int value = Byte.MIN_VALUE; high > low; ) {
+ while (count[--value & 0xFF] == 0);
+ int num = count[value & 0xFF];
- for (low = high - count[value]; high > low;
- a[--high] = (byte) value
- );
- }
- } else {
- for (int i = MAX_BYTE_INDEX; high > low; ) {
- while (count[--i & 0xFF] == 0);
-
- int value = i & 0xFF;
- int c = count[value];
-
- do {
- a[--high] = (byte) value;
- } while (--c > 0);
- }
+ do {
+ a[--high] = (byte) value;
+ } while (--num > 0);
}
}
-// [char]
+// #[char]
/**
- * Sorts the specified range of the array using
- * counting sort or Dual-Pivot Quicksort.
+ * Sorts the specified range of the array using counting sort
+ * Dual-Pivot Quicksort.
*
* @param a the array to be sorted
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
static void sort(char[] a, int low, int high) {
- if (high - low > MIN_SHORT_OR_CHAR_COUNTING_SORT_SIZE) {
+ if (high - low > MIN_COUNTING_SORT_SIZE) {
countingSort(a, low, high);
} else {
sort(a, 0, low, high);
@@ -1921,21 +1903,20 @@ static void sort(char[] a, int low, int high) {
}
/**
- * Sorts the specified array using the Dual-Pivot Quicksort and/or
- * other sorts in special-cases, possibly with parallel partitions.
+ * Sorts the specified range of the array using Dual-Pivot Quicksort.
*
* @param a the array to be sorted
* @param bits the combination of recursion depth and bit flag, where
- * the right bit "0" indicates that array is the leftmost part
+ * the right bit "0" indicates that range is the leftmost part
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
- static void sort(char[] a, int bits, int low, int high) {
+ private static void sort(char[] a, int bits, int low, int high) {
while (true) {
- int end = high - 1, size = high - low;
+ int size = high - low;
/*
- * Invoke insertion sort on small leftmost part.
+ * Invoke insertion sort on small part.
*/
if (size < MAX_INSERTION_SORT_SIZE) {
insertionSort(a, low, high);
@@ -1943,19 +1924,11 @@ static void sort(char[] a, int bits, int low, int high) {
}
/*
- * Switch to counting sort if execution
- * time is becoming quadratic.
+ * Divide the given array into the golden ratio using
+ * an inexpensive approximation to select five sample
+ * elements and determine pivots.
*/
- if ((bits += DELTA) > MAX_RECURSION_DEPTH) {
- countingSort(a, low, high);
- return;
- }
-
- /*
- * Use an inexpensive approximation of the golden ratio
- * to select five sample elements and determine pivots.
- */
- int step = (size >> 3) * 3 + 3;
+ int step = (size >> 2) + (size >> 3) + (size >> 7);
/*
* Five elements around (and including) the central element
@@ -1964,200 +1937,253 @@ static void sort(char[] a, int bits, int low, int high) {
* determined to work well on a wide variety of inputs.
*/
int e1 = low + step;
- int e5 = end - step;
+ int e5 = high - step;
int e3 = (e1 + e5) >>> 1;
int e2 = (e1 + e3) >>> 1;
int e4 = (e3 + e5) >>> 1;
- char a3 = a[e3];
/*
- * Sort these elements in place by the combination
+ * Sort these elements in-place by the combination
* of 4-element sorting network and insertion sort.
*
- * 5 ------o-----------o------------
- * | |
- * 4 ------|-----o-----o-----o------
- * | | |
- * 2 ------o-----|-----o-----o------
- * | |
- * 1 ------------o-----o------------
- */
- if (a[e5] < a[e2]) { char t = a[e5]; a[e5] = a[e2]; a[e2] = t; }
- if (a[e4] < a[e1]) { char t = a[e4]; a[e4] = a[e1]; a[e1] = t; }
- if (a[e5] < a[e4]) { char t = a[e5]; a[e5] = a[e4]; a[e4] = t; }
- if (a[e2] < a[e1]) { char t = a[e2]; a[e2] = a[e1]; a[e1] = t; }
- if (a[e4] < a[e2]) { char t = a[e4]; a[e4] = a[e2]; a[e2] = t; }
-
- if (a3 < a[e2]) {
- if (a3 < a[e1]) {
- a[e3] = a[e2]; a[e2] = a[e1]; a[e1] = a3;
+ * 1 ---------o---------------o-----------------
+ * | |
+ * 2 ---------|-------o-------o-------o---------
+ * | | |
+ * 3 ---------|-------|---------------|---------
+ * | | |
+ * 4 ---------o-------|-------o-------o---------
+ * | |
+ * 5 -----------------o-------o-----------------
+ */
+ if (a[e1] > a[e4]) { char t = a[e1]; a[e1] = a[e4]; a[e4] = t; }
+ if (a[e2] > a[e5]) { char t = a[e2]; a[e2] = a[e5]; a[e5] = t; }
+ if (a[e4] > a[e5]) { char t = a[e4]; a[e4] = a[e5]; a[e5] = t; }
+ if (a[e1] > a[e2]) { char t = a[e1]; a[e1] = a[e2]; a[e2] = t; }
+ if (a[e2] > a[e4]) { char t = a[e2]; a[e2] = a[e4]; a[e4] = t; }
+
+ /*
+ * Insert the third element.
+ */
+ if (a[e3] < a[e2]) {
+ if (a[e3] < a[e1]) {
+ char t = a[e3]; a[e3] = a[e2]; a[e2] = a[e1]; a[e1] = t;
} else {
- a[e3] = a[e2]; a[e2] = a3;
+ char t = a[e3]; a[e3] = a[e2]; a[e2] = t;
}
- } else if (a3 > a[e4]) {
- if (a3 > a[e5]) {
- a[e3] = a[e4]; a[e4] = a[e5]; a[e5] = a3;
+ } else if (a[e3] > a[e4]) {
+ if (a[e3] > a[e5]) {
+ char t = a[e3]; a[e3] = a[e4]; a[e4] = a[e5]; a[e5] = t;
} else {
- a[e3] = a[e4]; a[e4] = a3;
+ char t = a[e3]; a[e3] = a[e4]; a[e4] = t;
}
}
- // Pointers
- int lower = low; // The index of the last element of the left part
- int upper = end; // The index of the first element of the right part
+ /*
+ * Switch to counting sort to avoid quadratic time.
+ */
+ if ((bits += 2) > MAX_RECURSION_DEPTH) {
+ countingSort(a, low, high);
+ return;
+ }
/*
- * Partitioning with 2 pivots in case of different elements.
+ * indices[0] - the index of the last element of the left part
+ * indices[1] - the index of the first element of the right part
+ */
+ int[] indices;
+
+ /*
+ * Partitioning with two pivots on array of fully random elements.
*/
if (a[e1] < a[e2] && a[e2] < a[e3] && a[e3] < a[e4] && a[e4] < a[e5]) {
- /*
- * Use the first and fifth of the five sorted elements as
- * the pivots. These values are inexpensive approximation
- * of tertiles. Note, that pivot1 < pivot2.
- */
- char pivot1 = a[e1];
- char pivot2 = a[e5];
+ indices = partitionWithTwoPivots(a, low, high, e1, e5);
/*
- * The first and the last elements to be sorted are moved
- * to the locations formerly occupied by the pivots. When
- * partitioning is completed, the pivots are swapped back
- * into their final positions, and excluded from the next
- * subsequent sorting.
+ * Sort non-left parts recursively (possibly in parallel),
+ * excluding known pivots.
*/
- a[e1] = a[lower];
- a[e5] = a[upper];
+ sort(a, bits | 1, indices[0] + 1, indices[1]);
+ sort(a, bits | 1, indices[1] + 1, high);
+
+ } else { // Partitioning with one pivot
+
+ indices = partitionWithOnePivot(a, low, high, e3);
/*
- * Skip elements, which are less or greater than the pivots.
+ * Sort the right part (possibly in parallel), excluding
+ * known pivot. All elements from the central part are
+ * equal and therefore already sorted.
*/
+ sort(a, bits | 1, indices[1], high);
+ }
+ high = indices[0]; // Iterate along the left part
+ }
+ }
+
+ /**
+ * Partitions the specified range of the array using two given pivots.
+ *
+ * @param a the array for partitioning
+ * @param low the index of the first element, inclusive, for partitioning
+ * @param high the index of the last element, exclusive, for partitioning
+ * @param pivotIndex1 the index of pivot1, the first pivot
+ * @param pivotIndex2 the index of pivot2, the second pivot
+ * @return indices of parts after partitioning
+ */
+ private static int[] partitionWithTwoPivots(
+ char[] a, int low, int high, int pivotIndex1, int pivotIndex2) {
+ /*
+ * Pointers to the right and left parts.
+ */
+ int upper = --high;
+ int lower = low;
+
+ /*
+ * Use the first and fifth of the five sorted elements as
+ * the pivots. These values are inexpensive approximation
+ * of tertiles. Note, that pivot1 < pivot2.
+ */
+ char pivot1 = a[pivotIndex1];
+ char pivot2 = a[pivotIndex2];
+
+ /*
+ * The first and the last elements to be sorted are moved
+ * to the locations formerly occupied by the pivots. When
+ * partitioning is completed, the pivots are swapped back
+ * into their final positions, and excluded from the next
+ * subsequent sorting.
+ */
+ a[pivotIndex1] = a[lower];
+ a[pivotIndex2] = a[upper];
+
+ /*
+ * Skip elements, which are less or greater than the pivots.
+ */
+ while (a[++lower] < pivot1);
+ while (a[--upper] > pivot2);
+
+ /*
+ * Backward 3-interval partitioning
+ *
+ * left part central part right part
+ * +--------------+----------+--------------------------+--------------+
+ * | < pivot1 | ? | pivot1 <= .. <= pivot2 | > pivot2 |
+ * +--------------+----------+--------------------------+--------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
+ *
+ * Pointer k is the last index of ?-part
+ * Pointer lower is the last index of left part
+ * Pointer upper is the first index of right part
+ */
+ for (int unused = --lower, k = ++upper; --k > lower; ) {
+ char ak = a[k];
+
+ if (ak < pivot1) { // Move a[k] to the left part
while (a[++lower] < pivot1);
- while (a[--upper] > pivot2);
- /*
- * Backward 3-interval partitioning
- *
- * left part central part right part
- * +------------------------------------------------------------+
- * | < pivot1 | ? | pivot1 <= && <= pivot2 | > pivot2 |
- * +------------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * lower k upper
- *
- * Invariants:
- *
- * all in (low, lower] < pivot1
- * pivot1 <= all in (k, upper) <= pivot2
- * all in [upper, end) > pivot2
- *
- * Pointer k is the last index of ?-part
- */
- for (int unused = --lower, k = ++upper; --k > lower; ) {
- char ak = a[k];
-
- if (ak < pivot1) { // Move a[k] to the left side
- while (lower < k) {
- if (a[++lower] >= pivot1) {
- if (a[lower] > pivot2) {
- a[k] = a[--upper];
- a[upper] = a[lower];
- } else {
- a[k] = a[lower];
- }
- a[lower] = ak;
- break;
- }
- }
- } else if (ak > pivot2) { // Move a[k] to the right side
- a[k] = a[--upper];
- a[upper] = ak;
- }
+ if (lower > k) {
+ lower = k;
+ break;
}
+ if (a[lower] > pivot2) {
+ a[k] = a[--upper];
+ a[upper] = a[lower];
+ } else {
+ a[k] = a[lower];
+ }
+ a[lower] = ak;
+ } else if (ak > pivot2) { // Move a[k] to the right part
+ a[k] = a[--upper];
+ a[upper] = ak;
+ }
+ }
- /*
- * Swap the pivots into their final positions.
- */
- a[low] = a[lower]; a[lower] = pivot1;
- a[end] = a[upper]; a[upper] = pivot2;
+ /*
+ * Swap the pivots into their final positions.
+ */
+ a[low] = a[lower]; a[lower] = pivot1;
+ a[high] = a[upper]; a[upper] = pivot2;
- /*
- * Sort non-left parts recursively,
- * excluding known pivots.
- */
- sort(a, bits | 1, lower + 1, upper);
- sort(a, bits | 1, upper + 1, high);
+ return new int[] { lower, upper };
+ }
- } else { // Use single pivot in case of many equal elements
+ /**
+ * Partitions the specified range of the array using one given pivot.
+ *
+ * @param a the array for partitioning
+ * @param low the index of the first element, inclusive, for partitioning
+ * @param high the index of the last element, exclusive, for partitioning
+ * @param pivotIndex the index of single pivot
+ * @return indices of parts after partitioning
+ */
+ private static int[] partitionWithOnePivot(
+ char[] a, int low, int high, int pivotIndex) {
+ /*
+ * Pointers to the right and left parts.
+ */
+ int upper = high;
+ int lower = low;
- /*
- * Use the third of the five sorted elements as the pivot.
- * This value is inexpensive approximation of the median.
- */
- char pivot = a[e3];
+ /*
+ * Use the third of the five sorted elements as the pivot.
+ * This value is inexpensive approximation of the median.
+ */
+ char pivot = a[pivotIndex];
- /*
- * The first element to be sorted is moved to the
- * location formerly occupied by the pivot. After
- * completion of partitioning the pivot is swapped
- * back into its final position, and excluded from
- * the next subsequent sorting.
- */
- a[e3] = a[lower];
+ /*
+ * The first element to be sorted is moved to the
+ * location formerly occupied by the pivot. After
+ * completion of partitioning the pivot is swapped
+ * back into its final position, and excluded from
+ * the next subsequent sorting.
+ */
+ a[pivotIndex] = a[lower];
- /*
- * Traditional 3-way (Dutch National Flag) partitioning
- *
- * left part central part right part
- * +------------------------------------------------------+
- * | < pivot | ? | == pivot | > pivot |
- * +------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * lower k upper
- *
- * Invariants:
- *
- * all in (low, lower] < pivot
- * all in (k, upper) == pivot
- * all in [upper, end] > pivot
- *
- * Pointer k is the last index of ?-part
- */
- for (int k = ++upper; --k > lower; ) {
- char ak = a[k];
-
- if (ak != pivot) {
- a[k] = pivot;
-
- if (ak < pivot) { // Move a[k] to the left side
- while (a[++lower] < pivot);
-
- if (a[lower] > pivot) {
- a[--upper] = a[lower];
- }
- a[lower] = ak;
- } else { // ak > pivot - Move a[k] to the right side
- a[--upper] = ak;
- }
- }
- }
+ /*
+ * Dutch National Flag partitioning
+ *
+ * left part central part right part
+ * +--------------+----------+--------------+-------------+
+ * | < pivot | ? | == pivot | > pivot |
+ * +--------------+----------+--------------+-------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
+ *
+ * Pointer k is the last index of ?-part
+ * Pointer lower is the last index of left part
+ * Pointer upper is the first index of right part
+ */
+ for (int k = upper; --k > lower; ) {
+ char ak = a[k];
- /*
- * Swap the pivot into its final position.
- */
- a[low] = a[lower]; a[lower] = pivot;
+ if (ak == pivot) {
+ continue;
+ }
+ a[k] = pivot;
- /*
- * Sort the right part, excluding known pivot.
- * All elements from the central part are
- * equal and therefore already sorted.
- */
- sort(a, bits | 1, upper, high);
+ if (ak < pivot) { // Move a[k] to the left part
+ while (a[++lower] < pivot);
+
+ if (a[lower] > pivot) {
+ a[--upper] = a[lower];
+ }
+ a[lower] = ak;
+ } else { // ak > pivot - Move a[k] to the right part
+ a[--upper] = ak;
}
- high = lower; // Iterate along the left part
}
+
+ /*
+ * Swap the pivot into its final position.
+ */
+ a[low] = a[lower]; a[lower] = pivot;
+
+ return new int[] { lower, upper };
}
/**
@@ -2167,24 +2193,20 @@ static void sort(char[] a, int bits, int low, int high) {
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
- private static void insertionSort(char[] a, int low, int high) {
+ static void insertionSort(char[] a, int low, int high) {
for (int i, k = low; ++k < high; ) {
char ai = a[i = k];
if (ai < a[i - 1]) {
- while (--i >= low && ai < a[i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = ai;
+ do {
+ a[i] = a[--i];
+ } while (i > low && ai < a[i - 1]);
+
+ a[i] = ai;
}
}
}
- /**
- * The number of distinct char values.
- */
- private static final int NUM_CHAR_VALUES = 1 << 16;
-
/**
* Sorts the specified range of the array using counting sort.
*
@@ -2192,47 +2214,133 @@ private static void insertionSort(char[] a, int low, int high) {
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
- private static void countingSort(char[] a, int low, int high) {
- int[] count = new int[NUM_CHAR_VALUES];
+ static void countingSort(char[] a, int low, int high) {
+ int size = high - low;
+
+ if (size > MIN_NUMERICAL_SORT_SIZE) {
+ /*
+ * Count the number of all values.
+ */
+ int[] count = new int[1 << 16];
+
+ /*
+ * Compute the histogram.
+ */
+ for (int i = high; i > low; ++count[a[--i]]);
+
+ /*
+ * Place values on their final positions.
+ */
+ for (int value = count.length; high > low; ) {
+ while (count[--value] == 0);
+ int num = count[value];
+
+ do {
+ a[--high] = (char) value;
+ } while (--num > 0);
+ }
+
+ } else {
+
+ /*
+ * Allocate additional buffer.
+ */
+ char[] b = new char[size];
+
+ /*
+ * Count the number of all digits.
+ */
+ int[] count1 = new int[1 << 8];
+ int[] count2 = new int[1 << 8];
+
+ for (int i = low; i < high; ++i) {
+ ++count1[ a[i] & 0xFF];
+ ++count2[(a[i] >>> 8) & 0xFF];
+ }
+
+ /*
+ * Check digits to be processed.
+ */
+ boolean processDigit1 = processDigit(count1, size, low);
+ boolean processDigit2 = processDigit(count2, size, low);
+
+ /*
+ * Process the 1-st digit.
+ */
+ if (processDigit1) {
+ for (int i = high; i > low; ) {
+ b[--count1[a[--i] & 0xFF] - low] = a[i];
+ }
+ }
+
+ /*
+ * Process the 2-nd digit.
+ */
+ if (processDigit2) {
+ if (processDigit1) {
+ for (int i = size; i > 0; ) {
+ a[--count2[(b[--i] >>> 8) & 0xFF]] = b[i];
+ }
+ } else {
+ for (int i = high; i > low; ) {
+ b[--count2[(a[--i] >>> 8) & 0xFF] - low] = a[i];
+ }
+ }
+ }
+ /*
+ * Copy the buffer to original array, if we process ood number of digits.
+ */
+ if (processDigit1 ^ processDigit2) {
+ System.arraycopy(b, 0, a, low, size);
+ }
+ }
+ }
+
+ /**
+ * Checks the count array and then computes the histogram.
+ *
+ * @param count the count array
+ * @param total the total number of elements
+ * @param low the index of the first element, inclusive
+ * @return {@code true} if the digit must be processed, otherwise {@code false}
+ */
+ private static boolean processDigit(int[] count, int total, int low) {
/*
- * Compute a histogram with the number of each values.
+ * Check if we can skip the given digit.
*/
- for (int i = high; i > low; ++count[a[--i]]);
+ for (int c : count) {
+ if (c == total) {
+ return false;
+ }
+ if (c > 0) {
+ break;
+ }
+ }
/*
- * Place values on their final positions.
+ * Compute the histogram.
*/
- if (high - low > NUM_CHAR_VALUES) {
- for (int i = NUM_CHAR_VALUES; i > 0; ) {
- for (low = high - count[--i]; high > low;
- a[--high] = (char) i
- );
- }
- } else {
- for (int i = NUM_CHAR_VALUES; high > low; ) {
- while (count[--i] == 0);
- int c = count[i];
+ count[0] += low;
- do {
- a[--high] = (char) i;
- } while (--c > 0);
- }
+ for (int i = 0; ++i < count.length; ) {
+ count[i] += count[i - 1];
}
+ return true;
}
-// [short]
+// #[short]
/**
- * Sorts the specified range of the array using
- * counting sort or Dual-Pivot Quicksort.
+ * Sorts the specified range of the array using counting sort
+ * Dual-Pivot Quicksort.
*
* @param a the array to be sorted
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
static void sort(short[] a, int low, int high) {
- if (high - low > MIN_SHORT_OR_CHAR_COUNTING_SORT_SIZE) {
+ if (high - low > MIN_COUNTING_SORT_SIZE) {
countingSort(a, low, high);
} else {
sort(a, 0, low, high);
@@ -2240,21 +2348,20 @@ static void sort(short[] a, int low, int high) {
}
/**
- * Sorts the specified array using the Dual-Pivot Quicksort and/or
- * other sorts in special-cases, possibly with parallel partitions.
+ * Sorts the specified range of the array using Dual-Pivot Quicksort.
*
* @param a the array to be sorted
* @param bits the combination of recursion depth and bit flag, where
- * the right bit "0" indicates that array is the leftmost part
+ * the right bit "0" indicates that range is the leftmost part
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
- static void sort(short[] a, int bits, int low, int high) {
+ private static void sort(short[] a, int bits, int low, int high) {
while (true) {
- int end = high - 1, size = high - low;
+ int size = high - low;
/*
- * Invoke insertion sort on small leftmost part.
+ * Invoke insertion sort on small part.
*/
if (size < MAX_INSERTION_SORT_SIZE) {
insertionSort(a, low, high);
@@ -2262,19 +2369,11 @@ static void sort(short[] a, int bits, int low, int high) {
}
/*
- * Switch to counting sort if execution
- * time is becoming quadratic.
- */
- if ((bits += DELTA) > MAX_RECURSION_DEPTH) {
- countingSort(a, low, high);
- return;
- }
-
- /*
- * Use an inexpensive approximation of the golden ratio
- * to select five sample elements and determine pivots.
+ * Divide the given array into the golden ratio using
+ * an inexpensive approximation to select five sample
+ * elements and determine pivots.
*/
- int step = (size >> 3) * 3 + 3;
+ int step = (size >> 2) + (size >> 3) + (size >> 7);
/*
* Five elements around (and including) the central element
@@ -2283,200 +2382,253 @@ static void sort(short[] a, int bits, int low, int high) {
* determined to work well on a wide variety of inputs.
*/
int e1 = low + step;
- int e5 = end - step;
+ int e5 = high - step;
int e3 = (e1 + e5) >>> 1;
int e2 = (e1 + e3) >>> 1;
int e4 = (e3 + e5) >>> 1;
- short a3 = a[e3];
/*
- * Sort these elements in place by the combination
+ * Sort these elements in-place by the combination
* of 4-element sorting network and insertion sort.
*
- * 5 ------o-----------o------------
- * | |
- * 4 ------|-----o-----o-----o------
- * | | |
- * 2 ------o-----|-----o-----o------
- * | |
- * 1 ------------o-----o------------
- */
- if (a[e5] < a[e2]) { short t = a[e5]; a[e5] = a[e2]; a[e2] = t; }
- if (a[e4] < a[e1]) { short t = a[e4]; a[e4] = a[e1]; a[e1] = t; }
- if (a[e5] < a[e4]) { short t = a[e5]; a[e5] = a[e4]; a[e4] = t; }
- if (a[e2] < a[e1]) { short t = a[e2]; a[e2] = a[e1]; a[e1] = t; }
- if (a[e4] < a[e2]) { short t = a[e4]; a[e4] = a[e2]; a[e2] = t; }
-
- if (a3 < a[e2]) {
- if (a3 < a[e1]) {
- a[e3] = a[e2]; a[e2] = a[e1]; a[e1] = a3;
+ * 1 ---------o---------------o-----------------
+ * | |
+ * 2 ---------|-------o-------o-------o---------
+ * | | |
+ * 3 ---------|-------|---------------|---------
+ * | | |
+ * 4 ---------o-------|-------o-------o---------
+ * | |
+ * 5 -----------------o-------o-----------------
+ */
+ if (a[e1] > a[e4]) { short t = a[e1]; a[e1] = a[e4]; a[e4] = t; }
+ if (a[e2] > a[e5]) { short t = a[e2]; a[e2] = a[e5]; a[e5] = t; }
+ if (a[e4] > a[e5]) { short t = a[e4]; a[e4] = a[e5]; a[e5] = t; }
+ if (a[e1] > a[e2]) { short t = a[e1]; a[e1] = a[e2]; a[e2] = t; }
+ if (a[e2] > a[e4]) { short t = a[e2]; a[e2] = a[e4]; a[e4] = t; }
+
+ /*
+ * Insert the third element.
+ */
+ if (a[e3] < a[e2]) {
+ if (a[e3] < a[e1]) {
+ short t = a[e3]; a[e3] = a[e2]; a[e2] = a[e1]; a[e1] = t;
} else {
- a[e3] = a[e2]; a[e2] = a3;
+ short t = a[e3]; a[e3] = a[e2]; a[e2] = t;
}
- } else if (a3 > a[e4]) {
- if (a3 > a[e5]) {
- a[e3] = a[e4]; a[e4] = a[e5]; a[e5] = a3;
+ } else if (a[e3] > a[e4]) {
+ if (a[e3] > a[e5]) {
+ short t = a[e3]; a[e3] = a[e4]; a[e4] = a[e5]; a[e5] = t;
} else {
- a[e3] = a[e4]; a[e4] = a3;
+ short t = a[e3]; a[e3] = a[e4]; a[e4] = t;
}
}
- // Pointers
- int lower = low; // The index of the last element of the left part
- int upper = end; // The index of the first element of the right part
+ /*
+ * Switch to counting sort to avoid quadratic time.
+ */
+ if ((bits += 2) > MAX_RECURSION_DEPTH) {
+ countingSort(a, low, high);
+ return;
+ }
+
+ /*
+ * indices[0] - the index of the last element of the left part
+ * indices[1] - the index of the first element of the right part
+ */
+ int[] indices;
/*
- * Partitioning with 2 pivots in case of different elements.
+ * Partitioning with two pivots on array of fully random elements.
*/
if (a[e1] < a[e2] && a[e2] < a[e3] && a[e3] < a[e4] && a[e4] < a[e5]) {
- /*
- * Use the first and fifth of the five sorted elements as
- * the pivots. These values are inexpensive approximation
- * of tertiles. Note, that pivot1 < pivot2.
- */
- short pivot1 = a[e1];
- short pivot2 = a[e5];
+ indices = partitionWithTwoPivots(a, low, high, e1, e5);
/*
- * The first and the last elements to be sorted are moved
- * to the locations formerly occupied by the pivots. When
- * partitioning is completed, the pivots are swapped back
- * into their final positions, and excluded from the next
- * subsequent sorting.
+ * Sort non-left parts recursively (possibly in parallel),
+ * excluding known pivots.
*/
- a[e1] = a[lower];
- a[e5] = a[upper];
+ sort(a, bits | 1, indices[0] + 1, indices[1]);
+ sort(a, bits | 1, indices[1] + 1, high);
+
+ } else { // Partitioning with one pivot
+
+ indices = partitionWithOnePivot(a, low, high, e3);
/*
- * Skip elements, which are less or greater than the pivots.
+ * Sort the right part (possibly in parallel), excluding
+ * known pivot. All elements from the central part are
+ * equal and therefore already sorted.
*/
+ sort(a, bits | 1, indices[1], high);
+ }
+ high = indices[0]; // Iterate along the left part
+ }
+ }
+
+ /**
+ * Partitions the specified range of the array using two given pivots.
+ *
+ * @param a the array for partitioning
+ * @param low the index of the first element, inclusive, for partitioning
+ * @param high the index of the last element, exclusive, for partitioning
+ * @param pivotIndex1 the index of pivot1, the first pivot
+ * @param pivotIndex2 the index of pivot2, the second pivot
+ * @return indices of parts after partitioning
+ */
+ private static int[] partitionWithTwoPivots(
+ short[] a, int low, int high, int pivotIndex1, int pivotIndex2) {
+ /*
+ * Pointers to the right and left parts.
+ */
+ int upper = --high;
+ int lower = low;
+
+ /*
+ * Use the first and fifth of the five sorted elements as
+ * the pivots. These values are inexpensive approximation
+ * of tertiles. Note, that pivot1 < pivot2.
+ */
+ short pivot1 = a[pivotIndex1];
+ short pivot2 = a[pivotIndex2];
+
+ /*
+ * The first and the last elements to be sorted are moved
+ * to the locations formerly occupied by the pivots. When
+ * partitioning is completed, the pivots are swapped back
+ * into their final positions, and excluded from the next
+ * subsequent sorting.
+ */
+ a[pivotIndex1] = a[lower];
+ a[pivotIndex2] = a[upper];
+
+ /*
+ * Skip elements, which are less or greater than the pivots.
+ */
+ while (a[++lower] < pivot1);
+ while (a[--upper] > pivot2);
+
+ /*
+ * Backward 3-interval partitioning
+ *
+ * left part central part right part
+ * +--------------+----------+--------------------------+--------------+
+ * | < pivot1 | ? | pivot1 <= .. <= pivot2 | > pivot2 |
+ * +--------------+----------+--------------------------+--------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
+ *
+ * Pointer k is the last index of ?-part
+ * Pointer lower is the last index of left part
+ * Pointer upper is the first index of right part
+ */
+ for (int unused = --lower, k = ++upper; --k > lower; ) {
+ short ak = a[k];
+
+ if (ak < pivot1) { // Move a[k] to the left part
while (a[++lower] < pivot1);
- while (a[--upper] > pivot2);
- /*
- * Backward 3-interval partitioning
- *
- * left part central part right part
- * +------------------------------------------------------------+
- * | < pivot1 | ? | pivot1 <= && <= pivot2 | > pivot2 |
- * +------------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * lower k upper
- *
- * Invariants:
- *
- * all in (low, lower] < pivot1
- * pivot1 <= all in (k, upper) <= pivot2
- * all in [upper, end) > pivot2
- *
- * Pointer k is the last index of ?-part
- */
- for (int unused = --lower, k = ++upper; --k > lower; ) {
- short ak = a[k];
-
- if (ak < pivot1) { // Move a[k] to the left side
- while (lower < k) {
- if (a[++lower] >= pivot1) {
- if (a[lower] > pivot2) {
- a[k] = a[--upper];
- a[upper] = a[lower];
- } else {
- a[k] = a[lower];
- }
- a[lower] = ak;
- break;
- }
- }
- } else if (ak > pivot2) { // Move a[k] to the right side
- a[k] = a[--upper];
- a[upper] = ak;
- }
+ if (lower > k) {
+ lower = k;
+ break;
+ }
+ if (a[lower] > pivot2) {
+ a[k] = a[--upper];
+ a[upper] = a[lower];
+ } else {
+ a[k] = a[lower];
}
+ a[lower] = ak;
+ } else if (ak > pivot2) { // Move a[k] to the right part
+ a[k] = a[--upper];
+ a[upper] = ak;
+ }
+ }
+
+ /*
+ * Swap the pivots into their final positions.
+ */
+ a[low] = a[lower]; a[lower] = pivot1;
+ a[high] = a[upper]; a[upper] = pivot2;
+
+ return new int[] { lower, upper };
+ }
+
+ /**
+ * Partitions the specified range of the array using one given pivot.
+ *
+ * @param a the array for partitioning
+ * @param low the index of the first element, inclusive, for partitioning
+ * @param high the index of the last element, exclusive, for partitioning
+ * @param pivotIndex the index of single pivot
+ * @return indices of parts after partitioning
+ */
+ private static int[] partitionWithOnePivot(
+ short[] a, int low, int high, int pivotIndex) {
+ /*
+ * Pointers to the right and left parts.
+ */
+ int upper = high;
+ int lower = low;
- /*
- * Swap the pivots into their final positions.
- */
- a[low] = a[lower]; a[lower] = pivot1;
- a[end] = a[upper]; a[upper] = pivot2;
+ /*
+ * Use the third of the five sorted elements as the pivot.
+ * This value is inexpensive approximation of the median.
+ */
+ short pivot = a[pivotIndex];
- /*
- * Sort non-left parts recursively,
- * excluding known pivots.
- */
- sort(a, bits | 1, lower + 1, upper);
- sort(a, bits | 1, upper + 1, high);
+ /*
+ * The first element to be sorted is moved to the
+ * location formerly occupied by the pivot. After
+ * completion of partitioning the pivot is swapped
+ * back into its final position, and excluded from
+ * the next subsequent sorting.
+ */
+ a[pivotIndex] = a[lower];
- } else { // Use single pivot in case of many equal elements
+ /*
+ * Dutch National Flag partitioning
+ *
+ * left part central part right part
+ * +--------------+----------+--------------+-------------+
+ * | < pivot | ? | == pivot | > pivot |
+ * +--------------+----------+--------------+-------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
+ *
+ * Pointer k is the last index of ?-part
+ * Pointer lower is the last index of left part
+ * Pointer upper is the first index of right part
+ */
+ for (int k = upper; --k > lower; ) {
+ short ak = a[k];
- /*
- * Use the third of the five sorted elements as the pivot.
- * This value is inexpensive approximation of the median.
- */
- short pivot = a[e3];
+ if (ak == pivot) {
+ continue;
+ }
+ a[k] = pivot;
- /*
- * The first element to be sorted is moved to the
- * location formerly occupied by the pivot. After
- * completion of partitioning the pivot is swapped
- * back into its final position, and excluded from
- * the next subsequent sorting.
- */
- a[e3] = a[lower];
+ if (ak < pivot) { // Move a[k] to the left part
+ while (a[++lower] < pivot);
- /*
- * Traditional 3-way (Dutch National Flag) partitioning
- *
- * left part central part right part
- * +------------------------------------------------------+
- * | < pivot | ? | == pivot | > pivot |
- * +------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * lower k upper
- *
- * Invariants:
- *
- * all in (low, lower] < pivot
- * all in (k, upper) == pivot
- * all in [upper, end] > pivot
- *
- * Pointer k is the last index of ?-part
- */
- for (int k = ++upper; --k > lower; ) {
- short ak = a[k];
-
- if (ak != pivot) {
- a[k] = pivot;
-
- if (ak < pivot) { // Move a[k] to the left side
- while (a[++lower] < pivot);
-
- if (a[lower] > pivot) {
- a[--upper] = a[lower];
- }
- a[lower] = ak;
- } else { // ak > pivot - Move a[k] to the right side
- a[--upper] = ak;
- }
- }
+ if (a[lower] > pivot) {
+ a[--upper] = a[lower];
}
-
- /*
- * Swap the pivot into its final position.
- */
- a[low] = a[lower]; a[lower] = pivot;
-
- /*
- * Sort the right part, excluding known pivot.
- * All elements from the central part are
- * equal and therefore already sorted.
- */
- sort(a, bits | 1, upper, high);
+ a[lower] = ak;
+ } else { // ak > pivot - Move a[k] to the right part
+ a[--upper] = ak;
}
- high = lower; // Iterate along the left part
}
+
+ /*
+ * Swap the pivot into its final position.
+ */
+ a[low] = a[lower]; a[lower] = pivot;
+
+ return new int[] { lower, upper };
}
/**
@@ -2486,29 +2638,20 @@ static void sort(short[] a, int bits, int low, int high) {
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
- private static void insertionSort(short[] a, int low, int high) {
+ static void insertionSort(short[] a, int low, int high) {
for (int i, k = low; ++k < high; ) {
short ai = a[i = k];
if (ai < a[i - 1]) {
- while (--i >= low && ai < a[i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = ai;
+ do {
+ a[i] = a[--i];
+ } while (i > low && ai < a[i - 1]);
+
+ a[i] = ai;
}
}
}
- /**
- * The number of distinct short values.
- */
- private static final int NUM_SHORT_VALUES = 1 << 16;
-
- /**
- * Max index of short counter.
- */
- private static final int MAX_SHORT_INDEX = Short.MAX_VALUE + NUM_SHORT_VALUES + 1;
-
/**
* Sorts the specified range of the array using counting sort.
*
@@ -2516,44 +2659,99 @@ private static void insertionSort(short[] a, int low, int high) {
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
- private static void countingSort(short[] a, int low, int high) {
- int[] count = new int[NUM_SHORT_VALUES];
+ static void countingSort(short[] a, int low, int high) {
+ int size = high - low;
- /*
- * Compute a histogram with the number of each values.
- */
- for (int i = high; i > low; ++count[a[--i] & 0xFFFF]);
+ if (size > MIN_NUMERICAL_SORT_SIZE) {
+ /*
+ * Count the number of all values.
+ */
+ int[] count = new int[1 << 16];
- /*
- * Place values on their final positions.
- */
- if (high - low > NUM_SHORT_VALUES) {
- for (int i = MAX_SHORT_INDEX; --i > Short.MAX_VALUE; ) {
- int value = i & 0xFFFF;
+ /*
+ * Compute the histogram.
+ */
+ for (int i = high; i > low; ++count[a[--i] & 0xFFFF]);
- for (low = high - count[value]; high > low;
- a[--high] = (short) value
- );
+ /*
+ * Place values on their final positions.
+ */
+ for (int value = Short.MIN_VALUE; high > low; ) {
+ while (count[--value & 0xFFFF] == 0);
+ int num = count[value & 0xFFFF];
+
+ do {
+ a[--high] = (short) value;
+ } while (--num > 0);
}
+
} else {
- for (int i = MAX_SHORT_INDEX; high > low; ) {
- while (count[--i & 0xFFFF] == 0);
- int value = i & 0xFFFF;
- int c = count[value];
+ /*
+ * Allocate additional buffer.
+ */
+ short[] b = new short[size];
+
+ /*
+ * Count the number of all digits.
+ */
+ int[] count1 = new int[1 << 8];
+ int[] count2 = new int[1 << 8];
- do {
- a[--high] = (short) value;
- } while (--c > 0);
+ for (int i = low; i < high; ++i) {
+ ++count1[ a[i] & 0xFF];
+ ++count2[((a[i] >>> 8) & 0xFF) ^ 0x80]; // Flip the sign bit
+ }
+
+ /*
+ * Check digits to be processed.
+ */
+ boolean processDigit1 = processDigit(count1, size, low);
+ boolean processDigit2 = processDigit(count2, size, low);
+
+ /*
+ * Process the 1-st digit.
+ */
+ if (processDigit1) {
+ for (int i = high; i > low; ) {
+ b[--count1[a[--i] & 0xFF] - low] = a[i];
+ }
+ }
+
+ /*
+ * Process the 2-nd digit.
+ */
+ if (processDigit2) {
+ if (processDigit1) {
+ for (int i = size; i > 0; ) {
+ a[--count2[((b[--i] >>> 8) & 0xFF) ^ 0x80]] = b[i];
+ }
+ } else {
+ for (int i = high; i > low; ) {
+ b[--count2[((a[--i] >>> 8) & 0xFF) ^ 0x80] - low] = a[i];
+ }
+ }
+ }
+
+ /*
+ * Copy the buffer to original array, if we process ood number of digits.
+ */
+ if (processDigit1 ^ processDigit2) {
+ System.arraycopy(b, 0, a, low, size);
}
}
}
-// [float]
+// #[float]
+
+ /**
+ * The binary representation of float negative zero.
+ */
+ private static final int FLOAT_NEGATIVE_ZERO = Float.floatToRawIntBits(-0.0f);
/**
* Sorts the specified range of the array using parallel merge
- * sort and/or Dual-Pivot Quicksort.
+ * sort and/or Dual-Pivot Quicksort.
*
* To balance the faster splitting and parallelism of merge sort
* with the faster element partitioning of Quicksort, ranges are
@@ -2572,15 +2770,15 @@ static void sort(float[] a, int parallelism, int low, int high) {
* turn them into positive zero, and move all NaNs
* to the end of the array.
*/
- int numNegativeZero = 0;
+ int negativeZeroCount = 0;
for (int k = high; k > low; ) {
float ak = a[--k];
- if (ak == 0.0f && Float.floatToRawIntBits(ak) < 0) { // ak is -0.0f
- numNegativeZero += 1;
+ if (Float.floatToRawIntBits(ak) == FLOAT_NEGATIVE_ZERO) { // ak is -0.0f
+ negativeZeroCount++;
a[k] = 0.0f;
- } else if (ak != ak) { // ak is NaN
+ } else if (ak != ak) { // ak is Not-a-Number (NaN)
a[k] = a[--high];
a[high] = ak;
}
@@ -2590,21 +2788,17 @@ static void sort(float[] a, int parallelism, int low, int high) {
* Phase 2. Sort everything except NaNs,
* which are already in place.
*/
- int size = high - low;
-
- if (parallelism > 1 && size > MIN_PARALLEL_SORT_SIZE) {
- int depth = getDepth(parallelism, size >> 12);
- float[] b = depth == 0 ? null : new float[size];
- new Sorter(null, a, b, low, size, low, depth).invoke();
+ if (parallelism > 1 && high - low > MIN_PARALLEL_SORT_SIZE) {
+ new Sorter<>(a, parallelism, low, high - low).invoke();
} else {
sort(null, a, 0, low, high);
}
/*
- * Phase 3. Turn positive zero 0.0f
- * back into negative zero -0.0f.
+ * Phase 3. Turn the required number of positive
+ * zeros 0.0f back into negative zeros -0.0f.
*/
- if (++numNegativeZero == 1) {
+ if (++negativeZeroCount == 1) {
return;
}
@@ -2613,43 +2807,43 @@ static void sort(float[] a, int parallelism, int low, int high) {
* the index of the first zero.
*/
while (low <= high) {
- int middle = (low + high) >>> 1;
+ int mid = (low + high) >>> 1;
- if (a[middle] < 0) {
- low = middle + 1;
+ if (a[mid] < 0.0f) {
+ low = mid + 1;
} else {
- high = middle - 1;
+ high = mid - 1;
}
}
/*
- * Replace the required number of 0.0f by -0.0f.
+ * Replace 0.0f by negative zeros -0.0f.
*/
- while (--numNegativeZero > 0) {
+ while (--negativeZeroCount > 0) {
a[++high] = -0.0f;
}
}
/**
- * Sorts the specified array using the Dual-Pivot Quicksort and/or
- * other sorts in special-cases, possibly with parallel partitions.
+ * Sorts the specified range of the array using Dual-Pivot Quicksort.
*
- * @param sorter parallel context
+ * @param sorter the parallel context
* @param a the array to be sorted
* @param bits the combination of recursion depth and bit flag, where
- * the right bit "0" indicates that array is the leftmost part
+ * the right bit "0" indicates that range is the leftmost part
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
- static void sort(Sorter sorter, float[] a, int bits, int low, int high) {
+ static void sort(Sorter sorter, float[] a, int bits, int low, int high) {
while (true) {
- int end = high - 1, size = high - low;
+ int size = high - low;
/*
- * Run mixed insertion sort on small non-leftmost parts.
+ * Run adaptive mixed insertion sort on small non-leftmost parts.
*/
- if (size < MAX_MIXED_INSERTION_SORT_SIZE + bits && (bits & 1) > 0) {
- sort(float.class, a, Unsafe.ARRAY_FLOAT_BASE_OFFSET, low, high, DualPivotQuicksort::mixedInsertionSort);
+ if (size < MAX_INSERTION_SORT_SIZE + bits && (bits & 1) > 0) {
+ sort(float.class, a, Unsafe.ARRAY_FLOAT_BASE_OFFSET,
+ low, high, DualPivotQuicksort::mixedInsertionSort);
return;
}
@@ -2657,33 +2851,25 @@ static void sort(Sorter sorter, float[] a, int bits, int low, int high) {
* Invoke insertion sort on small leftmost part.
*/
if (size < MAX_INSERTION_SORT_SIZE) {
- sort(float.class, a, Unsafe.ARRAY_FLOAT_BASE_OFFSET, low, high, DualPivotQuicksort::insertionSort);
- return;
- }
-
- /*
- * Check if the whole array or large non-leftmost
- * parts are nearly sorted and then merge runs.
- */
- if ((bits == 0 || size > MIN_TRY_MERGE_SIZE && (bits & 1) > 0)
- && tryMergeRuns(sorter, a, low, size)) {
+ sort(float.class, a, Unsafe.ARRAY_FLOAT_BASE_OFFSET,
+ low, high, DualPivotQuicksort::insertionSort);
return;
}
/*
- * Switch to heap sort if execution
- * time is becoming quadratic.
+ * Try merging sort on large part.
*/
- if ((bits += DELTA) > MAX_RECURSION_DEPTH) {
- heapSort(a, low, high);
+ if (size > MIN_MERGING_SORT_SIZE * bits
+ && tryMergingSort(sorter, a, low, high)) {
return;
}
/*
- * Use an inexpensive approximation of the golden ratio
- * to select five sample elements and determine pivots.
+ * Divide the given array into the golden ratio using
+ * an inexpensive approximation to select five sample
+ * elements and determine pivots.
*/
- int step = (size >> 3) * 3 + 3;
+ int step = (size >> 2) + (size >> 3) + (size >> 7);
/*
* Five elements around (and including) the central element
@@ -2692,117 +2878,127 @@ && tryMergeRuns(sorter, a, low, size)) {
* determined to work well on a wide variety of inputs.
*/
int e1 = low + step;
- int e5 = end - step;
+ int e5 = high - step;
int e3 = (e1 + e5) >>> 1;
int e2 = (e1 + e3) >>> 1;
int e4 = (e3 + e5) >>> 1;
- float a3 = a[e3];
/*
- * Sort these elements in place by the combination
+ * Sort these elements in-place by the combination
* of 4-element sorting network and insertion sort.
*
- * 5 ------o-----------o------------
- * | |
- * 4 ------|-----o-----o-----o------
- * | | |
- * 2 ------o-----|-----o-----o------
- * | |
- * 1 ------------o-----o------------
- */
- if (a[e5] < a[e2]) { float t = a[e5]; a[e5] = a[e2]; a[e2] = t; }
- if (a[e4] < a[e1]) { float t = a[e4]; a[e4] = a[e1]; a[e1] = t; }
- if (a[e5] < a[e4]) { float t = a[e5]; a[e5] = a[e4]; a[e4] = t; }
- if (a[e2] < a[e1]) { float t = a[e2]; a[e2] = a[e1]; a[e1] = t; }
- if (a[e4] < a[e2]) { float t = a[e4]; a[e4] = a[e2]; a[e2] = t; }
-
- if (a3 < a[e2]) {
- if (a3 < a[e1]) {
- a[e3] = a[e2]; a[e2] = a[e1]; a[e1] = a3;
+ * 1 ---------o---------------o-----------------
+ * | |
+ * 2 ---------|-------o-------o-------o---------
+ * | | |
+ * 3 ---------|-------|---------------|---------
+ * | | |
+ * 4 ---------o-------|-------o-------o---------
+ * | |
+ * 5 -----------------o-------o-----------------
+ */
+ if (a[e1] > a[e4]) { float t = a[e1]; a[e1] = a[e4]; a[e4] = t; }
+ if (a[e2] > a[e5]) { float t = a[e2]; a[e2] = a[e5]; a[e5] = t; }
+ if (a[e4] > a[e5]) { float t = a[e4]; a[e4] = a[e5]; a[e5] = t; }
+ if (a[e1] > a[e2]) { float t = a[e1]; a[e1] = a[e2]; a[e2] = t; }
+ if (a[e2] > a[e4]) { float t = a[e2]; a[e2] = a[e4]; a[e4] = t; }
+
+ /*
+ * Insert the third element.
+ */
+ if (a[e3] < a[e2]) {
+ if (a[e3] < a[e1]) {
+ float t = a[e3]; a[e3] = a[e2]; a[e2] = a[e1]; a[e1] = t;
} else {
- a[e3] = a[e2]; a[e2] = a3;
+ float t = a[e3]; a[e3] = a[e2]; a[e2] = t;
}
- } else if (a3 > a[e4]) {
- if (a3 > a[e5]) {
- a[e3] = a[e4]; a[e4] = a[e5]; a[e5] = a3;
+ } else if (a[e3] > a[e4]) {
+ if (a[e3] > a[e5]) {
+ float t = a[e3]; a[e3] = a[e4]; a[e4] = a[e5]; a[e5] = t;
} else {
- a[e3] = a[e4]; a[e4] = a3;
+ float t = a[e3]; a[e3] = a[e4]; a[e4] = t;
}
}
- // Pointers
- int lower; // The index of the last element of the left part
- int upper; // The index of the first element of the right part
+ /*
+ * Switch to heap sort to avoid quadratic time.
+ */
+ if ((bits += 2) > MAX_RECURSION_DEPTH) {
+ heapSort(a, low, high);
+ return;
+ }
+
+ /*
+ * indices[0] - the index of the last element of the left part
+ * indices[1] - the index of the first element of the right part
+ */
+ int[] indices;
/*
- * Partitioning with 2 pivots in case of different elements.
+ * Partitioning with two pivots on array of fully random elements.
*/
if (a[e1] < a[e2] && a[e2] < a[e3] && a[e3] < a[e4] && a[e4] < a[e5]) {
- /*
- * Use the first and fifth of the five sorted elements as
- * the pivots. These values are inexpensive approximation
- * of tertiles. Note, that pivot1 < pivot2.
- */
- int[] pivotIndices = partition(float.class, a, Unsafe.ARRAY_FLOAT_BASE_OFFSET, low, high, e1, e5, DualPivotQuicksort::partitionDualPivot);
- lower = pivotIndices[0];
- upper = pivotIndices[1];
+ indices = partition(float.class, a, Unsafe.ARRAY_FLOAT_BASE_OFFSET,
+ low, high, e1, e5, DualPivotQuicksort::partitionWithTwoPivots);
+
/*
* Sort non-left parts recursively (possibly in parallel),
* excluding known pivots.
*/
if (size > MIN_PARALLEL_SORT_SIZE && sorter != null) {
- sorter.forkSorter(bits | 1, lower + 1, upper);
- sorter.forkSorter(bits | 1, upper + 1, high);
+ sorter.fork(bits | 1, indices[0] + 1, indices[1]);
+ sorter.fork(bits | 1, indices[1] + 1, high);
} else {
- sort(sorter, a, bits | 1, lower + 1, upper);
- sort(sorter, a, bits | 1, upper + 1, high);
+ sort(sorter, a, bits | 1, indices[0] + 1, indices[1]);
+ sort(sorter, a, bits | 1, indices[1] + 1, high);
}
- } else { // Use single pivot in case of many equal elements
+ } else { // Partitioning with one pivot
+
+ indices = partition(float.class, a, Unsafe.ARRAY_FLOAT_BASE_OFFSET,
+ low, high, e3, e3, DualPivotQuicksort::partitionWithOnePivot);
- /*
- * Use the third of the five sorted elements as the pivot.
- * This value is inexpensive approximation of the median.
- */
- int[] pivotIndices = partition(float.class, a, Unsafe.ARRAY_FLOAT_BASE_OFFSET, low, high, e3, e3, DualPivotQuicksort::partitionSinglePivot);
- lower = pivotIndices[0];
- upper = pivotIndices[1];
/*
* Sort the right part (possibly in parallel), excluding
* known pivot. All elements from the central part are
* equal and therefore already sorted.
*/
if (size > MIN_PARALLEL_SORT_SIZE && sorter != null) {
- sorter.forkSorter(bits | 1, upper, high);
+ sorter.fork(bits | 1, indices[1], high);
} else {
- sort(sorter, a, bits | 1, upper, high);
+ sort(sorter, a, bits | 1, indices[1], high);
}
}
- high = lower; // Iterate along the left part
+ high = indices[0]; // Iterate along the left part
}
}
/**
- * Partitions the specified range of the array using the two pivots provided.
+ * Partitions the specified range of the array using two given pivots.
*
- * @param array the array to be partitioned
+ * @param a the array for partitioning
* @param low the index of the first element, inclusive, for partitioning
* @param high the index of the last element, exclusive, for partitioning
* @param pivotIndex1 the index of pivot1, the first pivot
* @param pivotIndex2 the index of pivot2, the second pivot
- *
+ * @return indices of parts after partitioning
*/
- @ForceInline
- private static int[] partitionDualPivot(float[] a, int low, int high, int pivotIndex1, int pivotIndex2) {
- int end = high - 1;
+ private static int[] partitionWithTwoPivots(
+ float[] a, int low, int high, int pivotIndex1, int pivotIndex2) {
+ /*
+ * Pointers to the right and left parts.
+ */
+ int upper = --high;
int lower = low;
- int upper = end;
- int e1 = pivotIndex1;
- int e5 = pivotIndex2;
- float pivot1 = a[e1];
- float pivot2 = a[e5];
+ /*
+ * Use the first and fifth of the five sorted elements as
+ * the pivots. These values are inexpensive approximation
+ * of tertiles. Note, that pivot1 < pivot2.
+ */
+ float pivot1 = a[pivotIndex1];
+ float pivot2 = a[pivotIndex2];
/*
* The first and the last elements to be sorted are moved
@@ -2811,8 +3007,8 @@ private static int[] partitionDualPivot(float[] a, int low, int high, int pivotI
* into their final positions, and excluded from the next
* subsequent sorting.
*/
- a[e1] = a[lower];
- a[e5] = a[upper];
+ a[pivotIndex1] = a[lower];
+ a[pivotIndex2] = a[upper];
/*
* Skip elements, which are less or greater than the pivots.
@@ -2823,39 +3019,36 @@ private static int[] partitionDualPivot(float[] a, int low, int high, int pivotI
/*
* Backward 3-interval partitioning
*
- * left part central part right part
- * +------------------------------------------------------------+
- * | < pivot1 | ? | pivot1 <= && <= pivot2 | > pivot2 |
- * +------------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * lower k upper
- *
- * Invariants:
- *
- * all in (low, lower] < pivot1
- * pivot1 <= all in (k, upper) <= pivot2
- * all in [upper, end) > pivot2
+ * left part central part right part
+ * +--------------+----------+--------------------------+--------------+
+ * | < pivot1 | ? | pivot1 <= .. <= pivot2 | > pivot2 |
+ * +--------------+----------+--------------------------+--------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
*
* Pointer k is the last index of ?-part
+ * Pointer lower is the last index of left part
+ * Pointer upper is the first index of right part
*/
for (int unused = --lower, k = ++upper; --k > lower; ) {
float ak = a[k];
- if (ak < pivot1) { // Move a[k] to the left side
- while (lower < k) {
- if (a[++lower] >= pivot1) {
- if (a[lower] > pivot2) {
- a[k] = a[--upper];
- a[upper] = a[lower];
- } else {
- a[k] = a[lower];
- }
- a[lower] = ak;
- break;
- }
+ if (ak < pivot1) { // Move a[k] to the left part
+ while (a[++lower] < pivot1);
+
+ if (lower > k) {
+ lower = k;
+ break;
+ }
+ if (a[lower] > pivot2) {
+ a[k] = a[--upper];
+ a[upper] = a[lower];
+ } else {
+ a[k] = a[lower];
}
- } else if (ak > pivot2) { // Move a[k] to the right side
+ a[lower] = ak;
+ } else if (ak > pivot2) { // Move a[k] to the right part
a[k] = a[--upper];
a[upper] = ak;
}
@@ -2864,30 +3057,35 @@ private static int[] partitionDualPivot(float[] a, int low, int high, int pivotI
/*
* Swap the pivots into their final positions.
*/
- a[low] = a[lower]; a[lower] = pivot1;
- a[end] = a[upper]; a[upper] = pivot2;
+ a[low] = a[lower]; a[lower] = pivot1;
+ a[high] = a[upper]; a[upper] = pivot2;
- return new int[] {lower, upper};
+ return new int[] { lower, upper };
}
/**
- * Partitions the specified range of the array using a single pivot provided.
+ * Partitions the specified range of the array using one given pivot.
*
- * @param array the array to be partitioned
+ * @param a the array for partitioning
* @param low the index of the first element, inclusive, for partitioning
* @param high the index of the last element, exclusive, for partitioning
- * @param pivotIndex1 the index of pivot1, the first pivot
- * @param pivotIndex2 the index of pivot2, the second pivot
- *
+ * @param pivotIndex1 the index of single pivot
+ * @param pivotIndex2 the index of single pivot
+ * @return indices of parts after partitioning
*/
- @ForceInline
- private static int[] partitionSinglePivot(float[] a, int low, int high, int pivotIndex1, int pivotIndex2) {
- int end = high - 1;
+ private static int[] partitionWithOnePivot(
+ float[] a, int low, int high, int pivotIndex1, int pivotIndex2) {
+ /*
+ * Pointers to the right and left parts.
+ */
+ int upper = high;
int lower = low;
- int upper = end;
- int e3 = pivotIndex1;
- float pivot = a[e3];
+ /*
+ * Use the third of the five sorted elements as the pivot.
+ * This value is inexpensive approximation of the median.
+ */
+ float pivot = a[pivotIndex1];
/*
* The first element to be sorted is moved to the
@@ -2896,43 +3094,40 @@ private static int[] partitionSinglePivot(float[] a, int low, int high, int pivo
* back into its final position, and excluded from
* the next subsequent sorting.
*/
- a[e3] = a[lower];
+ a[pivotIndex1] = a[lower];
/*
- * Traditional 3-way (Dutch National Flag) partitioning
+ * Dutch National Flag partitioning
*
- * left part central part right part
- * +------------------------------------------------------+
- * | < pivot | ? | == pivot | > pivot |
- * +------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * lower k upper
- *
- * Invariants:
- *
- * all in (low, lower] < pivot
- * all in (k, upper) == pivot
- * all in [upper, end] > pivot
+ * left part central part right part
+ * +--------------+----------+--------------+-------------+
+ * | < pivot | ? | == pivot | > pivot |
+ * +--------------+----------+--------------+-------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
*
* Pointer k is the last index of ?-part
+ * Pointer lower is the last index of left part
+ * Pointer upper is the first index of right part
*/
- for (int k = ++upper; --k > lower; ) {
+ for (int k = upper; --k > lower; ) {
float ak = a[k];
- if (ak != pivot) {
- a[k] = pivot;
+ if (ak == pivot) {
+ continue;
+ }
+ a[k] = pivot;
- if (ak < pivot) { // Move a[k] to the left side
- while (a[++lower] < pivot);
+ if (ak < pivot) { // Move a[k] to the left part
+ while (a[++lower] < pivot);
- if (a[lower] > pivot) {
- a[--upper] = a[lower];
- }
- a[lower] = ak;
- } else { // ak > pivot - Move a[k] to the right side
- a[--upper] = ak;
+ if (a[lower] > pivot) {
+ a[--upper] = a[lower];
}
+ a[lower] = ak;
+ } else { // ak > pivot - Move a[k] to the right part
+ a[--upper] = ak;
}
}
@@ -2940,14 +3135,15 @@ private static int[] partitionSinglePivot(float[] a, int low, int high, int pivo
* Swap the pivot into its final position.
*/
a[low] = a[lower]; a[lower] = pivot;
- return new int[] {lower, upper};
+
+ return new int[] { lower, upper };
}
/**
- * Sorts the specified range of the array using mixed insertion sort.
+ * Sorts the specified range of the array using mixed insertion sort.
*
- * Mixed insertion sort is combination of simple insertion sort,
- * pin insertion sort and pair insertion sort.
+ * Mixed insertion sort is combination of pin insertion sort,
+ * simple insertion sort and pair insertion sort.
*
* In the context of Dual-Pivot Quicksort, the pivot element
* from the left part plays the role of sentinel, because it
@@ -2959,205 +3155,132 @@ private static int[] partitionSinglePivot(float[] a, int low, int high, int pivo
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
- private static void mixedInsertionSort(float[] a, int low, int high) {
- int size = high - low;
- int end = high - 3 * ((size >> 5) << 3);
- if (end == high) {
+ static void mixedInsertionSort(float[] a, int low, int high) {
+ /*
+ * Split part for pin and pair insertion sorts.
+ */
+ int end = high - 3 * ((high - low) >> 3 << 1);
- /*
- * Invoke simple insertion sort on tiny array.
- */
- for (int i; ++low < end; ) {
+ /*
+ * Invoke simple insertion sort on small part.
+ */
+ if (end == high) {
+ for (int i; ++low < high; ) {
float ai = a[i = low];
- while (ai < a[--i]) {
- a[i + 1] = a[i];
+ while (ai < a[i - 1]) {
+ a[i] = a[--i];
}
- a[i + 1] = ai;
+ a[i] = ai;
}
- } else {
+ return;
+ }
+
+ /*
+ * Start with pin insertion sort.
+ */
+ for (int i, p = high; ++low < end; ) {
+ float ai = a[i = low], pin = a[--p];
/*
- * Start with pin insertion sort on small part.
- *
- * Pin insertion sort is extended simple insertion sort.
- * The main idea of this sort is to put elements larger
- * than an element called pin to the end of array (the
- * proper area for such elements). It avoids expensive
- * movements of these elements through the whole array.
+ * Swap larger element with pin.
*/
- float pin = a[end];
-
- for (int i, p = high; ++low < end; ) {
- float ai = a[i = low];
-
- if (ai < a[i - 1]) { // Small element
-
- /*
- * Insert small element into sorted part.
- */
- a[i] = a[--i];
-
- while (ai < a[--i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = ai;
-
- } else if (p > i && ai > pin) { // Large element
-
- /*
- * Find element smaller than pin.
- */
- while (a[--p] > pin);
-
- /*
- * Swap it with large element.
- */
- if (p > i) {
- ai = a[p];
- a[p] = a[i];
- }
-
- /*
- * Insert small element into sorted part.
- */
- while (ai < a[--i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = ai;
- }
+ if (ai > pin) {
+ ai = pin;
+ a[p] = a[i];
}
/*
- * Continue with pair insertion sort on remain part.
+ * Insert element into sorted part.
*/
- for (int i; low < high; ++low) {
- float a1 = a[i = low], a2 = a[++low];
+ while (ai < a[i - 1]) {
+ a[i] = a[--i];
+ }
+ a[i] = ai;
+ }
- /*
- * Insert two elements per iteration: at first, insert the
- * larger element and then insert the smaller element, but
- * from the position where the larger element was inserted.
- */
- if (a1 > a2) {
+ /*
+ * Finish with pair insertion sort.
+ */
+ for (int i; low < high; ++low) {
+ float a1 = a[i = low], a2 = a[++low];
- while (a1 < a[--i]) {
- a[i + 2] = a[i];
- }
- a[++i + 1] = a1;
+ /*
+ * Insert two elements per iteration: at first, insert the
+ * larger element and then insert the smaller element, but
+ * from the position where the larger element was inserted.
+ */
+ if (a1 > a2) {
- while (a2 < a[--i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = a2;
+ while (a1 < a[--i]) {
+ a[i + 2] = a[i];
+ }
+ a[++i + 1] = a1;
- } else if (a1 < a[i - 1]) {
+ while (a2 < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = a2;
- while (a2 < a[--i]) {
- a[i + 2] = a[i];
- }
- a[++i + 1] = a2;
+ } else if (a1 < a[i - 1]) {
- while (a1 < a[--i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = a1;
+ while (a2 < a[--i]) {
+ a[i + 2] = a[i];
}
- }
- }
- }
-
- /**
- * Sorts the specified range of the array using insertion sort.
- *
- * @param a the array to be sorted
- * @param low the index of the first element, inclusive, to be sorted
- * @param high the index of the last element, exclusive, to be sorted
- */
- private static void insertionSort(float[] a, int low, int high) {
- for (int i, k = low; ++k < high; ) {
- float ai = a[i = k];
+ a[++i + 1] = a2;
- if (ai < a[i - 1]) {
- while (--i >= low && ai < a[i]) {
+ while (a1 < a[--i]) {
a[i + 1] = a[i];
}
- a[i + 1] = ai;
+ a[i + 1] = a1;
}
}
}
/**
- * Sorts the specified range of the array using heap sort.
+ * Sorts the specified range of the array using insertion sort.
*
* @param a the array to be sorted
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
- private static void heapSort(float[] a, int low, int high) {
- for (int k = (low + high) >>> 1; k > low; ) {
- pushDown(a, --k, a[k], low, high);
- }
- while (--high > low) {
- float max = a[low];
- pushDown(a, low, a[high], low, high);
- a[high] = max;
- }
- }
-
- /**
- * Pushes specified element down during heap sort.
- *
- * @param a the given array
- * @param p the start index
- * @param value the given element
- * @param low the index of the first element, inclusive, to be sorted
- * @param high the index of the last element, exclusive, to be sorted
- */
- private static void pushDown(float[] a, int p, float value, int low, int high) {
- for (int k ;; a[p] = a[p = k]) {
- k = (p << 1) - low + 2; // Index of the right child
-
- if (k > high) {
- break;
- }
- if (k == high || a[k] < a[k - 1]) {
- --k;
- }
- if (a[k] <= value) {
- break;
+ static void insertionSort(float[] a, int low, int high) {
+ for (int i, k = low; ++k < high; ) {
+ float ai = a[i = k];
+
+ if (ai < a[i - 1]) {
+ do {
+ a[i] = a[--i];
+ } while (i > low && ai < a[i - 1]);
+
+ a[i] = ai;
}
}
- a[p] = value;
}
/**
- * Tries to sort the specified range of the array.
+ * Tries to sort the specified range of the array using merging sort.
*
- * @param sorter parallel context
+ * @param sorter the parallel context
* @param a the array to be sorted
- * @param low the index of the first element to be sorted
- * @param size the array size
- * @return true if finally sorted, false otherwise
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ * @return {@code true} if the array is finally sorted, otherwise {@code false}
*/
- private static boolean tryMergeRuns(Sorter sorter, float[] a, int low, int size) {
-
+ static boolean tryMergingSort(Sorter sorter, float[] a, int low, int high) {
/*
- * The run array is constructed only if initial runs are
- * long enough to continue, run[i] then holds start index
- * of the i-th sequence of elements in non-descending order.
+ * The element run[i] holds the start index
+ * of i-th sequence in non-descending order.
*/
+ int count = 1;
int[] run = null;
- int high = low + size;
- int count = 1, last = low;
/*
* Identify all possible runs.
*/
- for (int k = low + 1; k < high; ) {
-
+ for (int k = low + 1, last = low; k < high; ) {
/*
- * Find the end index of the current run.
+ * Find the next run.
*/
if (a[k - 1] < a[k]) {
@@ -3173,81 +3296,79 @@ private static boolean tryMergeRuns(Sorter sorter, float[] a, int low, int size)
for (int i = last - 1, j = k; ++i < --j && a[i] > a[j]; ) {
float ai = a[i]; a[i] = a[j]; a[j] = ai;
}
+
+ // Check the next sequence
+ if (k < high && a[k - 1] < a[k]) {
+ continue;
+ }
+
} else { // Identify constant sequence
for (float ak = a[k]; ++k < high && ak == a[k]; );
+ // Check the next sequence
if (k < high) {
continue;
}
}
/*
- * Check special cases.
+ * Process the current run.
*/
if (run == null) {
- if (k == high) {
+ if (k == high) {
/*
- * The array is monotonous sequence,
+ * Array is monotonous sequence
* and therefore already sorted.
*/
return true;
}
-
- if (k - low < MIN_FIRST_RUN_SIZE) {
-
- /*
- * The first run is too small
- * to proceed with scanning.
- */
- return false;
- }
-
- run = new int[((size >> 10) | 0x7F) & 0x3FF];
+ run = new int[Math.min((high - low) >> 6, MAX_RUN_CAPACITY) | 8];
run[0] = low;
- } else if (a[last - 1] > a[last]) {
-
- if (count > (k - low) >> MIN_FIRST_RUNS_FACTOR) {
+ } else if (a[last - 1] > a[last]) { // Start the new run
+ if (k - low < count * MIN_RUN_SIZE) {
/*
- * The first runs are not long
- * enough to continue scanning.
+ * Terminate the scanning,
+ * if the runs are too small.
*/
return false;
}
- if (++count == MAX_RUN_CAPACITY) {
-
+ if (++count == run.length) {
/*
* Array is not highly structured.
*/
return false;
}
+ }
- if (count == run.length) {
+ /*
+ * Save the current run.
+ */
+ run[count] = (last = k);
- /*
- * Increase capacity of index array.
- */
- run = Arrays.copyOf(run, count << 1);
- }
+ /*
+ * Check single-element run at the end.
+ */
+ if (++k == high) {
+ --k;
}
- run[count] = (last = k);
}
/*
- * Merge runs of highly structured array.
+ * Merge all runs.
*/
if (count > 1) {
float[] b; int offset = low;
- if (sorter == null || (b = (float[]) sorter.b) == null) {
- b = new float[size];
- } else {
+ if (sorter != null && (b = sorter.b) != null) {
offset = sorter.offset;
+ } else if ((b = tryAllocate(float[].class, high - low)) == null) {
+ return false;
}
- mergeRuns(a, b, offset, 1, sorter != null, run, 0, count);
+ mergeRuns(sorter, a, b, offset, true, run, 0, count);
}
return true;
}
@@ -3255,158 +3376,245 @@ private static boolean tryMergeRuns(Sorter sorter, float[] a, int low, int size)
/**
* Merges the specified runs.
*
+ * @param sorter the parallel context
* @param a the source array
- * @param b the temporary buffer used in merging
+ * @param b the buffer for merging
* @param offset the start index in the source, inclusive
- * @param aim specifies merging: to source ( > 0), buffer ( < 0) or any ( == 0)
- * @param parallel indicates whether merging is performed in parallel
+ * @param aim whether the original array is used for merging
* @param run the start indexes of the runs, inclusive
* @param lo the start index of the first run, inclusive
* @param hi the start index of the last run, inclusive
- * @return the destination where runs are merged
*/
- private static float[] mergeRuns(float[] a, float[] b, int offset,
- int aim, boolean parallel, int[] run, int lo, int hi) {
+ private static void mergeRuns(Sorter sorter, float[] a, float[] b, int offset,
+ boolean aim, int[] run, int lo, int hi) {
if (hi - lo == 1) {
- if (aim >= 0) {
- return a;
+ if (!aim) {
+ System.arraycopy(a, run[lo], b, run[lo] - offset, run[hi] - run[lo]);
}
- for (int i = run[hi], j = i - offset, low = run[lo]; i > low;
- b[--j] = a[--i]
- );
- return b;
+ return;
}
/*
- * Split into approximately equal parts.
+ * Split the array into two approximately equal parts.
*/
- int mi = lo, rmi = (run[lo] + run[hi]) >>> 1;
- while (run[++mi + 1] <= rmi);
+ int mi = lo, key = (run[lo] + run[hi]) >>> 1;
+ while (run[++mi + 1] <= key);
/*
- * Merge the left and right parts.
+ * Merge the runs of all parts.
*/
- float[] a1, a2;
+ mergeRuns(sorter, a, b, offset, !aim, run, lo, mi);
+ mergeRuns(sorter, a, b, offset, !aim, run, mi, hi);
- if (parallel && hi - lo > MIN_RUN_COUNT) {
- RunMerger merger = new RunMerger(a, b, offset, 0, run, mi, hi).forkMe();
- a1 = mergeRuns(a, b, offset, -aim, true, run, lo, mi);
- a2 = (float[]) merger.getDestination();
- } else {
- a1 = mergeRuns(a, b, offset, -aim, false, run, lo, mi);
- a2 = mergeRuns(a, b, offset, 0, false, run, mi, hi);
- }
+ float[] dst = aim ? a : b;
+ float[] src = aim ? b : a;
- float[] dst = a1 == a ? b : a;
+ int k = !aim ? run[lo] - offset : run[lo];
+ int lo1 = aim ? run[lo] - offset : run[lo];
+ int hi1 = aim ? run[mi] - offset : run[mi];
+ int lo2 = aim ? run[mi] - offset : run[mi];
+ int hi2 = aim ? run[hi] - offset : run[hi];
- int k = a1 == a ? run[lo] - offset : run[lo];
- int lo1 = a1 == b ? run[lo] - offset : run[lo];
- int hi1 = a1 == b ? run[mi] - offset : run[mi];
- int lo2 = a2 == b ? run[mi] - offset : run[mi];
- int hi2 = a2 == b ? run[hi] - offset : run[hi];
-
- if (parallel) {
- new Merger(null, dst, k, a1, lo1, hi1, a2, lo2, hi2).invoke();
+ /*
+ * Merge the left and right parts.
+ */
+ if (hi1 - lo1 > MIN_PARALLEL_SORT_SIZE && sorter != null) {
+ new Merger<>(null, dst, k, src, lo1, hi1, lo2, hi2).invoke();
} else {
- mergeParts(null, dst, k, a1, lo1, hi1, a2, lo2, hi2);
+ mergeParts(dst, k, src, lo1, hi1, lo2, hi2);
}
- return dst;
}
/**
- * Merges the sorted parts.
+ * Merges the sorted parts in parallel.
*
- * @param merger parallel context
+ * @param merger the parallel context
* @param dst the destination where parts are merged
* @param k the start index of the destination, inclusive
- * @param a1 the first part
+ * @param src the source array
* @param lo1 the start index of the first part, inclusive
* @param hi1 the end index of the first part, exclusive
- * @param a2 the second part
* @param lo2 the start index of the second part, inclusive
* @param hi2 the end index of the second part, exclusive
*/
- private static void mergeParts(Merger merger, float[] dst, int k,
- float[] a1, int lo1, int hi1, float[] a2, int lo2, int hi2) {
+ private static void mergeParts(Merger merger, float[] dst, int k,
+ float[] src, int lo1, int hi1, int lo2, int hi2) {
- if (merger != null && a1 == a2) {
+ while (true) {
+ /*
+ * The first part must be larger.
+ */
+ if (hi1 - lo1 < hi2 - lo2) {
+ int lo = lo1; lo1 = lo2; lo2 = lo;
+ int hi = hi1; hi1 = hi2; hi2 = hi;
+ }
- while (true) {
+ /*
+ * Merge the small parts sequentially.
+ */
+ if (hi1 - lo1 < MIN_PARALLEL_SORT_SIZE) {
+ break;
+ }
- /*
- * The first part must be larger.
- */
- if (hi1 - lo1 < hi2 - lo2) {
- int lo = lo1; lo1 = lo2; lo2 = lo;
- int hi = hi1; hi1 = hi2; hi2 = hi;
- }
+ /*
+ * Find the median of the larger part.
+ */
+ int mi1 = (lo1 + hi1) >>> 1;
+ int mi2 = hi2;
+ float key = src[mi1];
- /*
- * Small parts will be merged sequentially.
- */
- if (hi1 - lo1 < MIN_PARALLEL_MERGE_PARTS_SIZE) {
- break;
+ /*
+ * Split the smaller part.
+ */
+ for (int mi0 = lo2; mi0 < mi2; ) {
+ int mid = (mi0 + mi2) >>> 1;
+
+ if (key > src[mid]) {
+ mi0 = mid + 1;
+ } else {
+ mi2 = mid;
}
+ }
- /*
- * Find the median of the larger part.
- */
- int mi1 = (lo1 + hi1) >>> 1;
- float key = a1[mi1];
- int mi2 = hi2;
+ /*
+ * Merge the first parts in parallel.
+ */
+ merger.fork(k, lo1, mi1, lo2, mi2);
- /*
- * Partition the smaller part.
- */
- for (int loo = lo2; loo < mi2; ) {
- int t = (loo + mi2) >>> 1;
+ /*
+ * Reserve space for the second parts.
+ */
+ k += mi2 - lo2 + mi1 - lo1;
- if (key > a2[t]) {
- loo = t + 1;
- } else {
- mi2 = t;
- }
- }
+ /*
+ * Iterate along the second parts.
+ */
+ lo1 = mi1;
+ lo2 = mi2;
+ }
- int d = mi2 - lo2 + mi1 - lo1;
+ /*
+ * Check if the array is already ordered and then merge the parts.
+ */
+ if (lo1 < hi1 && lo2 < hi2 && src[hi1 - 1] > src[lo2]) {
+ mergeParts(dst, k, src, lo1, hi1, lo2, hi2);
+ } else {
+ System.arraycopy(src, lo1, dst, k, hi1 - lo1);
+ System.arraycopy(src, lo2, dst, k + hi1 - lo1, hi2 - lo2);
+ }
+ }
- /*
- * Merge the right sub-parts in parallel.
- */
- merger.forkMerger(dst, k + d, a1, mi1, hi1, a2, mi2, hi2);
+ /**
+ * Merges the sorted parts sequentially.
+ *
+ * @param dst the destination where parts are merged
+ * @param k the start index of the destination, inclusive
+ * @param src the source array
+ * @param lo1 the start index of the first part, inclusive
+ * @param hi1 the end index of the first part, exclusive
+ * @param lo2 the start index of the second part, inclusive
+ * @param hi2 the end index of the second part, exclusive
+ */
+ private static void mergeParts(float[] dst, int k,
+ float[] src, int lo1, int hi1, int lo2, int hi2) {
- /*
- * Process the sub-left parts.
- */
- hi1 = mi1;
- hi2 = mi2;
+ if (src[hi1 - 1] < src[hi2 - 1]) {
+ while (lo1 < hi1) {
+ float next = src[lo1];
+
+ if (next <= src[lo2]) {
+ dst[k++] = src[lo1++];
+ }
+ if (next >= src[lo2]) {
+ dst[k++] = src[lo2++];
+ }
+ }
+ } else if (src[hi1 - 1] > src[hi2 - 1]) {
+ while (lo2 < hi2) {
+ float next = src[lo1];
+
+ if (next <= src[lo2]) {
+ dst[k++] = src[lo1++];
+ }
+ if (next >= src[lo2]) {
+ dst[k++] = src[lo2++];
+ }
+ }
+ } else {
+ while (lo1 < hi1 && lo2 < hi2) {
+ float next = src[lo1];
+
+ if (next <= src[lo2]) {
+ dst[k++] = src[lo1++];
+ }
+ if (next >= src[lo2]) {
+ dst[k++] = src[lo2++];
+ }
}
}
/*
- * Merge small parts sequentially.
+ * Copy the tail of the left and right parts.
*/
- while (lo1 < hi1 && lo2 < hi2) {
- dst[k++] = a1[lo1] < a2[lo2] ? a1[lo1++] : a2[lo2++];
+ System.arraycopy(src, lo1, dst, k, hi1 - lo1);
+ System.arraycopy(src, lo2, dst, k, hi2 - lo2);
+ }
+
+ /**
+ * Sorts the specified range of the array using heap sort.
+ *
+ * @param a the array to be sorted
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ static void heapSort(float[] a, int low, int high) {
+ for (int k = (low + high) >>> 1; k > low; ) {
+ pushDown(a, --k, a[k], low, high);
}
- if (dst != a1 || k < lo1) {
- while (lo1 < hi1) {
- dst[k++] = a1[lo1++];
- }
+ while (--high > low) {
+ float max = a[low];
+ pushDown(a, low, a[high], low, high);
+ a[high] = max;
}
- if (dst != a2 || k < lo2) {
- while (lo2 < hi2) {
- dst[k++] = a2[lo2++];
+ }
+
+ /**
+ * Pushes specified element down during heap sort.
+ *
+ * @param a the given array
+ * @param p the start index
+ * @param value the given element
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ private static void pushDown(float[] a, int p, float value, int low, int high) {
+ for (int k ;; a[p] = a[p = k]) {
+ k = (p << 1) - low + 2; // Index of the right child
+
+ if (k > high) {
+ break;
+ }
+ if (k == high || a[k] < a[k - 1]) {
+ --k;
+ }
+ if (a[k] <= value) {
+ break;
}
}
+ a[p] = value;
}
-// [double]
+// #[double]
+
+ /**
+ * The binary representation of double negative zero.
+ */
+ private static final long DOUBLE_NEGATIVE_ZERO = Double.doubleToRawLongBits(-0.0d);
/**
* Sorts the specified range of the array using parallel merge
- * sort and/or Dual-Pivot Quicksort.
+ * sort and/or Dual-Pivot Quicksort.
*
* To balance the faster splitting and parallelism of merge sort
* with the faster element partitioning of Quicksort, ranges are
@@ -3425,15 +3633,15 @@ static void sort(double[] a, int parallelism, int low, int high) {
* turn them into positive zero, and move all NaNs
* to the end of the array.
*/
- int numNegativeZero = 0;
+ int negativeZeroCount = 0;
for (int k = high; k > low; ) {
double ak = a[--k];
- if (ak == 0.0d && Double.doubleToRawLongBits(ak) < 0) { // ak is -0.0d
- numNegativeZero += 1;
+ if (Double.doubleToRawLongBits(ak) == DOUBLE_NEGATIVE_ZERO) { // ak is -0.0d
+ negativeZeroCount++;
a[k] = 0.0d;
- } else if (ak != ak) { // ak is NaN
+ } else if (ak != ak) { // ak is Not-a-Number (NaN)
a[k] = a[--high];
a[high] = ak;
}
@@ -3443,21 +3651,17 @@ static void sort(double[] a, int parallelism, int low, int high) {
* Phase 2. Sort everything except NaNs,
* which are already in place.
*/
- int size = high - low;
-
- if (parallelism > 1 && size > MIN_PARALLEL_SORT_SIZE) {
- int depth = getDepth(parallelism, size >> 12);
- double[] b = depth == 0 ? null : new double[size];
- new Sorter(null, a, b, low, size, low, depth).invoke();
+ if (parallelism > 1 && high - low > MIN_PARALLEL_SORT_SIZE) {
+ new Sorter<>(a, parallelism, low, high - low).invoke();
} else {
sort(null, a, 0, low, high);
}
/*
- * Phase 3. Turn positive zero 0.0d
- * back into negative zero -0.0d.
+ * Phase 3. Turn the required number of positive
+ * zeros 0.0d back into negative zeros -0.0d.
*/
- if (++numNegativeZero == 1) {
+ if (++negativeZeroCount == 1) {
return;
}
@@ -3466,42 +3670,43 @@ static void sort(double[] a, int parallelism, int low, int high) {
* the index of the first zero.
*/
while (low <= high) {
- int middle = (low + high) >>> 1;
+ int mid = (low + high) >>> 1;
- if (a[middle] < 0) {
- low = middle + 1;
+ if (a[mid] < 0.0d) {
+ low = mid + 1;
} else {
- high = middle - 1;
+ high = mid - 1;
}
}
/*
- * Replace the required number of 0.0d by -0.0d.
+ * Replace 0.0d by negative zeros -0.0d.
*/
- while (--numNegativeZero > 0) {
+ while (--negativeZeroCount > 0) {
a[++high] = -0.0d;
}
}
/**
- * Sorts the specified array using the Dual-Pivot Quicksort and/or
- * other sorts in special-cases, possibly with parallel partitions.
+ * Sorts the specified range of the array using Dual-Pivot Quicksort.
*
- * @param sorter parallel context
+ * @param sorter the parallel context
* @param a the array to be sorted
* @param bits the combination of recursion depth and bit flag, where
- * the right bit "0" indicates that array is the leftmost part
+ * the right bit "0" indicates that range is the leftmost part
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
- static void sort(Sorter sorter, double[] a, int bits, int low, int high) {
+ static void sort(Sorter sorter, double[] a, int bits, int low, int high) {
while (true) {
- int end = high - 1, size = high - low;
+ int size = high - low;
+
/*
- * Run mixed insertion sort on small non-leftmost parts.
+ * Run adaptive mixed insertion sort on small non-leftmost parts.
*/
- if (size < MAX_MIXED_INSERTION_SORT_SIZE + bits && (bits & 1) > 0) {
- sort(double.class, a, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, low, high, DualPivotQuicksort::mixedInsertionSort);
+ if (size < MAX_INSERTION_SORT_SIZE + bits && (bits & 1) > 0) {
+ sort(double.class, a, Unsafe.ARRAY_DOUBLE_BASE_OFFSET,
+ low, high, DualPivotQuicksort::mixedInsertionSort);
return;
}
@@ -3509,33 +3714,25 @@ static void sort(Sorter sorter, double[] a, int bits, int low, int high) {
* Invoke insertion sort on small leftmost part.
*/
if (size < MAX_INSERTION_SORT_SIZE) {
- sort(double.class, a, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, low, high, DualPivotQuicksort::insertionSort);
+ sort(double.class, a, Unsafe.ARRAY_DOUBLE_BASE_OFFSET,
+ low, high, DualPivotQuicksort::insertionSort);
return;
}
/*
- * Check if the whole array or large non-leftmost
- * parts are nearly sorted and then merge runs.
+ * Try merging sort on large part.
*/
- if ((bits == 0 || size > MIN_TRY_MERGE_SIZE && (bits & 1) > 0)
- && tryMergeRuns(sorter, a, low, size)) {
+ if (size > MIN_MERGING_SORT_SIZE * bits
+ && tryMergingSort(sorter, a, low, high)) {
return;
}
/*
- * Switch to heap sort if execution
- * time is becoming quadratic.
+ * Divide the given array into the golden ratio using
+ * an inexpensive approximation to select five sample
+ * elements and determine pivots.
*/
- if ((bits += DELTA) > MAX_RECURSION_DEPTH) {
- heapSort(a, low, high);
- return;
- }
-
- /*
- * Use an inexpensive approximation of the golden ratio
- * to select five sample elements and determine pivots.
- */
- int step = (size >> 3) * 3 + 3;
+ int step = (size >> 2) + (size >> 3) + (size >> 7);
/*
* Five elements around (and including) the central element
@@ -3544,82 +3741,86 @@ && tryMergeRuns(sorter, a, low, size)) {
* determined to work well on a wide variety of inputs.
*/
int e1 = low + step;
- int e5 = end - step;
+ int e5 = high - step;
int e3 = (e1 + e5) >>> 1;
int e2 = (e1 + e3) >>> 1;
int e4 = (e3 + e5) >>> 1;
- double a3 = a[e3];
/*
- * Sort these elements in place by the combination
+ * Sort these elements in-place by the combination
* of 4-element sorting network and insertion sort.
*
- * 5 ------o-----------o------------
- * | |
- * 4 ------|-----o-----o-----o------
- * | | |
- * 2 ------o-----|-----o-----o------
- * | |
- * 1 ------------o-----o------------
- */
- if (a[e5] < a[e2]) { double t = a[e5]; a[e5] = a[e2]; a[e2] = t; }
- if (a[e4] < a[e1]) { double t = a[e4]; a[e4] = a[e1]; a[e1] = t; }
- if (a[e5] < a[e4]) { double t = a[e5]; a[e5] = a[e4]; a[e4] = t; }
- if (a[e2] < a[e1]) { double t = a[e2]; a[e2] = a[e1]; a[e1] = t; }
- if (a[e4] < a[e2]) { double t = a[e4]; a[e4] = a[e2]; a[e2] = t; }
-
- if (a3 < a[e2]) {
- if (a3 < a[e1]) {
- a[e3] = a[e2]; a[e2] = a[e1]; a[e1] = a3;
+ * 1 ---------o---------------o-----------------
+ * | |
+ * 2 ---------|-------o-------o-------o---------
+ * | | |
+ * 3 ---------|-------|---------------|---------
+ * | | |
+ * 4 ---------o-------|-------o-------o---------
+ * | |
+ * 5 -----------------o-------o-----------------
+ */
+ if (a[e1] > a[e4]) { double t = a[e1]; a[e1] = a[e4]; a[e4] = t; }
+ if (a[e2] > a[e5]) { double t = a[e2]; a[e2] = a[e5]; a[e5] = t; }
+ if (a[e4] > a[e5]) { double t = a[e4]; a[e4] = a[e5]; a[e5] = t; }
+ if (a[e1] > a[e2]) { double t = a[e1]; a[e1] = a[e2]; a[e2] = t; }
+ if (a[e2] > a[e4]) { double t = a[e2]; a[e2] = a[e4]; a[e4] = t; }
+
+ /*
+ * Insert the third element.
+ */
+ if (a[e3] < a[e2]) {
+ if (a[e3] < a[e1]) {
+ double t = a[e3]; a[e3] = a[e2]; a[e2] = a[e1]; a[e1] = t;
} else {
- a[e3] = a[e2]; a[e2] = a3;
+ double t = a[e3]; a[e3] = a[e2]; a[e2] = t;
}
- } else if (a3 > a[e4]) {
- if (a3 > a[e5]) {
- a[e3] = a[e4]; a[e4] = a[e5]; a[e5] = a3;
+ } else if (a[e3] > a[e4]) {
+ if (a[e3] > a[e5]) {
+ double t = a[e3]; a[e3] = a[e4]; a[e4] = a[e5]; a[e5] = t;
} else {
- a[e3] = a[e4]; a[e4] = a3;
+ double t = a[e3]; a[e3] = a[e4]; a[e4] = t;
}
}
- // Pointers
- int lower; // The index of the last element of the left part
- int upper; // The index of the first element of the right part
+ /*
+ * Switch to heap sort to avoid quadratic time.
+ */
+ if ((bits += 2) > MAX_RECURSION_DEPTH) {
+ heapSort(a, low, high);
+ return;
+ }
+
+ /*
+ * indices[0] - the index of the last element of the left part
+ * indices[1] - the index of the first element of the right part
+ */
+ int[] indices;
/*
- * Partitioning with 2 pivots in case of different elements.
+ * Partitioning with two pivots on array of fully random elements.
*/
if (a[e1] < a[e2] && a[e2] < a[e3] && a[e3] < a[e4] && a[e4] < a[e5]) {
- /*
- * Use the first and fifth of the five sorted elements as
- * the pivots. These values are inexpensive approximation
- * of tertiles. Note, that pivot1 < pivot2.
- */
- int[] pivotIndices = partition(double.class, a, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, low, high, e1, e5, DualPivotQuicksort::partitionDualPivot);
- lower = pivotIndices[0];
- upper = pivotIndices[1];
+ indices = partition(double.class, a, Unsafe.ARRAY_DOUBLE_BASE_OFFSET,
+ low, high, e1, e5, DualPivotQuicksort::partitionWithTwoPivots);
+
/*
* Sort non-left parts recursively (possibly in parallel),
* excluding known pivots.
*/
if (size > MIN_PARALLEL_SORT_SIZE && sorter != null) {
- sorter.forkSorter(bits | 1, lower + 1, upper);
- sorter.forkSorter(bits | 1, upper + 1, high);
+ sorter.fork(bits | 1, indices[0] + 1, indices[1]);
+ sorter.fork(bits | 1, indices[1] + 1, high);
} else {
- sort(sorter, a, bits | 1, lower + 1, upper);
- sort(sorter, a, bits | 1, upper + 1, high);
+ sort(sorter, a, bits | 1, indices[0] + 1, indices[1]);
+ sort(sorter, a, bits | 1, indices[1] + 1, high);
}
- } else { // Use single pivot in case of many equal elements
+ } else { // Partitioning with one pivot
- /*
- * Use the third of the five sorted elements as the pivot.
- * This value is inexpensive approximation of the median.
- */
- int[] pivotIndices = partition(double.class, a, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, low, high, e3, e3, DualPivotQuicksort::partitionSinglePivot);
- lower = pivotIndices[0];
- upper = pivotIndices[1];
+ indices = partition(double.class, a, Unsafe.ARRAY_DOUBLE_BASE_OFFSET,
+ low, high, e3, e3, DualPivotQuicksort::partitionWithOnePivot);
/*
* Sort the right part (possibly in parallel), excluding
@@ -3627,88 +3828,90 @@ && tryMergeRuns(sorter, a, low, size)) {
* equal and therefore already sorted.
*/
if (size > MIN_PARALLEL_SORT_SIZE && sorter != null) {
- sorter.forkSorter(bits | 1, upper, high);
+ sorter.fork(bits | 1, indices[1], high);
} else {
- sort(sorter, a, bits | 1, upper, high);
+ sort(sorter, a, bits | 1, indices[1], high);
}
}
- high = lower; // Iterate along the left part
+ high = indices[0]; // Iterate along the left part
}
}
/**
- * Partitions the specified range of the array using the two pivots provided.
+ * Partitions the specified range of the array using two given pivots.
*
- * @param array the array to be partitioned
+ * @param a the array for partitioning
* @param low the index of the first element, inclusive, for partitioning
* @param high the index of the last element, exclusive, for partitioning
* @param pivotIndex1 the index of pivot1, the first pivot
* @param pivotIndex2 the index of pivot2, the second pivot
- *
+ * @return indices of parts after partitioning
*/
- @ForceInline
- private static int[] partitionDualPivot(double[] a, int low, int high, int pivotIndex1, int pivotIndex2) {
- int end = high - 1;
+ private static int[] partitionWithTwoPivots(
+ double[] a, int low, int high, int pivotIndex1, int pivotIndex2) {
+ /*
+ * Pointers to the right and left parts.
+ */
+ int upper = --high;
int lower = low;
- int upper = end;
- int e1 = pivotIndex1;
- int e5 = pivotIndex2;
- double pivot1 = a[e1];
- double pivot2 = a[e5];
+ /*
+ * Use the first and fifth of the five sorted elements as
+ * the pivots. These values are inexpensive approximation
+ * of tertiles. Note, that pivot1 < pivot2.
+ */
+ double pivot1 = a[pivotIndex1];
+ double pivot2 = a[pivotIndex2];
/*
- * The first and the last elements to be sorted are moved
- * to the locations formerly occupied by the pivots. When
- * partitioning is completed, the pivots are swapped back
- * into their final positions, and excluded from the next
- * subsequent sorting.
- */
- a[e1] = a[lower];
- a[e5] = a[upper];
+ * The first and the last elements to be sorted are moved
+ * to the locations formerly occupied by the pivots. When
+ * partitioning is completed, the pivots are swapped back
+ * into their final positions, and excluded from the next
+ * subsequent sorting.
+ */
+ a[pivotIndex1] = a[lower];
+ a[pivotIndex2] = a[upper];
/*
- * Skip elements, which are less or greater than the pivots.
- */
+ * Skip elements, which are less or greater than the pivots.
+ */
while (a[++lower] < pivot1);
while (a[--upper] > pivot2);
/*
* Backward 3-interval partitioning
*
- * left part central part right part
- * +------------------------------------------------------------+
- * | < pivot1 | ? | pivot1 <= && <= pivot2 | > pivot2 |
- * +------------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * lower k upper
- *
- * Invariants:
- *
- * all in (low, lower] < pivot1
- * pivot1 <= all in (k, upper) <= pivot2
- * all in [upper, end) > pivot2
+ * left part central part right part
+ * +--------------+----------+--------------------------+--------------+
+ * | < pivot1 | ? | pivot1 <= .. <= pivot2 | > pivot2 |
+ * +--------------+----------+--------------------------+--------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
*
* Pointer k is the last index of ?-part
+ * Pointer lower is the last index of left part
+ * Pointer upper is the first index of right part
*/
for (int unused = --lower, k = ++upper; --k > lower; ) {
double ak = a[k];
- if (ak < pivot1) { // Move a[k] to the left side
- while (lower < k) {
- if (a[++lower] >= pivot1) {
- if (a[lower] > pivot2) {
- a[k] = a[--upper];
- a[upper] = a[lower];
- } else {
- a[k] = a[lower];
- }
- a[lower] = ak;
- break;
- }
+ if (ak < pivot1) { // Move a[k] to the left part
+ while (a[++lower] < pivot1);
+
+ if (lower > k) {
+ lower = k;
+ break;
+ }
+ if (a[lower] > pivot2) {
+ a[k] = a[--upper];
+ a[upper] = a[lower];
+ } else {
+ a[k] = a[lower];
}
- } else if (ak > pivot2) { // Move a[k] to the right side
+ a[lower] = ak;
+ } else if (ak > pivot2) { // Move a[k] to the right part
a[k] = a[--upper];
a[upper] = ak;
}
@@ -3717,75 +3920,77 @@ private static int[] partitionDualPivot(double[] a, int low, int high, int pivot
/*
* Swap the pivots into their final positions.
*/
- a[low] = a[lower]; a[lower] = pivot1;
- a[end] = a[upper]; a[upper] = pivot2;
+ a[low] = a[lower]; a[lower] = pivot1;
+ a[high] = a[upper]; a[upper] = pivot2;
- return new int[] {lower, upper};
+ return new int[] { lower, upper };
}
/**
- * Partitions the specified range of the array using a single pivot provided.
+ * Partitions the specified range of the array using one given pivot.
*
- * @param array the array to be partitioned
+ * @param a the array for partitioning
* @param low the index of the first element, inclusive, for partitioning
* @param high the index of the last element, exclusive, for partitioning
- * @param pivotIndex1 the index of pivot1, the first pivot
- * @param pivotIndex2 the index of pivot2, the second pivot
+ * @param pivotIndex1 the index of single pivot
+ * @param pivotIndex2 the index of single pivot
+ * @return indices of parts after partitioning
*/
- @ForceInline
- private static int[] partitionSinglePivot(double[] a, int low, int high, int pivotIndex1, int pivotIndex2) {
-
- int end = high - 1;
+ private static int[] partitionWithOnePivot(
+ double[] a, int low, int high, int pivotIndex1, int pivotIndex2) {
+ /*
+ * Pointers to the right and left parts.
+ */
+ int upper = high;
int lower = low;
- int upper = end;
- int e3 = pivotIndex1;
- double pivot = a[e3];
+ /*
+ * Use the third of the five sorted elements as the pivot.
+ * This value is inexpensive approximation of the median.
+ */
+ double pivot = a[pivotIndex1];
/*
- * The first element to be sorted is moved to the
- * location formerly occupied by the pivot. After
- * completion of partitioning the pivot is swapped
- * back into its final position, and excluded from
- * the next subsequent sorting.
- */
- a[e3] = a[lower];
+ * The first element to be sorted is moved to the
+ * location formerly occupied by the pivot. After
+ * completion of partitioning the pivot is swapped
+ * back into its final position, and excluded from
+ * the next subsequent sorting.
+ */
+ a[pivotIndex1] = a[lower];
/*
- * Traditional 3-way (Dutch National Flag) partitioning
- *
- * left part central part right part
- * +------------------------------------------------------+
- * | < pivot | ? | == pivot | > pivot |
- * +------------------------------------------------------+
- * ^ ^ ^
- * | | |
- * lower k upper
- *
- * Invariants:
+ * Dutch National Flag partitioning
*
- * all in (low, lower] < pivot
- * all in (k, upper) == pivot
- * all in [upper, end] > pivot
+ * left part central part right part
+ * +--------------+----------+--------------+-------------+
+ * | < pivot | ? | == pivot | > pivot |
+ * +--------------+----------+--------------+-------------+
+ * ^ ^ ^
+ * | | |
+ * lower k upper
*
* Pointer k is the last index of ?-part
+ * Pointer lower is the last index of left part
+ * Pointer upper is the first index of right part
*/
- for (int k = ++upper; --k > lower; ) {
+ for (int k = upper; --k > lower; ) {
double ak = a[k];
- if (ak != pivot) {
- a[k] = pivot;
+ if (ak == pivot) {
+ continue;
+ }
+ a[k] = pivot;
- if (ak < pivot) { // Move a[k] to the left side
- while (a[++lower] < pivot);
+ if (ak < pivot) { // Move a[k] to the left part
+ while (a[++lower] < pivot);
- if (a[lower] > pivot) {
- a[--upper] = a[lower];
- }
- a[lower] = ak;
- } else { // ak > pivot - Move a[k] to the right side
- a[--upper] = ak;
+ if (a[lower] > pivot) {
+ a[--upper] = a[lower];
}
+ a[lower] = ak;
+ } else { // ak > pivot - Move a[k] to the right part
+ a[--upper] = ak;
}
}
@@ -3793,14 +3998,15 @@ private static int[] partitionSinglePivot(double[] a, int low, int high, int piv
* Swap the pivot into its final position.
*/
a[low] = a[lower]; a[lower] = pivot;
- return new int[] {lower, upper};
+
+ return new int[] { lower, upper };
}
/**
- * Sorts the specified range of the array using mixed insertion sort.
+ * Sorts the specified range of the array using mixed insertion sort.
*
- * Mixed insertion sort is combination of simple insertion sort,
- * pin insertion sort and pair insertion sort.
+ * Mixed insertion sort is combination of pin insertion sort,
+ * simple insertion sort and pair insertion sort.
*
* In the context of Dual-Pivot Quicksort, the pivot element
* from the left part plays the role of sentinel, because it
@@ -3812,110 +4018,84 @@ private static int[] partitionSinglePivot(double[] a, int low, int high, int piv
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
- private static void mixedInsertionSort(double[] a, int low, int high) {
- int size = high - low;
- int end = high - 3 * ((size >> 5) << 3);
- if (end == high) {
-
- /*
- * Invoke simple insertion sort on tiny array.
- */
- for (int i; ++low < end; ) {
- double ai = a[i = low];
-
- while (ai < a[--i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = ai;
- }
- } else {
-
- /*
- * Start with pin insertion sort on small part.
- *
- * Pin insertion sort is extended simple insertion sort.
- * The main idea of this sort is to put elements larger
- * than an element called pin to the end of array (the
- * proper area for such elements). It avoids expensive
- * movements of these elements through the whole array.
- */
- double pin = a[end];
-
- for (int i, p = high; ++low < end; ) {
- double ai = a[i = low];
-
- if (ai < a[i - 1]) { // Small element
-
- /*
- * Insert small element into sorted part.
- */
- a[i] = a[--i];
-
- while (ai < a[--i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = ai;
-
- } else if (p > i && ai > pin) { // Large element
-
- /*
- * Find element smaller than pin.
- */
- while (a[--p] > pin);
+ static void mixedInsertionSort(double[] a, int low, int high) {
+ /*
+ * Split part for pin and pair insertion sorts.
+ */
+ int end = high - 3 * ((high - low) >> 3 << 1);
- /*
- * Swap it with large element.
- */
- if (p > i) {
- ai = a[p];
- a[p] = a[i];
- }
+ /*
+ * Invoke simple insertion sort on small part.
+ */
+ if (end == high) {
+ for (int i; ++low < high; ) {
+ double ai = a[i = low];
- /*
- * Insert small element into sorted part.
- */
- while (ai < a[--i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = ai;
+ while (ai < a[i - 1]) {
+ a[i] = a[--i];
}
+ a[i] = ai;
}
+ return;
+ }
+
+ /*
+ * Start with pin insertion sort.
+ */
+ for (int i, p = high; ++low < end; ) {
+ double ai = a[i = low], pin = a[--p];
/*
- * Continue with pair insertion sort on remain part.
+ * Swap larger element with pin.
*/
- for (int i; low < high; ++low) {
- double a1 = a[i = low], a2 = a[++low];
+ if (ai > pin) {
+ ai = pin;
+ a[p] = a[i];
+ }
- /*
- * Insert two elements per iteration: at first, insert the
- * larger element and then insert the smaller element, but
- * from the position where the larger element was inserted.
- */
- if (a1 > a2) {
+ /*
+ * Insert element into sorted part.
+ */
+ while (ai < a[i - 1]) {
+ a[i] = a[--i];
+ }
+ a[i] = ai;
+ }
- while (a1 < a[--i]) {
- a[i + 2] = a[i];
- }
- a[++i + 1] = a1;
+ /*
+ * Finish with pair insertion sort.
+ */
+ for (int i; low < high; ++low) {
+ double a1 = a[i = low], a2 = a[++low];
- while (a2 < a[--i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = a2;
+ /*
+ * Insert two elements per iteration: at first, insert the
+ * larger element and then insert the smaller element, but
+ * from the position where the larger element was inserted.
+ */
+ if (a1 > a2) {
- } else if (a1 < a[i - 1]) {
+ while (a1 < a[--i]) {
+ a[i + 2] = a[i];
+ }
+ a[++i + 1] = a1;
- while (a2 < a[--i]) {
- a[i + 2] = a[i];
- }
- a[++i + 1] = a2;
+ while (a2 < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = a2;
- while (a1 < a[--i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = a1;
+ } else if (a1 < a[i - 1]) {
+
+ while (a2 < a[--i]) {
+ a[i + 2] = a[i];
}
+ a[++i + 1] = a2;
+
+ while (a1 < a[--i]) {
+ a[i + 1] = a[i];
+ }
+ a[i + 1] = a1;
}
}
}
@@ -3927,90 +4107,43 @@ private static void mixedInsertionSort(double[] a, int low, int high) {
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
*/
- private static void insertionSort(double[] a, int low, int high) {
+ static void insertionSort(double[] a, int low, int high) {
for (int i, k = low; ++k < high; ) {
double ai = a[i = k];
if (ai < a[i - 1]) {
- while (--i >= low && ai < a[i]) {
- a[i + 1] = a[i];
- }
- a[i + 1] = ai;
+ do {
+ a[i] = a[--i];
+ } while (i > low && ai < a[i - 1]);
+
+ a[i] = ai;
}
}
}
/**
- * Sorts the specified range of the array using heap sort.
+ * Tries to sort the specified range of the array using merging sort.
*
+ * @param sorter the parallel context
* @param a the array to be sorted
* @param low the index of the first element, inclusive, to be sorted
* @param high the index of the last element, exclusive, to be sorted
+ * @return {@code true} if the array is finally sorted, otherwise {@code false}
*/
- private static void heapSort(double[] a, int low, int high) {
- for (int k = (low + high) >>> 1; k > low; ) {
- pushDown(a, --k, a[k], low, high);
- }
- while (--high > low) {
- double max = a[low];
- pushDown(a, low, a[high], low, high);
- a[high] = max;
- }
- }
-
- /**
- * Pushes specified element down during heap sort.
- *
- * @param a the given array
- * @param p the start index
- * @param value the given element
- * @param low the index of the first element, inclusive, to be sorted
- * @param high the index of the last element, exclusive, to be sorted
- */
- private static void pushDown(double[] a, int p, double value, int low, int high) {
- for (int k ;; a[p] = a[p = k]) {
- k = (p << 1) - low + 2; // Index of the right child
-
- if (k > high) {
- break;
- }
- if (k == high || a[k] < a[k - 1]) {
- --k;
- }
- if (a[k] <= value) {
- break;
- }
- }
- a[p] = value;
- }
-
- /**
- * Tries to sort the specified range of the array.
- *
- * @param sorter parallel context
- * @param a the array to be sorted
- * @param low the index of the first element to be sorted
- * @param size the array size
- * @return true if finally sorted, false otherwise
- */
- private static boolean tryMergeRuns(Sorter sorter, double[] a, int low, int size) {
-
+ static boolean tryMergingSort(Sorter sorter, double[] a, int low, int high) {
/*
- * The run array is constructed only if initial runs are
- * long enough to continue, run[i] then holds start index
- * of the i-th sequence of elements in non-descending order.
+ * The element run[i] holds the start index
+ * of i-th sequence in non-descending order.
*/
+ int count = 1;
int[] run = null;
- int high = low + size;
- int count = 1, last = low;
/*
* Identify all possible runs.
*/
- for (int k = low + 1; k < high; ) {
-
+ for (int k = low + 1, last = low; k < high; ) {
/*
- * Find the end index of the current run.
+ * Find the next run.
*/
if (a[k - 1] < a[k]) {
@@ -4026,81 +4159,79 @@ private static boolean tryMergeRuns(Sorter sorter, double[] a, int low, int size
for (int i = last - 1, j = k; ++i < --j && a[i] > a[j]; ) {
double ai = a[i]; a[i] = a[j]; a[j] = ai;
}
+
+ // Check the next sequence
+ if (k < high && a[k - 1] < a[k]) {
+ continue;
+ }
+
} else { // Identify constant sequence
for (double ak = a[k]; ++k < high && ak == a[k]; );
+ // Check the next sequence
if (k < high) {
continue;
}
}
/*
- * Check special cases.
+ * Process the current run.
*/
if (run == null) {
- if (k == high) {
+ if (k == high) {
/*
- * The array is monotonous sequence,
+ * Array is monotonous sequence
* and therefore already sorted.
*/
return true;
}
-
- if (k - low < MIN_FIRST_RUN_SIZE) {
-
- /*
- * The first run is too small
- * to proceed with scanning.
- */
- return false;
- }
-
- run = new int[((size >> 10) | 0x7F) & 0x3FF];
+ run = new int[Math.min((high - low) >> 6, MAX_RUN_CAPACITY) | 8];
run[0] = low;
- } else if (a[last - 1] > a[last]) {
-
- if (count > (k - low) >> MIN_FIRST_RUNS_FACTOR) {
+ } else if (a[last - 1] > a[last]) { // Start the new run
+ if (k - low < count * MIN_RUN_SIZE) {
/*
- * The first runs are not long
- * enough to continue scanning.
+ * Terminate the scanning,
+ * if the runs are too small.
*/
return false;
}
- if (++count == MAX_RUN_CAPACITY) {
-
+ if (++count == run.length) {
/*
* Array is not highly structured.
*/
return false;
}
+ }
- if (count == run.length) {
+ /*
+ * Save the current run.
+ */
+ run[count] = (last = k);
- /*
- * Increase capacity of index array.
- */
- run = Arrays.copyOf(run, count << 1);
- }
+ /*
+ * Check single-element run at the end.
+ */
+ if (++k == high) {
+ --k;
}
- run[count] = (last = k);
}
/*
- * Merge runs of highly structured array.
+ * Merge all runs.
*/
if (count > 1) {
double[] b; int offset = low;
- if (sorter == null || (b = (double[]) sorter.b) == null) {
- b = new double[size];
- } else {
+ if (sorter != null && (b = sorter.b) != null) {
offset = sorter.offset;
+ } else if ((b = tryAllocate(double[].class, high - low)) == null) {
+ return false;
}
- mergeRuns(a, b, offset, 1, sorter != null, run, 0, count);
+ mergeRuns(sorter, a, b, offset, true, run, 0, count);
}
return true;
}
@@ -4108,166 +4239,261 @@ private static boolean tryMergeRuns(Sorter sorter, double[] a, int low, int size
/**
* Merges the specified runs.
*
+ * @param sorter the parallel context
* @param a the source array
- * @param b the temporary buffer used in merging
+ * @param b the buffer for merging
* @param offset the start index in the source, inclusive
- * @param aim specifies merging: to source ( > 0), buffer ( < 0) or any ( == 0)
- * @param parallel indicates whether merging is performed in parallel
+ * @param aim whether the original array is used for merging
* @param run the start indexes of the runs, inclusive
* @param lo the start index of the first run, inclusive
* @param hi the start index of the last run, inclusive
- * @return the destination where runs are merged
*/
- private static double[] mergeRuns(double[] a, double[] b, int offset,
- int aim, boolean parallel, int[] run, int lo, int hi) {
+ private static void mergeRuns(Sorter sorter, double[] a, double[] b, int offset,
+ boolean aim, int[] run, int lo, int hi) {
if (hi - lo == 1) {
- if (aim >= 0) {
- return a;
+ if (!aim) {
+ System.arraycopy(a, run[lo], b, run[lo] - offset, run[hi] - run[lo]);
}
- for (int i = run[hi], j = i - offset, low = run[lo]; i > low;
- b[--j] = a[--i]
- );
- return b;
+ return;
}
/*
- * Split into approximately equal parts.
+ * Split the array into two approximately equal parts.
*/
- int mi = lo, rmi = (run[lo] + run[hi]) >>> 1;
- while (run[++mi + 1] <= rmi);
+ int mi = lo, key = (run[lo] + run[hi]) >>> 1;
+ while (run[++mi + 1] <= key);
/*
- * Merge the left and right parts.
+ * Merge the runs of all parts.
*/
- double[] a1, a2;
+ mergeRuns(sorter, a, b, offset, !aim, run, lo, mi);
+ mergeRuns(sorter, a, b, offset, !aim, run, mi, hi);
- if (parallel && hi - lo > MIN_RUN_COUNT) {
- RunMerger merger = new RunMerger(a, b, offset, 0, run, mi, hi).forkMe();
- a1 = mergeRuns(a, b, offset, -aim, true, run, lo, mi);
- a2 = (double[]) merger.getDestination();
- } else {
- a1 = mergeRuns(a, b, offset, -aim, false, run, lo, mi);
- a2 = mergeRuns(a, b, offset, 0, false, run, mi, hi);
- }
+ double[] dst = aim ? a : b;
+ double[] src = aim ? b : a;
- double[] dst = a1 == a ? b : a;
+ int k = !aim ? run[lo] - offset : run[lo];
+ int lo1 = aim ? run[lo] - offset : run[lo];
+ int hi1 = aim ? run[mi] - offset : run[mi];
+ int lo2 = aim ? run[mi] - offset : run[mi];
+ int hi2 = aim ? run[hi] - offset : run[hi];
- int k = a1 == a ? run[lo] - offset : run[lo];
- int lo1 = a1 == b ? run[lo] - offset : run[lo];
- int hi1 = a1 == b ? run[mi] - offset : run[mi];
- int lo2 = a2 == b ? run[mi] - offset : run[mi];
- int hi2 = a2 == b ? run[hi] - offset : run[hi];
-
- if (parallel) {
- new Merger(null, dst, k, a1, lo1, hi1, a2, lo2, hi2).invoke();
+ /*
+ * Merge the left and right parts.
+ */
+ if (hi1 - lo1 > MIN_PARALLEL_SORT_SIZE && sorter != null) {
+ new Merger<>(null, dst, k, src, lo1, hi1, lo2, hi2).invoke();
} else {
- mergeParts(null, dst, k, a1, lo1, hi1, a2, lo2, hi2);
+ mergeParts(dst, k, src, lo1, hi1, lo2, hi2);
}
- return dst;
}
/**
- * Merges the sorted parts.
+ * Merges the sorted parts in parallel.
*
- * @param merger parallel context
+ * @param merger the parallel context
* @param dst the destination where parts are merged
* @param k the start index of the destination, inclusive
- * @param a1 the first part
+ * @param src the source array
* @param lo1 the start index of the first part, inclusive
* @param hi1 the end index of the first part, exclusive
- * @param a2 the second part
* @param lo2 the start index of the second part, inclusive
* @param hi2 the end index of the second part, exclusive
*/
- private static void mergeParts(Merger merger, double[] dst, int k,
- double[] a1, int lo1, int hi1, double[] a2, int lo2, int hi2) {
+ private static void mergeParts(Merger merger, double[] dst, int k,
+ double[] src, int lo1, int hi1, int lo2, int hi2) {
- if (merger != null && a1 == a2) {
+ while (true) {
+ /*
+ * The first part must be larger.
+ */
+ if (hi1 - lo1 < hi2 - lo2) {
+ int lo = lo1; lo1 = lo2; lo2 = lo;
+ int hi = hi1; hi1 = hi2; hi2 = hi;
+ }
- while (true) {
+ /*
+ * Merge the small parts sequentially.
+ */
+ if (hi1 - lo1 < MIN_PARALLEL_SORT_SIZE) {
+ break;
+ }
- /*
- * The first part must be larger.
- */
- if (hi1 - lo1 < hi2 - lo2) {
- int lo = lo1; lo1 = lo2; lo2 = lo;
- int hi = hi1; hi1 = hi2; hi2 = hi;
- }
+ /*
+ * Find the median of the larger part.
+ */
+ int mi1 = (lo1 + hi1) >>> 1;
+ int mi2 = hi2;
+ double key = src[mi1];
- /*
- * Small parts will be merged sequentially.
- */
- if (hi1 - lo1 < MIN_PARALLEL_MERGE_PARTS_SIZE) {
- break;
+ /*
+ * Split the smaller part.
+ */
+ for (int mi0 = lo2; mi0 < mi2; ) {
+ int mid = (mi0 + mi2) >>> 1;
+
+ if (key > src[mid]) {
+ mi0 = mid + 1;
+ } else {
+ mi2 = mid;
}
+ }
- /*
- * Find the median of the larger part.
- */
- int mi1 = (lo1 + hi1) >>> 1;
- double key = a1[mi1];
- int mi2 = hi2;
+ /*
+ * Merge the first parts in parallel.
+ */
+ merger.fork(k, lo1, mi1, lo2, mi2);
- /*
- * Partition the smaller part.
- */
- for (int loo = lo2; loo < mi2; ) {
- int t = (loo + mi2) >>> 1;
+ /*
+ * Reserve space for the second parts.
+ */
+ k += mi2 - lo2 + mi1 - lo1;
- if (key > a2[t]) {
- loo = t + 1;
- } else {
- mi2 = t;
- }
- }
+ /*
+ * Iterate along the second parts.
+ */
+ lo1 = mi1;
+ lo2 = mi2;
+ }
- int d = mi2 - lo2 + mi1 - lo1;
+ /*
+ * Check if the array is already ordered and then merge the parts.
+ */
+ if (lo1 < hi1 && lo2 < hi2 && src[hi1 - 1] > src[lo2]) {
+ mergeParts(dst, k, src, lo1, hi1, lo2, hi2);
+ } else {
+ System.arraycopy(src, lo1, dst, k, hi1 - lo1);
+ System.arraycopy(src, lo2, dst, k + hi1 - lo1, hi2 - lo2);
+ }
+ }
- /*
- * Merge the right sub-parts in parallel.
- */
- merger.forkMerger(dst, k + d, a1, mi1, hi1, a2, mi2, hi2);
+ /**
+ * Merges the sorted parts sequentially.
+ *
+ * @param dst the destination where parts are merged
+ * @param k the start index of the destination, inclusive
+ * @param src the source array
+ * @param lo1 the start index of the first part, inclusive
+ * @param hi1 the end index of the first part, exclusive
+ * @param lo2 the start index of the second part, inclusive
+ * @param hi2 the end index of the second part, exclusive
+ */
+ private static void mergeParts(double[] dst, int k,
+ double[] src, int lo1, int hi1, int lo2, int hi2) {
- /*
- * Process the sub-left parts.
- */
- hi1 = mi1;
- hi2 = mi2;
+ if (src[hi1 - 1] < src[hi2 - 1]) {
+ while (lo1 < hi1) {
+ double next = src[lo1];
+
+ if (next <= src[lo2]) {
+ dst[k++] = src[lo1++];
+ }
+ if (next >= src[lo2]) {
+ dst[k++] = src[lo2++];
+ }
+ }
+ } else if (src[hi1 - 1] > src[hi2 - 1]) {
+ while (lo2 < hi2) {
+ double next = src[lo1];
+
+ if (next <= src[lo2]) {
+ dst[k++] = src[lo1++];
+ }
+ if (next >= src[lo2]) {
+ dst[k++] = src[lo2++];
+ }
+ }
+ } else {
+ while (lo1 < hi1 && lo2 < hi2) {
+ double next = src[lo1];
+
+ if (next <= src[lo2]) {
+ dst[k++] = src[lo1++];
+ }
+ if (next >= src[lo2]) {
+ dst[k++] = src[lo2++];
+ }
}
}
/*
- * Merge small parts sequentially.
+ * Copy the tail of the left and right parts.
*/
- while (lo1 < hi1 && lo2 < hi2) {
- dst[k++] = a1[lo1] < a2[lo2] ? a1[lo1++] : a2[lo2++];
+ System.arraycopy(src, lo1, dst, k, hi1 - lo1);
+ System.arraycopy(src, lo2, dst, k, hi2 - lo2);
+ }
+
+ /**
+ * Sorts the specified range of the array using heap sort.
+ *
+ * @param a the array to be sorted
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ static void heapSort(double[] a, int low, int high) {
+ for (int k = (low + high) >>> 1; k > low; ) {
+ pushDown(a, --k, a[k], low, high);
}
- if (dst != a1 || k < lo1) {
- while (lo1 < hi1) {
- dst[k++] = a1[lo1++];
- }
+ while (--high > low) {
+ double max = a[low];
+ pushDown(a, low, a[high], low, high);
+ a[high] = max;
}
- if (dst != a2 || k < lo2) {
- while (lo2 < hi2) {
- dst[k++] = a2[lo2++];
+ }
+
+ /**
+ * Pushes specified element down during heap sort.
+ *
+ * @param a the given array
+ * @param p the start index
+ * @param value the given element
+ * @param low the index of the first element, inclusive, to be sorted
+ * @param high the index of the last element, exclusive, to be sorted
+ */
+ private static void pushDown(double[] a, int p, double value, int low, int high) {
+ for (int k ;; a[p] = a[p = k]) {
+ k = (p << 1) - low + 2; // Index of the right child
+
+ if (k > high) {
+ break;
+ }
+ if (k == high || a[k] < a[k - 1]) {
+ --k;
+ }
+ if (a[k] <= value) {
+ break;
}
}
+ a[p] = value;
}
-// [class]
+// #[class]
/**
- * This class implements parallel sorting.
+ * Implementation of parallel sorting.
*/
- private static final class Sorter extends CountedCompleter {
- private static final long serialVersionUID = 20180818L;
+ private static final class Sorter extends CountedCompleter {
+
+ @java.io.Serial
+ private static final long serialVersionUID = 123456789L;
+
@SuppressWarnings("serial")
- private final Object a, b;
+ private final T a, b;
private final int low, size, offset, depth;
+ @SuppressWarnings("unchecked")
+ private Sorter(T a, int parallelism, int low, int size) {
+ this.a = a;
+ this.low = low;
+ this.size = size;
+ this.offset = low;
+ this.b = (T) tryAllocate(a.getClass(), size);
+ this.depth = b == null ? 0 : ((parallelism >> 7) + 2) * (-2);
+ }
+
private Sorter(CountedCompleter> parent,
- Object a, Object b, int low, int size, int offset, int depth) {
+ T a, T b, int low, int size, int offset, int depth) {
super(parent);
this.a = a;
this.b = b;
@@ -4278,152 +4504,112 @@ private Sorter(CountedCompleter> parent,
}
@Override
- public final void compute() {
+ @SuppressWarnings("unchecked")
+ public void compute() {
if (depth < 0) {
setPendingCount(2);
int half = size >> 1;
- new Sorter(this, b, a, low, half, offset, depth + 1).fork();
- new Sorter(this, b, a, low + half, size - half, offset, depth + 1).compute();
+ new Sorter<>(this, b, a, low, half, offset, depth + 1).fork();
+ new Sorter<>(this, b, a, low + half, size - half, offset, depth + 1).compute();
} else {
- if (a instanceof int[]) {
- sort(this, (int[]) a, depth, low, low + size);
- } else if (a instanceof long[]) {
- sort(this, (long[]) a, depth, low, low + size);
- } else if (a instanceof float[]) {
- sort(this, (float[]) a, depth, low, low + size);
- } else if (a instanceof double[]) {
- sort(this, (double[]) a, depth, low, low + size);
- } else {
- throw new IllegalArgumentException(
- "Unknown type of array: " + a.getClass().getName());
+ switch(a) {
+ case int[] ai -> sort((Sorter) this, ai, depth, low, low + size);
+ case long[] al -> sort((Sorter) this, al, depth, low, low + size);
+ case float[] af -> sort((Sorter) this, af, depth, low, low + size);
+ case double[] ad -> sort((Sorter) this, ad, depth, low, low + size);
+ default -> throw new IllegalArgumentException("Unknown array: " + a.getClass().getName());
}
}
tryComplete();
}
@Override
- public final void onCompletion(CountedCompleter> caller) {
+ public void onCompletion(CountedCompleter> caller) {
if (depth < 0) {
int mi = low + (size >> 1);
boolean src = (depth & 1) == 0;
- new Merger(null,
+ new Merger<>(null,
a,
src ? low : low - offset,
b,
src ? low - offset : low,
src ? mi - offset : mi,
- b,
src ? mi - offset : mi,
src ? low + size - offset : low + size
).invoke();
}
}
- private void forkSorter(int depth, int low, int high) {
+ private void fork(int depth, int low, int high) {
addToPendingCount(1);
- Object a = this.a; // Use local variable for performance
- new Sorter(this, a, b, low, high - low, offset, depth).fork();
+ new Sorter<>(this, a, b, low, high - low, offset, depth).fork();
}
}
/**
- * This class implements parallel merging.
+ * Implementation of parallel merging.
*/
- private static final class Merger extends CountedCompleter {
- private static final long serialVersionUID = 20180818L;
+ private static final class Merger extends CountedCompleter {
+
+ @java.io.Serial
+ private static final long serialVersionUID = 123456789L;
+
@SuppressWarnings("serial")
- private final Object dst, a1, a2;
+ private final T dst, src;
private final int k, lo1, hi1, lo2, hi2;
- private Merger(CountedCompleter> parent, Object dst, int k,
- Object a1, int lo1, int hi1, Object a2, int lo2, int hi2) {
+ private Merger(CountedCompleter> parent, T dst, int k,
+ T src, int lo1, int hi1, int lo2, int hi2) {
super(parent);
this.dst = dst;
this.k = k;
- this.a1 = a1;
+ this.src = src;
this.lo1 = lo1;
this.hi1 = hi1;
- this.a2 = a2;
this.lo2 = lo2;
this.hi2 = hi2;
}
@Override
- public final void compute() {
- if (dst instanceof int[]) {
- mergeParts(this, (int[]) dst, k,
- (int[]) a1, lo1, hi1, (int[]) a2, lo2, hi2);
- } else if (dst instanceof long[]) {
- mergeParts(this, (long[]) dst, k,
- (long[]) a1, lo1, hi1, (long[]) a2, lo2, hi2);
- } else if (dst instanceof float[]) {
- mergeParts(this, (float[]) dst, k,
- (float[]) a1, lo1, hi1, (float[]) a2, lo2, hi2);
- } else if (dst instanceof double[]) {
- mergeParts(this, (double[]) dst, k,
- (double[]) a1, lo1, hi1, (double[]) a2, lo2, hi2);
- } else {
- throw new IllegalArgumentException(
- "Unknown type of array: " + dst.getClass().getName());
+ @SuppressWarnings("unchecked")
+ public void compute() {
+ switch(dst) {
+ case int[] di -> mergeParts((Merger) this, di, k, (int[]) src, lo1, hi1, lo2, hi2);
+ case long[] dl -> mergeParts((Merger) this, dl, k, (long[]) src, lo1, hi1, lo2, hi2);
+ case float[] df -> mergeParts((Merger) this, df, k, (float[]) src, lo1, hi1, lo2, hi2);
+ case double[] dd -> mergeParts((Merger) this, dd, k, (double[]) src, lo1, hi1, lo2, hi2);
+ default -> throw new IllegalArgumentException("Unknown array: " + dst.getClass().getName());
}
propagateCompletion();
}
- private void forkMerger(Object dst, int k,
- Object a1, int lo1, int hi1, Object a2, int lo2, int hi2) {
+ private void fork(int k, int lo1, int hi1, int lo2, int hi2) {
addToPendingCount(1);
- new Merger(this, dst, k, a1, lo1, hi1, a2, lo2, hi2).fork();
+ new Merger<>(this, dst, k, src, lo1, hi1, lo2, hi2).fork();
}
}
/**
- * This class implements parallel merging of runs.
+ * Tries to allocate additional buffer.
+ *
+ * @param the class of array
+ * @param clazz the given array class
+ * @param length the length of additional buffer
+ * @return {@code null} if requested buffer is too big or there is no enough memory,
+ * otherwise created buffer
*/
- private static final class RunMerger extends RecursiveTask {
- private static final long serialVersionUID = 20180818L;
- @SuppressWarnings("serial")
- private final Object a, b;
- private final int[] run;
- private final int offset, aim, lo, hi;
-
- private RunMerger(Object a, Object b, int offset,
- int aim, int[] run, int lo, int hi) {
- this.a = a;
- this.b = b;
- this.offset = offset;
- this.aim = aim;
- this.run = run;
- this.lo = lo;
- this.hi = hi;
- }
-
- @Override
- protected final Object compute() {
- if (a instanceof int[]) {
- return mergeRuns((int[]) a, (int[]) b, offset, aim, true, run, lo, hi);
- }
- if (a instanceof long[]) {
- return mergeRuns((long[]) a, (long[]) b, offset, aim, true, run, lo, hi);
- }
- if (a instanceof float[]) {
- return mergeRuns((float[]) a, (float[]) b, offset, aim, true, run, lo, hi);
- }
- if (a instanceof double[]) {
- return mergeRuns((double[]) a, (double[]) b, offset, aim, true, run, lo, hi);
- }
- throw new IllegalArgumentException(
- "Unknown type of array: " + a.getClass().getName());
- }
-
- private RunMerger forkMe() {
- fork();
- return this;
- }
-
- private Object getDestination() {
- join();
- return getRawResult();
+ @SuppressWarnings("unchecked")
+ private static T tryAllocate(Class clazz, int length) {
+ try {
+ int maxLength = MAX_BUFFER_SIZE >>
+ (clazz == int[].class || clazz == float[].class ? 2 : 3);
+ return length > maxLength ? null :
+ (T) U.allocateUninitializedArray(clazz.componentType(), length);
+ } catch (OutOfMemoryError e) {
+ return null;
}
}
+
+ private static final Unsafe U = Unsafe.getUnsafe();
}
diff --git a/test/jdk/java/util/Arrays/Sorting.java b/test/jdk/java/util/Arrays/Sorting.java
index f285b0c65b72c..980a799647d4c 100644
--- a/test/jdk/java/util/Arrays/Sorting.java
+++ b/test/jdk/java/util/Arrays/Sorting.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -24,7 +24,7 @@
/*
* @test
* @compile/module=java.base java/util/SortingHelper.java
- * @bug 6880672 6896573 6899694 6976036 7013585 7018258 8003981 8226297
+ * @bug 6880672 6896573 6899694 6976036 7013585 7018258 8003981 8226297 8266431
* @build Sorting
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:DisableIntrinsic=_arraySort,_arrayPartition Sorting -shortrun
* @run main/othervm -XX:-TieredCompilation -XX:CompileCommand=CompileThresholdScaling,java.util.DualPivotQuicksort::sort,0.0001 Sorting -shortrun
@@ -36,7 +36,7 @@
*/
import java.io.PrintStream;
-import java.util.Comparator;
+import java.util.Arrays;
import java.util.Random;
import java.util.SortingHelper;
@@ -45,29 +45,36 @@ public class Sorting {
private static final PrintStream out = System.out;
private static final PrintStream err = System.err;
- // Array lengths used in a long run (default)
- private static final int[] LONG_RUN_LENGTHS = {
- 1, 3, 8, 21, 55, 100, 1_000, 10_000, 100_000 };
+ // Lengths of arrays for [mixed] insertion sort
+ private static final int[] RUN_LENGTHS =
+ { 1, 2, 14, 100, 500, 1_000 };
- // Array lengths used in a short run
- private static final int[] SHORT_RUN_LENGTHS = {
- 1, 8, 55, 100, 10_000 };
+ // Lengths of arrays for short run
+ private static final int[] SHORT_LENGTHS =
+ { 1, 2, 14, 100, 500, 1_000, 11_000 };
- // Random initial values used in a long run (default)
- private static final TestRandom[] LONG_RUN_RANDOMS = {
- TestRandom.BABA, TestRandom.DEDA, TestRandom.C0FFEE };
+ // Lengths of arrays for long run (default)
+ private static final int[] LONG_LENGTHS =
+ { 1, 2, 14, 100, 500, 1_000, 11_000, 50_000};
- // Random initial values used in a short run
- private static final TestRandom[] SHORT_RUN_RANDOMS = {
- TestRandom.C0FFEE };
+ // Initial random values for short run
+ private static final TestRandom[] SHORT_RANDOMS =
+ {TestRandom.C0FFEE};
- // Constants used in subarray sorting
+ // Initial random values for long run (default)
+ private static final TestRandom[] LONG_RANDOMS =
+ {TestRandom.DEDA, TestRandom.BABA, TestRandom.C0FFEE};
+
+ // Constant to fill the left part of array
private static final int A380 = 0xA380;
+
+ // Constant to fill the right part of array
private static final int B747 = 0xB747;
private final SortingHelper sortingHelper;
private final TestRandom[] randoms;
private final int[] lengths;
+ private final boolean withMin;
private Object[] gold;
private Object[] test;
@@ -75,327 +82,137 @@ public static void main(String[] args) {
long start = System.currentTimeMillis();
boolean shortRun = args.length > 0 && args[0].equals("-shortrun");
- int[] lengths = shortRun ? SHORT_RUN_LENGTHS : LONG_RUN_LENGTHS;
- TestRandom[] randoms = shortRun ? SHORT_RUN_RANDOMS : LONG_RUN_RANDOMS;
+ int[] lengths = shortRun ? SHORT_LENGTHS : LONG_LENGTHS;
+ TestRandom[] randoms = shortRun ? SHORT_RANDOMS : LONG_RANDOMS;
+
+ new Sorting(SortingHelper.INSERTION_SORT, randoms, false).testBase();
+ new Sorting(SortingHelper.MIXED_INSERTION_SORT, randoms, true).testBase();
+ new Sorting(SortingHelper.MERGING_SORT, randoms, lengths).testStructured();
+
+ new Sorting(SortingHelper.HEAP_SORT, randoms, lengths).testBase();
+ new Sorting(SortingHelper.COUNTING_SORT, randoms, lengths).testBase();
new Sorting(SortingHelper.DUAL_PIVOT_QUICKSORT, randoms, lengths).testCore();
- new Sorting(SortingHelper.PARALLEL_SORT, randoms, lengths).testCore();
- new Sorting(SortingHelper.HEAP_SORT, randoms, lengths).testBasic();
+ new Sorting(SortingHelper.PARALLEL_QUICKSORT, randoms, lengths).testCore();
+
new Sorting(SortingHelper.ARRAYS_SORT, randoms, lengths).testAll();
new Sorting(SortingHelper.ARRAYS_PARALLEL_SORT, randoms, lengths).testAll();
long end = System.currentTimeMillis();
- out.format("PASSED in %d sec.\n", (end - start) / 1000);
+ out.format("PASSED in %d sec.\n", (end - start) / 1_000);
+ }
+
+ private Sorting(SortingHelper sortingHelper, TestRandom[] randoms, boolean withMin) {
+ this(sortingHelper, randoms, RUN_LENGTHS, withMin);
}
private Sorting(SortingHelper sortingHelper, TestRandom[] randoms, int[] lengths) {
+ this(sortingHelper, randoms, lengths, false);
+ }
+
+ private Sorting(SortingHelper sortingHelper, TestRandom[] randoms, int[] lengths, boolean withMin) {
this.sortingHelper = sortingHelper;
this.randoms = randoms;
this.lengths = lengths;
+ this.withMin = withMin;
}
- private void testBasic() {
+ private void testBase() {
testEmptyArray();
for (int length : lengths) {
- createData(length);
- testBasic(length);
- }
- }
+ createArray(length);
+ testStructured(length);
- private void testBasic(int length) {
- for (TestRandom random : randoms) {
- testWithInsertionSort(length, random);
- testWithCheckSum(length, random);
- testWithScrambling(length, random);
+ for (TestRandom random : randoms) {
+ testWithCheckSum(length, random);
+ testWithInsertionSort(length, random);
+ testWithScrambling(length, random);
+ }
}
}
private void testCore() {
- for (int length : lengths) {
- createData(length);
- testCore(length);
- }
- }
-
- private void testCore(int length) {
- testBasic(length);
+ testBase();
- for (TestRandom random : randoms) {
- testMergingSort(length, random);
- testSubArray(length, random);
- testNegativeZero(length, random);
- testFloatingPointSorting(length, random);
- }
- }
-
- private void testAll() {
for (int length : lengths) {
- createData(length);
- testAll(length);
- }
- }
-
- private void testAll(int length) {
- testCore(length);
-
- for (TestRandom random : randoms) {
- testRange(length, random);
- testStability(length, random);
- }
- }
-
- private void testEmptyArray() {
- testEmptyAndNullIntArray();
- testEmptyAndNullLongArray();
- testEmptyAndNullByteArray();
- testEmptyAndNullCharArray();
- testEmptyAndNullShortArray();
- testEmptyAndNullFloatArray();
- testEmptyAndNullDoubleArray();
- }
-
- private void testStability(int length, TestRandom random) {
- printTestName("Test stability", random, length);
-
- Pair[] a = build(length, random);
- sortingHelper.sort(a);
- checkSorted(a);
- checkStable(a);
-
- a = build(length, random);
- sortingHelper.sort(a, pairComparator);
- checkSorted(a);
- checkStable(a);
+ createArray(length);
- out.println();
- }
-
- private void testEmptyAndNullIntArray() {
- sortingHelper.sort(new int[] {});
- sortingHelper.sort(new int[] {}, 0, 0);
-
- try {
- sortingHelper.sort(null);
- } catch (NullPointerException expected) {
- try {
- sortingHelper.sort(null, 0, 0);
- } catch (NullPointerException expected2) {
- return;
- }
- fail(sortingHelper + "(int[],fromIndex,toIndex) shouldn't " +
- "catch null array");
- }
- fail(sortingHelper + "(int[]) shouldn't catch null array");
- }
-
- private void testEmptyAndNullLongArray() {
- sortingHelper.sort(new long[] {});
- sortingHelper.sort(new long[] {}, 0, 0);
-
- try {
- sortingHelper.sort(null);
- } catch (NullPointerException expected) {
- try {
- sortingHelper.sort(null, 0, 0);
- } catch (NullPointerException expected2) {
- return;
+ for (TestRandom random : randoms) {
+ testNegativeZero(length, random);
+ testFloatingPointSorting(length, random);
}
- fail(sortingHelper + "(long[],fromIndex,toIndex) shouldn't " +
- "catch null array");
}
- fail(sortingHelper + "(long[]) shouldn't catch null array");
}
- private void testEmptyAndNullByteArray() {
- sortingHelper.sort(new byte[] {});
- sortingHelper.sort(new byte[] {}, 0, 0);
+ private void testAll() {
+ testCore();
- try {
- sortingHelper.sort(null);
- } catch (NullPointerException expected) {
- try {
- sortingHelper.sort(null, 0, 0);
- } catch (NullPointerException expected2) {
- return;
- }
- fail(sortingHelper + "(byte[],fromIndex,toIndex) shouldn't " +
- "catch null array");
+ for (int length : lengths) {
+ createArray(length);
+ sortRange(length);
}
- fail(sortingHelper + "(byte[]) shouldn't catch null array");
}
- private void testEmptyAndNullCharArray() {
- sortingHelper.sort(new char[] {});
- sortingHelper.sort(new char[] {}, 0, 0);
-
- try {
- sortingHelper.sort(null);
- } catch (NullPointerException expected) {
- try {
- sortingHelper.sort(null, 0, 0);
- } catch (NullPointerException expected2) {
- return;
- }
- fail(sortingHelper + "(char[],fromIndex,toIndex) shouldn't " +
- "catch null array");
- }
- fail(sortingHelper + "(char[]) shouldn't catch null array");
- }
+ private void testEmptyArray() {
+ sortingHelper.sort(new int[]{});
+ sortingHelper.sort(new int[]{}, 0, 0);
- private void testEmptyAndNullShortArray() {
- sortingHelper.sort(new short[] {});
- sortingHelper.sort(new short[] {}, 0, 0);
+ sortingHelper.sort(new long[]{});
+ sortingHelper.sort(new long[]{}, 0, 0);
- try {
- sortingHelper.sort(null);
- } catch (NullPointerException expected) {
- try {
- sortingHelper.sort(null, 0, 0);
- } catch (NullPointerException expected2) {
- return;
- }
- fail(sortingHelper + "(short[],fromIndex,toIndex) shouldn't " +
- "catch null array");
- }
- fail(sortingHelper + "(short[]) shouldn't catch null array");
- }
+ sortingHelper.sort(new byte[]{});
+ sortingHelper.sort(new byte[]{}, 0, 0);
- private void testEmptyAndNullFloatArray() {
- sortingHelper.sort(new float[] {});
- sortingHelper.sort(new float[] {}, 0, 0);
+ sortingHelper.sort(new char[]{});
+ sortingHelper.sort(new char[]{}, 0, 0);
- try {
- sortingHelper.sort(null);
- } catch (NullPointerException expected) {
- try {
- sortingHelper.sort(null, 0, 0);
- } catch (NullPointerException expected2) {
- return;
- }
- fail(sortingHelper + "(float[],fromIndex,toIndex) shouldn't " +
- "catch null array");
- }
- fail(sortingHelper + "(float[]) shouldn't catch null array");
- }
+ sortingHelper.sort(new short[]{});
+ sortingHelper.sort(new short[]{}, 0, 0);
- private void testEmptyAndNullDoubleArray() {
- sortingHelper.sort(new double[] {});
- sortingHelper.sort(new double[] {}, 0, 0);
+ sortingHelper.sort(new float[]{});
+ sortingHelper.sort(new float[]{}, 0, 0);
- try {
- sortingHelper.sort(null);
- } catch (NullPointerException expected) {
- try {
- sortingHelper.sort(null, 0, 0);
- } catch (NullPointerException expected2) {
- return;
- }
- fail(sortingHelper + "(double[],fromIndex,toIndex) shouldn't " +
- "catch null array");
- }
- fail(sortingHelper + "(double[]) shouldn't catch null array");
+ sortingHelper.sort(new double[]{});
+ sortingHelper.sort(new double[]{}, 0, 0);
}
- private void testSubArray(int length, TestRandom random) {
- if (length < 4) {
- return;
- }
- for (int m = 1; m < length / 2; m <<= 1) {
- int fromIndex = m;
- int toIndex = length - m;
-
- prepareSubArray((int[]) gold[0], fromIndex, toIndex);
- convertData(length);
-
- for (int i = 0; i < test.length; i++) {
- printTestName("Test subarray", random, length,
- ", m = " + m + ", " + getType(i));
- sortingHelper.sort(test[i], fromIndex, toIndex);
- checkSubArray(test[i], fromIndex, toIndex);
- }
- }
- out.println();
- }
+ private void sortRange(int length) {
+ int[] a = (int[]) gold[0];
- private void testRange(int length, TestRandom random) {
- if (length < 2) {
- return;
- }
for (int m = 1; m < length; m <<= 1) {
- for (int i = 1; i <= length; i++) {
- ((int[]) gold[0]) [i - 1] = i % m + m % i;
+ for (int i = 1; i <= length; ++i) {
+ a[i - 1] = i % m + m % i;
}
- convertData(length);
+ convertArray(m / 4);
- for (int i = 0; i < test.length; i++) {
- printTestName("Test range check", random, length,
- ", m = " + m + ", " + getType(i));
- checkRange(test[i], m);
+ for (int i = 0; i < test.length; ++i) {
+ printTestName("Test range check", length,
+ ", m = " + m + ", " + getType(i));
+ sortRange(test[i], m);
}
}
out.println();
}
- private void checkSorted(Pair[] a) {
- for (int i = 0; i < a.length - 1; i++) {
- if (a[i].getKey() > a[i + 1].getKey()) {
- fail("Array is not sorted at " + i + "-th position: " +
- a[i].getKey() + " and " + a[i + 1].getKey());
- }
- }
- }
-
- private void checkStable(Pair[] a) {
- for (int i = 0; i < a.length / 4; ) {
- int key1 = a[i].getKey();
- int value1 = a[i++].getValue();
- int key2 = a[i].getKey();
- int value2 = a[i++].getValue();
- int key3 = a[i].getKey();
- int value3 = a[i++].getValue();
- int key4 = a[i].getKey();
- int value4 = a[i++].getValue();
-
- if (!(key1 == key2 && key2 == key3 && key3 == key4)) {
- fail("Keys are different " + key1 + ", " + key2 + ", " +
- key3 + ", " + key4 + " at position " + i);
- }
- if (!(value1 < value2 && value2 < value3 && value3 < value4)) {
- fail("Sorting is not stable at position " + i +
- ". Second values have been changed: " + value1 + ", " +
- value2 + ", " + value3 + ", " + value4);
- }
- }
- }
-
- private Pair[] build(int length, Random random) {
- Pair[] a = new Pair[length * 4];
-
- for (int i = 0; i < a.length; ) {
- int key = random.nextInt();
- a[i++] = new Pair(key, 1);
- a[i++] = new Pair(key, 2);
- a[i++] = new Pair(key, 3);
- a[i++] = new Pair(key, 4);
- }
- return a;
- }
-
private void testWithInsertionSort(int length, TestRandom random) {
- if (length > 1000) {
+ if (length > 1_000) {
return;
}
+ int[] a = (int[]) gold[0];
+
for (int m = 1; m <= length; m <<= 1) {
for (UnsortedBuilder builder : UnsortedBuilder.values()) {
- builder.build((int[]) gold[0], m, random);
- convertData(length);
+ builder.build(a, m, random);
+ int shift = m / 4;
+ convertArray(shift);
- for (int i = 0; i < test.length; i++) {
+ for (int i = 0; i < test.length; ++i) {
printTestName("Test with insertion sort", random, length,
- ", m = " + m + ", " + getType(i) + " " + builder);
- sortingHelper.sort(test[i]);
- sortByInsertionSort(gold[i]);
+ ", m = " + m + ", " + getType(i) + " " + builder);
+ sortingHelper.sort(test[i], shift, length - shift);
+ sortByInsertionSort(gold[i], shift, length - shift);
+ checkSorted(gold[i], shift);
compare(test[i], gold[i]);
}
}
@@ -403,22 +220,29 @@ private void testWithInsertionSort(int length, TestRandom random) {
out.println();
}
- private void testMergingSort(int length, TestRandom random) {
- if (length < (4 << 10)) { // DualPivotQuicksort.MIN_TRY_MERGE_SIZE
+ private void testStructured() {
+ for (int length : lengths) {
+ createArray(length);
+ testStructured(length);
+ }
+ }
+
+ private void testStructured(int length) {
+ if (length < 512) {
return;
}
- final int PERIOD = 50;
+ int[] a = (int[]) gold[0];
- for (int m = PERIOD - 2; m <= PERIOD + 2; m++) {
- for (MergingBuilder builder : MergingBuilder.values()) {
- builder.build((int[]) gold[0], m);
- convertData(length);
+ for (int m = 1; m < 8; ++m) {
+ for (StructuredBuilder builder : StructuredBuilder.values()) {
+ builder.build(a, m);
+ convertArray(0);
- for (int i = 0; i < test.length; i++) {
- printTestName("Test merging sort", random, length,
- ", m = " + m + ", " + getType(i) + " " + builder);
- sortingHelper.sort(test[i]);
- checkSorted(test[i]);
+ for (int i = 0; i < test.length; ++i) {
+ printTestName("Test structured", length,
+ ", m = " + m + ", " + getType(i) + " " + builder);
+ sortingHelper.sort(test[i]/*, shift, length - shift*/);
+ checkSorted(test[i], 0);
}
}
}
@@ -426,16 +250,19 @@ private void testMergingSort(int length, TestRandom random) {
}
private void testWithCheckSum(int length, TestRandom random) {
+ int[] a = (int[]) gold[0];
+
for (int m = 1; m <= length; m <<= 1) {
for (UnsortedBuilder builder : UnsortedBuilder.values()) {
- builder.build((int[]) gold[0], m, random);
- convertData(length);
+ builder.build(a, m, random);
+ int shift = m / 4;
+ convertArray(shift);
- for (int i = 0; i < test.length; i++) {
+ for (int i = 0; i < test.length; ++i) {
printTestName("Test with check sum", random, length,
- ", m = " + m + ", " + getType(i) + " " + builder);
- sortingHelper.sort(test[i]);
- checkWithCheckSum(test[i], gold[i]);
+ ", m = " + m + ", " + getType(i) + " " + builder);
+ sortingHelper.sort(test[i], shift, length - shift);
+ checkWithCheckSum(test[i], gold[i], shift);
}
}
}
@@ -443,14 +270,16 @@ private void testWithCheckSum(int length, TestRandom random) {
}
private void testWithScrambling(int length, TestRandom random) {
+ int[] a = (int[]) gold[0];
+
for (int m = 1; m <= length; m <<= 1) {
for (SortedBuilder builder : SortedBuilder.values()) {
- builder.build((int[]) gold[0], m);
- convertData(length);
+ builder.build(a, m);
+ convertArray(0);
- for (int i = 0; i < test.length; i++) {
+ for (int i = 0; i < test.length; ++i) {
printTestName("Test with scrambling", random, length,
- ", m = " + m + ", " + getType(i) + " " + builder);
+ ", m = " + m + ", " + getType(i) + " " + builder);
scramble(test[i], random);
sortingHelper.sort(test[i]);
compare(test[i], gold[i]);
@@ -461,10 +290,10 @@ private void testWithScrambling(int length, TestRandom random) {
}
private void testNegativeZero(int length, TestRandom random) {
- for (int i = 5; i < test.length; i++) {
+ for (int i = 5; i < test.length; ++i) {
printTestName("Test negative zero -0.0", random, length, " " + getType(i));
- NegativeZeroBuilder builder = NegativeZeroBuilder.values() [i - 5];
+ NegativeZeroBuilder builder = NegativeZeroBuilder.values()[i - 5];
builder.build(test[i], random);
sortingHelper.sort(test[i]);
@@ -474,29 +303,30 @@ private void testNegativeZero(int length, TestRandom random) {
}
private void testFloatingPointSorting(int length, TestRandom random) {
- if (length < 2) {
+ if (length < 6) {
return;
}
- final int MAX = 13;
+ final int MAX = 14;
+ int s = 4;
- for (int a = 0; a < MAX; a++) {
- for (int g = 0; g < MAX; g++) {
- for (int z = 0; z < MAX; z++) {
- for (int n = 0; n < MAX; n++) {
- for (int p = 0; p < MAX; p++) {
- if (a + g + z + n + p != length) {
+ for (int k = 0; k < MAX; ++k) {
+ for (int g = 0; g < MAX; ++g) {
+ for (int z = 0; z < MAX; ++z) {
+ for (int n = 0; n < MAX; ++n) {
+ for (int p = 0; p < MAX; ++p) {
+ if (k + g + z + n + p + s != length) {
continue;
}
- for (int i = 5; i < test.length; i++) {
+ for (int i = 5; i < test.length; ++i) {
printTestName("Test float-pointing sorting", random, length,
- ", a = " + a + ", g = " + g + ", z = " + z +
- ", n = " + n + ", p = " + p + ", " + getType(i));
+ ", k = " + k + ", g = " + g + ", z = " + z +
+ ", n = " + n + ", p = " + p + ", " + getType(i));
FloatingPointBuilder builder = FloatingPointBuilder.values()[i - 5];
- builder.build(gold[i], a, g, z, n, p, random);
+ builder.build(gold[i], k, g, z, n, p, random);
copy(test[i], gold[i]);
scramble(test[i], random);
sortingHelper.sort(test[i]);
- compare(test[i], gold[i], a, n, g);
+ compare(test[i], gold[i], k, n + 2, g);
}
}
}
@@ -504,158 +334,179 @@ private void testFloatingPointSorting(int length, TestRandom random) {
}
}
- for (int m = 13; m > 4; m--) {
- int t = length / m;
- int g = t, z = t, n = t, p = t;
- int a = length - g - z - n - p;
+ for (int m = MAX; m > 4; --m) {
+ int g = length / m;
+ int k = length - g - g - g - g - s;
- for (int i = 5; i < test.length; i++) {
+ for (int i = 5; i < test.length; ++i) {
printTestName("Test float-pointing sorting", random, length,
- ", a = " + a + ", g = " + g + ", z = " + z +
- ", n = " + n + ", p = " + p + ", " + getType(i));
- FloatingPointBuilder builder = FloatingPointBuilder.values() [i - 5];
- builder.build(gold[i], a, g, z, n, p, random);
+ ", k = " + k + ", g = " + g + ", z = " + g +
+ ", n = " + g + ", p = " + g + ", " + getType(i));
+ FloatingPointBuilder builder = FloatingPointBuilder.values()[i - 5];
+ builder.build(gold[i], k, g, g, g, g, random);
copy(test[i], gold[i]);
scramble(test[i], random);
sortingHelper.sort(test[i]);
- compare(test[i], gold[i], a, n, g);
+ compare(test[i], gold[i], k, g + 2, g);
}
}
out.println();
}
- private void prepareSubArray(int[] a, int fromIndex, int toIndex) {
- for (int i = 0; i < fromIndex; i++) {
- a[i] = A380;
- }
- int middle = (fromIndex + toIndex) >>> 1;
- int k = 0;
-
- for (int i = fromIndex; i < middle; i++) {
- a[i] = k++;
- }
-
- for (int i = middle; i < toIndex; i++) {
- a[i] = k--;
- }
-
- for (int i = toIndex; i < a.length; i++) {
- a[i] = B747;
- }
- }
-
private void scramble(Object a, Random random) {
- if (a instanceof int[]) {
- scramble((int[]) a, random);
- } else if (a instanceof long[]) {
- scramble((long[]) a, random);
- } else if (a instanceof byte[]) {
- scramble((byte[]) a, random);
- } else if (a instanceof char[]) {
- scramble((char[]) a, random);
- } else if (a instanceof short[]) {
- scramble((short[]) a, random);
- } else if (a instanceof float[]) {
- scramble((float[]) a, random);
- } else if (a instanceof double[]) {
- scramble((double[]) a, random);
- } else {
- fail("Unknown type of array: " + a.getClass().getName());
+ switch (a) {
+ case int[] ai -> scramble(ai, random);
+ case long[] al -> scramble(al, random);
+ case byte[] ab -> scramble(ab, random);
+ case char[] ac -> scramble(ac, random);
+ case short[] as -> scramble(as, random);
+ case float[] af -> scramble(af, random);
+ case double[] ad -> scramble(ad, random);
+ default -> fail(a);
}
}
private void scramble(int[] a, Random random) {
- for (int i = 0; i < a.length * 7; i++) {
- swap(a, random.nextInt(a.length), random.nextInt(a.length));
+ if (withMin) {
+ for (int i = 7; i < a.length * 7; ++i) {
+ swap(a, random.nextInt(a.length - 1) + 1, random.nextInt(a.length - 1) + 1);
+ }
+ } else {
+ for (int i = 0; i < a.length * 7; ++i) {
+ swap(a, random.nextInt(a.length), random.nextInt(a.length));
+ }
}
}
private void scramble(long[] a, Random random) {
- for (int i = 0; i < a.length * 7; i++) {
- swap(a, random.nextInt(a.length), random.nextInt(a.length));
+ if (withMin) {
+ for (int i = 7; i < a.length * 7; ++i) {
+ swap(a, random.nextInt(a.length - 1) + 1, random.nextInt(a.length - 1) + 1);
+ }
+ } else {
+ for (int i = 1; i < a.length * 7; ++i) {
+ swap(a, random.nextInt(a.length), random.nextInt(a.length));
+ }
}
}
private void scramble(byte[] a, Random random) {
- for (int i = 0; i < a.length * 7; i++) {
- swap(a, random.nextInt(a.length), random.nextInt(a.length));
+ if (withMin) {
+ for (int i = 7; i < a.length * 7; ++i) {
+ swap(a, random.nextInt(a.length - 1) + 1, random.nextInt(a.length - 1) + 1);
+ }
+ } else {
+ for (int i = 1; i < a.length * 7; ++i) {
+ swap(a, random.nextInt(a.length), random.nextInt(a.length));
+ }
}
}
private void scramble(char[] a, Random random) {
- for (int i = 0; i < a.length * 7; i++) {
- swap(a, random.nextInt(a.length), random.nextInt(a.length));
+ if (withMin) {
+ for (int i = 7; i < a.length * 7; ++i) {
+ swap(a, random.nextInt(a.length - 1) + 1, random.nextInt(a.length - 1) + 1);
+ }
+ } else {
+ for (int i = 1; i < a.length * 7; ++i) {
+ swap(a, random.nextInt(a.length), random.nextInt(a.length));
+ }
}
}
private void scramble(short[] a, Random random) {
- for (int i = 0; i < a.length * 7; i++) {
- swap(a, random.nextInt(a.length), random.nextInt(a.length));
+ if (withMin) {
+ for (int i = 7; i < a.length * 7; ++i) {
+ swap(a, random.nextInt(a.length - 1) + 1, random.nextInt(a.length - 1) + 1);
+ }
+ } else {
+ for (int i = 1; i < a.length * 7; ++i) {
+ swap(a, random.nextInt(a.length), random.nextInt(a.length));
+ }
}
}
private void scramble(float[] a, Random random) {
- for (int i = 0; i < a.length * 7; i++) {
- swap(a, random.nextInt(a.length), random.nextInt(a.length));
+ if (withMin) {
+ for (int i = 7; i < a.length * 7; ++i) {
+ swap(a, random.nextInt(a.length - 1) + 1, random.nextInt(a.length - 1) + 1);
+ }
+ } else {
+ for (int i = 1; i < a.length * 7; ++i) {
+ swap(a, random.nextInt(a.length), random.nextInt(a.length));
+ }
}
}
private void scramble(double[] a, Random random) {
- for (int i = 0; i < a.length * 7; i++) {
- swap(a, random.nextInt(a.length), random.nextInt(a.length));
+ if (withMin) {
+ for (int i = 7; i < a.length * 7; ++i) {
+ swap(a, random.nextInt(a.length - 1) + 1, random.nextInt(a.length - 1) + 1);
+ }
+ } else {
+ for (int i = 1; i < a.length * 7; ++i) {
+ swap(a, random.nextInt(a.length), random.nextInt(a.length));
+ }
}
}
private void swap(int[] a, int i, int j) {
- int t = a[i]; a[i] = a[j]; a[j] = t;
+ int t = a[i];
+ a[i] = a[j];
+ a[j] = t;
}
private void swap(long[] a, int i, int j) {
- long t = a[i]; a[i] = a[j]; a[j] = t;
+ long t = a[i];
+ a[i] = a[j];
+ a[j] = t;
}
private void swap(byte[] a, int i, int j) {
- byte t = a[i]; a[i] = a[j]; a[j] = t;
+ byte t = a[i];
+ a[i] = a[j];
+ a[j] = t;
}
private void swap(char[] a, int i, int j) {
- char t = a[i]; a[i] = a[j]; a[j] = t;
+ char t = a[i];
+ a[i] = a[j];
+ a[j] = t;
}
private void swap(short[] a, int i, int j) {
- short t = a[i]; a[i] = a[j]; a[j] = t;
+ short t = a[i];
+ a[i] = a[j];
+ a[j] = t;
}
private void swap(float[] a, int i, int j) {
- float t = a[i]; a[i] = a[j]; a[j] = t;
+ float t = a[i];
+ a[i] = a[j];
+ a[j] = t;
}
private void swap(double[] a, int i, int j) {
- double t = a[i]; a[i] = a[j]; a[j] = t;
+ double t = a[i];
+ a[i] = a[j];
+ a[j] = t;
}
- private void checkWithCheckSum(Object test, Object gold) {
- checkSorted(test);
+ private void checkWithCheckSum(Object test, Object gold, int m) {
+ checkSorted(test, m);
checkCheckSum(test, gold);
}
- private void fail(String message) {
- err.format("\n*** TEST FAILED ***\n\n%s\n\n", message);
- throw new RuntimeException("Test failed");
- }
-
private void checkNegativeZero(Object a) {
- if (a instanceof float[]) {
- checkNegativeZero((float[]) a);
- } else if (a instanceof double[]) {
- checkNegativeZero((double[]) a);
- } else {
- fail("Unknown type of array: " + a.getClass().getName());
+ switch (a) {
+ case float[] af -> checkNegativeZero(af);
+ case double[] ad -> checkNegativeZero(ad);
+ default -> fail(a);
}
}
private void checkNegativeZero(float[] a) {
- for (int i = 0; i < a.length - 1; i++) {
+ for (int i = 0; i < a.length - 1; ++i) {
if (Float.floatToRawIntBits(a[i]) == 0 && Float.floatToRawIntBits(a[i + 1]) < 0) {
fail(a[i] + " before " + a[i + 1] + " at position " + i);
}
@@ -663,7 +514,7 @@ private void checkNegativeZero(float[] a) {
}
private void checkNegativeZero(double[] a) {
- for (int i = 0; i < a.length - 1; i++) {
+ for (int i = 0; i < a.length - 1; ++i) {
if (Double.doubleToRawLongBits(a[i]) == 0 && Double.doubleToRawLongBits(a[i + 1]) < 0) {
fail(a[i] + " before " + a[i + 1] + " at position " + i);
}
@@ -671,30 +522,28 @@ private void checkNegativeZero(double[] a) {
}
private void compare(Object a, Object b, int numNaN, int numNeg, int numNegZero) {
- if (a instanceof float[]) {
- compare((float[]) a, (float[]) b, numNaN, numNeg, numNegZero);
- } else if (a instanceof double[]) {
- compare((double[]) a, (double[]) b, numNaN, numNeg, numNegZero);
- } else {
- fail("Unknown type of array: " + a.getClass().getName());
+ switch (a) {
+ case float[] af -> compare(af, (float[]) b, numNaN, numNeg, numNegZero);
+ case double[] ad -> compare(ad, (double[]) b, numNaN, numNeg, numNegZero);
+ default -> fail(a);
}
}
private void compare(float[] a, float[] b, int numNaN, int numNeg, int numNegZero) {
- for (int i = a.length - numNaN; i < a.length; i++) {
- if (a[i] == a[i]) {
+ for (int i = a.length - numNaN; i < a.length; ++i) {
+ if (!Float.isNaN(a[i])) {
fail("There must be NaN instead of " + a[i] + " at position " + i);
}
}
final int NEGATIVE_ZERO = Float.floatToIntBits(-0.0f);
- for (int i = numNeg; i < numNeg + numNegZero; i++) {
- if (NEGATIVE_ZERO != Float.floatToIntBits(a[i])) {
+ for (int i = numNeg; i < numNeg + numNegZero; ++i) {
+ if (Float.floatToIntBits(a[i]) != NEGATIVE_ZERO) {
fail("There must be -0.0 instead of " + a[i] + " at position " + i);
}
}
- for (int i = 0; i < a.length - numNaN; i++) {
+ for (int i = 0; i < a.length - numNaN; ++i) {
if (a[i] != b[i]) {
fail("There must be " + b[i] + " instead of " + a[i] + " at position " + i);
}
@@ -702,20 +551,20 @@ private void compare(float[] a, float[] b, int numNaN, int numNeg, int numNegZer
}
private void compare(double[] a, double[] b, int numNaN, int numNeg, int numNegZero) {
- for (int i = a.length - numNaN; i < a.length; i++) {
- if (a[i] == a[i]) {
+ for (int i = a.length - numNaN; i < a.length; ++i) {
+ if (!Double.isNaN(a[i])) {
fail("There must be NaN instead of " + a[i] + " at position " + i);
}
}
final long NEGATIVE_ZERO = Double.doubleToLongBits(-0.0d);
- for (int i = numNeg; i < numNeg + numNegZero; i++) {
- if (NEGATIVE_ZERO != Double.doubleToLongBits(a[i])) {
+ for (int i = numNeg; i < numNeg + numNegZero; ++i) {
+ if (Double.doubleToLongBits(a[i]) != NEGATIVE_ZERO) {
fail("There must be -0.0 instead of " + a[i] + " at position " + i);
}
}
- for (int i = 0; i < a.length - numNaN; i++) {
+ for (int i = 0; i < a.length - numNaN; ++i) {
if (a[i] != b[i]) {
fail("There must be " + b[i] + " instead of " + a[i] + " at position " + i);
}
@@ -723,27 +572,20 @@ private void compare(double[] a, double[] b, int numNaN, int numNeg, int numNegZ
}
private void compare(Object a, Object b) {
- if (a instanceof int[]) {
- compare((int[]) a, (int[]) b);
- } else if (a instanceof long[]) {
- compare((long[]) a, (long[]) b);
- } else if (a instanceof byte[]) {
- compare((byte[]) a, (byte[]) b);
- } else if (a instanceof char[]) {
- compare((char[]) a, (char[]) b);
- } else if (a instanceof short[]) {
- compare((short[]) a, (short[]) b);
- } else if (a instanceof float[]) {
- compare((float[]) a, (float[]) b);
- } else if (a instanceof double[]) {
- compare((double[]) a, (double[]) b);
- } else {
- fail("Unknown type of array: " + a.getClass().getName());
+ switch (a) {
+ case int[] ai -> compare(ai, (int[]) b);
+ case long[] al -> compare(al, (long[]) b);
+ case byte[] ab -> compare(ab, (byte[]) b);
+ case char[] ac -> compare(ac, (char[]) b);
+ case short[] as -> compare(as, (short[]) b);
+ case float[] af -> compare(af, (float[]) b);
+ case double[] ad -> compare(ad, (double[]) b);
+ default -> fail(a);
}
}
private void compare(int[] a, int[] b) {
- for (int i = 0; i < a.length; i++) {
+ for (int i = 0; i < a.length; ++i) {
if (a[i] != b[i]) {
fail("There must be " + b[i] + " instead of " + a[i] + " at position " + i);
}
@@ -751,7 +593,7 @@ private void compare(int[] a, int[] b) {
}
private void compare(long[] a, long[] b) {
- for (int i = 0; i < a.length; i++) {
+ for (int i = 0; i < a.length; ++i) {
if (a[i] != b[i]) {
fail("There must be " + b[i] + " instead of " + a[i] + " at position " + i);
}
@@ -759,7 +601,7 @@ private void compare(long[] a, long[] b) {
}
private void compare(byte[] a, byte[] b) {
- for (int i = 0; i < a.length; i++) {
+ for (int i = 0; i < a.length; ++i) {
if (a[i] != b[i]) {
fail("There must be " + b[i] + " instead of " + a[i] + " at position " + i);
}
@@ -767,7 +609,7 @@ private void compare(byte[] a, byte[] b) {
}
private void compare(char[] a, char[] b) {
- for (int i = 0; i < a.length; i++) {
+ for (int i = 0; i < a.length; ++i) {
if (a[i] != b[i]) {
fail("There must be " + b[i] + " instead of " + a[i] + " at position " + i);
}
@@ -775,7 +617,7 @@ private void compare(char[] a, char[] b) {
}
private void compare(short[] a, short[] b) {
- for (int i = 0; i < a.length; i++) {
+ for (int i = 0; i < a.length; ++i) {
if (a[i] != b[i]) {
fail("There must be " + b[i] + " instead of " + a[i] + " at position " + i);
}
@@ -783,7 +625,7 @@ private void compare(short[] a, short[] b) {
}
private void compare(float[] a, float[] b) {
- for (int i = 0; i < a.length; i++) {
+ for (int i = 0; i < a.length; ++i) {
if (a[i] != b[i]) {
fail("There must be " + b[i] + " instead of " + a[i] + " at position " + i);
}
@@ -791,7 +633,7 @@ private void compare(float[] a, float[] b) {
}
private void compare(double[] a, double[] b) {
- for (int i = 0; i < a.length; i++) {
+ for (int i = 0; i < a.length; ++i) {
if (a[i] != b[i]) {
fail("There must be " + b[i] + " instead of " + a[i] + " at position " + i);
}
@@ -801,105 +643,155 @@ private void compare(double[] a, double[] b) {
private String getType(int i) {
Object a = test[i];
- if (a instanceof int[]) {
- return "INT ";
- }
- if (a instanceof long[]) {
- return "LONG ";
- }
- if (a instanceof byte[]) {
- return "BYTE ";
- }
- if (a instanceof char[]) {
- return "CHAR ";
- }
- if (a instanceof short[]) {
- return "SHORT ";
- }
- if (a instanceof float[]) {
- return "FLOAT ";
- }
- if (a instanceof double[]) {
- return "DOUBLE";
- }
- fail("Unknown type of array: " + a.getClass().getName());
- return null;
+ return switch (a) {
+ case int[] _ -> "INT ";
+ case long[] _ -> "LONG ";
+ case byte[] _ -> "BYTE ";
+ case char[] _ -> "CHAR ";
+ case short[] _ -> "SHORT ";
+ case float[] _ -> "FLOAT ";
+ case double[] _ -> "DOUBLE";
+ default -> null;
+ };
}
- private void checkSorted(Object a) {
- if (a instanceof int[]) {
- checkSorted((int[]) a);
- } else if (a instanceof long[]) {
- checkSorted((long[]) a);
- } else if (a instanceof byte[]) {
- checkSorted((byte[]) a);
- } else if (a instanceof char[]) {
- checkSorted((char[]) a);
- } else if (a instanceof short[]) {
- checkSorted((short[]) a);
- } else if (a instanceof float[]) {
- checkSorted((float[]) a);
- } else if (a instanceof double[]) {
- checkSorted((double[]) a);
- } else {
- fail("Unknown type of array: " + a.getClass().getName());
+ private void checkSorted(Object a, int m) {
+ switch (a) {
+ case int[] ai -> checkSorted(ai, m);
+ case long[] al -> checkSorted(al, m);
+ case byte[] ab -> checkSorted(ab, m);
+ case char[] ac -> checkSorted(ac, m);
+ case short[] as -> checkSorted(as, m);
+ case float[] af -> checkSorted(af, m);
+ case double[] ad -> checkSorted(ad, m);
+ default -> fail(a);
}
}
- private void checkSorted(int[] a) {
- for (int i = 0; i < a.length - 1; i++) {
+ private void checkSorted(int[] a, int m) {
+ for (int i = 0; i < m; ++i) {
+ if (a[i] != A380) {
+ fail("Sort changes left element at position " + i + hex(a[i], A380));
+ }
+ }
+ for (int i = m; i < a.length - m - 1; ++i) {
if (a[i] > a[i + 1]) {
fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
}
}
+ for (int i = a.length - m; i < a.length; ++i) {
+ if (a[i] != B747) {
+ fail("Sort changes right element at position " + i + hex(a[i], B747));
+ }
+ }
}
- private void checkSorted(long[] a) {
- for (int i = 0; i < a.length - 1; i++) {
+ private void checkSorted(long[] a, int m) {
+ for (int i = 0; i < m; ++i) {
+ if (a[i] != toLong(A380)) {
+ fail("Sort changes left element at position " + i + hex(a[i], A380));
+ }
+ }
+ for (int i = m; i < a.length - m - 1; ++i) {
if (a[i] > a[i + 1]) {
fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
}
}
+ for (int i = a.length - m; i < a.length; ++i) {
+ if (a[i] != toLong(B747)) {
+ fail("Sort changes right element at position " + i + hex(a[i], B747));
+ }
+ }
}
- private void checkSorted(byte[] a) {
- for (int i = 0; i < a.length - 1; i++) {
+ private void checkSorted(byte[] a, int m) {
+ for (int i = 0; i < m; ++i) {
+ if (a[i] != (byte) A380) {
+ fail("Sort changes left element at position " + i + hex(a[i], A380));
+ }
+ }
+ for (int i = m; i < a.length - m - 1; ++i) {
if (a[i] > a[i + 1]) {
fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
}
}
+ for (int i = a.length - m; i < a.length; ++i) {
+ if (a[i] != (byte) B747) {
+ fail("Sort changes right element at position " + i + hex(a[i], B747));
+ }
+ }
}
- private void checkSorted(char[] a) {
- for (int i = 0; i < a.length - 1; i++) {
+ private void checkSorted(char[] a, int m) {
+ for (int i = 0; i < m; ++i) {
+ if (a[i] != (char) A380) {
+ fail("Sort changes left element at position " + i + hex(a[i], A380));
+ }
+ }
+ for (int i = m; i < a.length - m - 1; ++i) {
if (a[i] > a[i + 1]) {
fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
}
}
+ for (int i = a.length - m; i < a.length; ++i) {
+ if (a[i] != (char) B747) {
+ fail("Sort changes right element at position " + i + hex(a[i], B747));
+ }
+ }
}
- private void checkSorted(short[] a) {
- for (int i = 0; i < a.length - 1; i++) {
+ private void checkSorted(short[] a, int m) {
+ for (int i = 0; i < m; ++i) {
+ if (a[i] != (short) A380) {
+ fail("Sort changes left element at position " + i + hex(a[i], A380));
+ }
+ }
+ for (int i = m; i < a.length - m - 1; ++i) {
if (a[i] > a[i + 1]) {
fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
}
}
+ for (int i = a.length - m; i < a.length; ++i) {
+ if (a[i] != (short) B747) {
+ fail("Sort changes right element at position " + i + hex(a[i], B747));
+ }
+ }
}
- private void checkSorted(float[] a) {
- for (int i = 0; i < a.length - 1; i++) {
+ private void checkSorted(float[] a, int m) {
+ for (int i = 0; i < m; ++i) {
+ if (a[i] != (float) A380) {
+ fail("Sort changes left element at position " + i + hex((long) a[i], A380));
+ }
+ }
+ for (int i = m; i < a.length - m - 1; ++i) {
if (a[i] > a[i + 1]) {
fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
}
}
+ for (int i = a.length - m; i < a.length; ++i) {
+ if (a[i] != (float) B747) {
+ fail("Sort changes right element at position " + i + hex((long) a[i], B747));
+ }
+ }
}
- private void checkSorted(double[] a) {
- for (int i = 0; i < a.length - 1; i++) {
+ private void checkSorted(double[] a, int m) {
+ for (int i = 0; i < m; ++i) {
+ if (a[i] != toDouble(A380)) {
+ fail("Sort changes left element at position " + i + hex((long) a[i], A380));
+ }
+ }
+ for (int i = m; i < a.length - m - 1; ++i) {
if (a[i] > a[i + 1]) {
fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
}
}
+ for (int i = a.length - m; i < a.length; ++i) {
+ if (a[i] != B747) {
+ fail("Sort changes right element at position " + i + hex((long) a[i], B747));
+ }
+ }
}
private void checkCheckSum(Object test, Object gold) {
@@ -912,29 +804,16 @@ private void checkCheckSum(Object test, Object gold) {
}
private int checkSumXor(Object a) {
- if (a instanceof int[]) {
- return checkSumXor((int[]) a);
- }
- if (a instanceof long[]) {
- return checkSumXor((long[]) a);
- }
- if (a instanceof byte[]) {
- return checkSumXor((byte[]) a);
- }
- if (a instanceof char[]) {
- return checkSumXor((char[]) a);
- }
- if (a instanceof short[]) {
- return checkSumXor((short[]) a);
- }
- if (a instanceof float[]) {
- return checkSumXor((float[]) a);
- }
- if (a instanceof double[]) {
- return checkSumXor((double[]) a);
- }
- fail("Unknown type of array: " + a.getClass().getName());
- return -1;
+ return switch (a) {
+ case int[] ai -> checkSumXor(ai);
+ case long[] al -> checkSumXor(al);
+ case byte[] ab -> checkSumXor(ab);
+ case char[] ac -> checkSumXor(ac);
+ case short[] as -> checkSumXor(as);
+ case float[] af -> checkSumXor(af);
+ case double[] ad -> checkSumXor(ad);
+ default -> -1;
+ };
}
private int checkSumXor(int[] a) {
@@ -961,7 +840,7 @@ private int checkSumXor(byte[] a) {
for (byte e : a) {
checkSum ^= e;
}
- return (int) checkSum;
+ return checkSum;
}
private int checkSumXor(char[] a) {
@@ -970,7 +849,7 @@ private int checkSumXor(char[] a) {
for (char e : a) {
checkSum ^= e;
}
- return (int) checkSum;
+ return checkSum;
}
private int checkSumXor(short[] a) {
@@ -979,7 +858,7 @@ private int checkSumXor(short[] a) {
for (short e : a) {
checkSum ^= e;
}
- return (int) checkSum;
+ return checkSum;
}
private int checkSumXor(float[] a) {
@@ -1001,29 +880,16 @@ private int checkSumXor(double[] a) {
}
private int checkSumPlus(Object a) {
- if (a instanceof int[]) {
- return checkSumPlus((int[]) a);
- }
- if (a instanceof long[]) {
- return checkSumPlus((long[]) a);
- }
- if (a instanceof byte[]) {
- return checkSumPlus((byte[]) a);
- }
- if (a instanceof char[]) {
- return checkSumPlus((char[]) a);
- }
- if (a instanceof short[]) {
- return checkSumPlus((short[]) a);
- }
- if (a instanceof float[]) {
- return checkSumPlus((float[]) a);
- }
- if (a instanceof double[]) {
- return checkSumPlus((double[]) a);
- }
- fail("Unknown type of array: " + a.getClass().getName());
- return -1;
+ return switch (a) {
+ case int[] ai -> checkSumPlus(ai);
+ case long[] al -> checkSumPlus(al);
+ case byte[] ab -> checkSumPlus(ab);
+ case char[] ac -> checkSumPlus(ac);
+ case short[] as -> checkSumPlus(as);
+ case float[] af -> checkSumPlus(af);
+ case double[] ad -> checkSumPlus(ad);
+ default -> -1;
+ };
}
private int checkSumPlus(int[] a) {
@@ -1050,7 +916,7 @@ private int checkSumPlus(byte[] a) {
for (byte e : a) {
checkSum += e;
}
- return (int) checkSum;
+ return checkSum;
}
private int checkSumPlus(char[] a) {
@@ -1059,7 +925,7 @@ private int checkSumPlus(char[] a) {
for (char e : a) {
checkSum += e;
}
- return (int) checkSum;
+ return checkSum;
}
private int checkSumPlus(short[] a) {
@@ -1068,7 +934,7 @@ private int checkSumPlus(short[] a) {
for (short e : a) {
checkSum += e;
}
- return (int) checkSum;
+ return checkSum;
}
private int checkSumPlus(float[] a) {
@@ -1089,551 +955,352 @@ private int checkSumPlus(double[] a) {
return checkSum;
}
- private void sortByInsertionSort(Object a) {
- if (a instanceof int[]) {
- sortByInsertionSort((int[]) a);
- } else if (a instanceof long[]) {
- sortByInsertionSort((long[]) a);
- } else if (a instanceof byte[]) {
- sortByInsertionSort((byte[]) a);
- } else if (a instanceof char[]) {
- sortByInsertionSort((char[]) a);
- } else if (a instanceof short[]) {
- sortByInsertionSort((short[]) a);
- } else if (a instanceof float[]) {
- sortByInsertionSort((float[]) a);
- } else if (a instanceof double[]) {
- sortByInsertionSort((double[]) a);
- } else {
- fail("Unknown type of array: " + a.getClass().getName());
- }
+ private void sortByInsertionSort(Object a, int low, int high) {
+ SortingHelper.INSERTION_SORT.sort(a, low, high);
}
- private void sortByInsertionSort(int[] a) {
- for (int j, i = 1; i < a.length; i++) {
- int ai = a[i];
-
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
- }
- a[j + 1] = ai;
+ private void sortRange(Object a, int m) {
+ switch (a) {
+ case int[] ai -> sortRange(ai, m);
+ case long[] al -> sortRange(al, m);
+ case byte[] ab -> sortRange(ab, m);
+ case char[] ac -> sortRange(ac, m);
+ case short[] as -> sortRange(as, m);
+ case float[] af -> sortRange(af, m);
+ case double[] ad -> sortRange(ad, m);
+ default -> fail(a);
}
}
- private void sortByInsertionSort(long[] a) {
- for (int j, i = 1; i < a.length; i++) {
- long ai = a[i];
-
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
+ private void sortRange(int[] a, int m) {
+ try {
+ sortingHelper.sort(a, m + 1, m);
+ fail(sortingHelper + " must throw IllegalArgumentException: " +
+ "fromIndex = " + (m + 1) + ", toIndex = " + m);
+ } catch (IllegalArgumentException iae) {
+ try {
+ sortingHelper.sort(a, -m, a.length);
+ fail(sortingHelper + " must throw ArrayIndexOutOfBoundsException: " +
+ "fromIndex = " + (-m));
+ } catch (ArrayIndexOutOfBoundsException aoe) {
+ try {
+ sortingHelper.sort(a, 0, a.length + m);
+ fail(sortingHelper + " must throw ArrayIndexOutOfBoundsException: " +
+ "toIndex = " + (a.length + m));
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ }
}
- a[j + 1] = ai;
}
}
- private void sortByInsertionSort(byte[] a) {
- for (int j, i = 1; i < a.length; i++) {
- byte ai = a[i];
-
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
+ private void sortRange(long[] a, int m) {
+ try {
+ sortingHelper.sort(a, m + 1, m);
+ fail(sortingHelper + " must throw IllegalArgumentException: " +
+ "fromIndex = " + (m + 1) + ", toIndex = " + m);
+ } catch (IllegalArgumentException iae) {
+ try {
+ sortingHelper.sort(a, -m, a.length);
+ fail(sortingHelper + " must throw ArrayIndexOutOfBoundsException: " +
+ "fromIndex = " + (-m));
+ } catch (ArrayIndexOutOfBoundsException aoe) {
+ try {
+ sortingHelper.sort(a, 0, a.length + m);
+ fail(sortingHelper + " must throw ArrayIndexOutOfBoundsException: " +
+ "toIndex = " + (a.length + m));
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ }
}
- a[j + 1] = ai;
}
}
- private void sortByInsertionSort(char[] a) {
- for (int j, i = 1; i < a.length; i++) {
- char ai = a[i];
-
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
+ private void sortRange(byte[] a, int m) {
+ try {
+ sortingHelper.sort(a, m + 1, m);
+ fail(sortingHelper + " must throw IllegalArgumentException: " +
+ "fromIndex = " + (m + 1) + ", toIndex = " + m);
+ } catch (IllegalArgumentException iae) {
+ try {
+ sortingHelper.sort(a, -m, a.length);
+ fail(sortingHelper + " must throw ArrayIndexOutOfBoundsException: " +
+ "fromIndex = " + (-m));
+ } catch (ArrayIndexOutOfBoundsException aoe) {
+ try {
+ sortingHelper.sort(a, 0, a.length + m);
+ fail(sortingHelper + " must throw ArrayIndexOutOfBoundsException: " +
+ "toIndex = " + (a.length + m));
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ }
}
- a[j + 1] = ai;
}
}
- private void sortByInsertionSort(short[] a) {
- for (int j, i = 1; i < a.length; i++) {
- short ai = a[i];
-
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
- }
- a[j + 1] = ai;
- }
- }
-
- private void sortByInsertionSort(float[] a) {
- for (int j, i = 1; i < a.length; i++) {
- float ai = a[i];
-
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
- }
- a[j + 1] = ai;
- }
- }
-
- private void sortByInsertionSort(double[] a) {
- for (int j, i = 1; i < a.length; i++) {
- double ai = a[i];
-
- for (j = i - 1; j >= 0 && ai < a[j]; j--) {
- a[j + 1] = a[j];
- }
- a[j + 1] = ai;
- }
- }
-
- private void checkSubArray(Object a, int fromIndex, int toIndex) {
- if (a instanceof int[]) {
- checkSubArray((int[]) a, fromIndex, toIndex);
- } else if (a instanceof long[]) {
- checkSubArray((long[]) a, fromIndex, toIndex);
- } else if (a instanceof byte[]) {
- checkSubArray((byte[]) a, fromIndex, toIndex);
- } else if (a instanceof char[]) {
- checkSubArray((char[]) a, fromIndex, toIndex);
- } else if (a instanceof short[]) {
- checkSubArray((short[]) a, fromIndex, toIndex);
- } else if (a instanceof float[]) {
- checkSubArray((float[]) a, fromIndex, toIndex);
- } else if (a instanceof double[]) {
- checkSubArray((double[]) a, fromIndex, toIndex);
- } else {
- fail("Unknown type of array: " + a.getClass().getName());
- }
- }
-
- private void checkSubArray(int[] a, int fromIndex, int toIndex) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i] != A380) {
- fail("Range sort changes left element at position " + i + hex(a[i], A380));
- }
- }
-
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i] > a[i + 1]) {
- fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
- }
- }
-
- for (int i = toIndex; i < a.length; i++) {
- if (a[i] != B747) {
- fail("Range sort changes right element at position " + i + hex(a[i], B747));
- }
- }
- }
-
- private void checkSubArray(long[] a, int fromIndex, int toIndex) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i] != (long) A380) {
- fail("Range sort changes left element at position " + i + hex(a[i], A380));
- }
- }
-
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i] > a[i + 1]) {
- fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
- }
- }
-
- for (int i = toIndex; i < a.length; i++) {
- if (a[i] != (long) B747) {
- fail("Range sort changes right element at position " + i + hex(a[i], B747));
- }
- }
- }
-
- private void checkSubArray(byte[] a, int fromIndex, int toIndex) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i] != (byte) A380) {
- fail("Range sort changes left element at position " + i + hex(a[i], A380));
- }
- }
-
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i] > a[i + 1]) {
- fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
- }
- }
-
- for (int i = toIndex; i < a.length; i++) {
- if (a[i] != (byte) B747) {
- fail("Range sort changes right element at position " + i + hex(a[i], B747));
- }
- }
- }
-
- private void checkSubArray(char[] a, int fromIndex, int toIndex) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i] != (char) A380) {
- fail("Range sort changes left element at position " + i + hex(a[i], A380));
- }
- }
-
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i] > a[i + 1]) {
- fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
- }
- }
-
- for (int i = toIndex; i < a.length; i++) {
- if (a[i] != (char) B747) {
- fail("Range sort changes right element at position " + i + hex(a[i], B747));
- }
- }
- }
-
- private void checkSubArray(short[] a, int fromIndex, int toIndex) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i] != (short) A380) {
- fail("Range sort changes left element at position " + i + hex(a[i], A380));
- }
- }
-
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i] > a[i + 1]) {
- fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
- }
- }
-
- for (int i = toIndex; i < a.length; i++) {
- if (a[i] != (short) B747) {
- fail("Range sort changes right element at position " + i + hex(a[i], B747));
- }
- }
- }
-
- private void checkSubArray(float[] a, int fromIndex, int toIndex) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i] != (float) A380) {
- fail("Range sort changes left element at position " + i + hex((long) a[i], A380));
- }
- }
-
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i] > a[i + 1]) {
- fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
- }
- }
-
- for (int i = toIndex; i < a.length; i++) {
- if (a[i] != (float) B747) {
- fail("Range sort changes right element at position " + i + hex((long) a[i], B747));
- }
- }
- }
-
- private void checkSubArray(double[] a, int fromIndex, int toIndex) {
- for (int i = 0; i < fromIndex; i++) {
- if (a[i] != (double) A380) {
- fail("Range sort changes left element at position " + i + hex((long) a[i], A380));
- }
- }
-
- for (int i = fromIndex; i < toIndex - 1; i++) {
- if (a[i] > a[i + 1]) {
- fail("Array is not sorted at " + i + "-th position: " + a[i] + " and " + a[i + 1]);
- }
- }
-
- for (int i = toIndex; i < a.length; i++) {
- if (a[i] != (double) B747) {
- fail("Range sort changes right element at position " + i + hex((long) a[i], B747));
- }
- }
- }
-
- private void checkRange(Object a, int m) {
- if (a instanceof int[]) {
- checkRange((int[]) a, m);
- } else if (a instanceof long[]) {
- checkRange((long[]) a, m);
- } else if (a instanceof byte[]) {
- checkRange((byte[]) a, m);
- } else if (a instanceof char[]) {
- checkRange((char[]) a, m);
- } else if (a instanceof short[]) {
- checkRange((short[]) a, m);
- } else if (a instanceof float[]) {
- checkRange((float[]) a, m);
- } else if (a instanceof double[]) {
- checkRange((double[]) a, m);
- } else {
- fail("Unknown type of array: " + a.getClass().getName());
- }
- }
-
- private void checkRange(int[] a, int m) {
+ private void sortRange(char[] a, int m) {
try {
sortingHelper.sort(a, m + 1, m);
- fail(sortingHelper + " does not throw IllegalArgumentException " +
- "as expected: fromIndex = " + (m + 1) + " toIndex = " + m);
+ fail(sortingHelper + " must throw IllegalArgumentException: " +
+ "fromIndex = " + (m + 1) + ", toIndex = " + m);
} catch (IllegalArgumentException iae) {
try {
sortingHelper.sort(a, -m, a.length);
- fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
- "as expected: fromIndex = " + (-m));
+ fail(sortingHelper + " must throw ArrayIndexOutOfBoundsException: " +
+ "fromIndex = " + (-m));
} catch (ArrayIndexOutOfBoundsException aoe) {
try {
sortingHelper.sort(a, 0, a.length + m);
- fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
- "as expected: toIndex = " + (a.length + m));
- } catch (ArrayIndexOutOfBoundsException expected) {}
+ fail(sortingHelper + " must throw ArrayIndexOutOfBoundsException: " +
+ "toIndex = " + (a.length + m));
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ }
}
}
}
- private void checkRange(long[] a, int m) {
+ private void sortRange(short[] a, int m) {
try {
sortingHelper.sort(a, m + 1, m);
- fail(sortingHelper + " does not throw IllegalArgumentException " +
- "as expected: fromIndex = " + (m + 1) + " toIndex = " + m);
+ fail(sortingHelper + " must throw IllegalArgumentException: " +
+ "fromIndex = " + (m + 1) + ", toIndex = " + m);
} catch (IllegalArgumentException iae) {
try {
sortingHelper.sort(a, -m, a.length);
- fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
- "as expected: fromIndex = " + (-m));
+ fail(sortingHelper + " must throw ArrayIndexOutOfBoundsException: " +
+ "fromIndex = " + (-m));
} catch (ArrayIndexOutOfBoundsException aoe) {
try {
sortingHelper.sort(a, 0, a.length + m);
- fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
- "as expected: toIndex = " + (a.length + m));
- } catch (ArrayIndexOutOfBoundsException expected) {}
+ fail(sortingHelper + " must throw ArrayIndexOutOfBoundsException: " +
+ "toIndex = " + (a.length + m));
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ }
}
}
}
- private void checkRange(byte[] a, int m) {
+ private void sortRange(float[] a, int m) {
try {
sortingHelper.sort(a, m + 1, m);
- fail(sortingHelper + " does not throw IllegalArgumentException " +
- "as expected: fromIndex = " + (m + 1) + " toIndex = " + m);
+ fail(sortingHelper + " must throw IllegalArgumentException: " +
+ "fromIndex = " + (m + 1) + ", toIndex = " + m);
} catch (IllegalArgumentException iae) {
try {
sortingHelper.sort(a, -m, a.length);
- fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
- "as expected: fromIndex = " + (-m));
+ fail(sortingHelper + " must throw ArrayIndexOutOfBoundsException: " +
+ "fromIndex = " + (-m));
} catch (ArrayIndexOutOfBoundsException aoe) {
try {
sortingHelper.sort(a, 0, a.length + m);
- fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
- "as expected: toIndex = " + (a.length + m));
- } catch (ArrayIndexOutOfBoundsException expected) {}
+ fail(sortingHelper + " must throw ArrayIndexOutOfBoundsException: " +
+ "toIndex = " + (a.length + m));
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ }
}
}
}
- private void checkRange(char[] a, int m) {
+ private void sortRange(double[] a, int m) {
try {
sortingHelper.sort(a, m + 1, m);
- fail(sortingHelper + " does not throw IllegalArgumentException " +
- "as expected: fromIndex = " + (m + 1) + " toIndex = " + m);
+ fail(sortingHelper + " must throw IllegalArgumentException: " +
+ "fromIndex = " + (m + 1) + ", toIndex = " + m);
} catch (IllegalArgumentException iae) {
try {
sortingHelper.sort(a, -m, a.length);
- fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
- "as expected: fromIndex = " + (-m));
+ fail(sortingHelper + " must throw ArrayIndexOutOfBoundsException: " +
+ "fromIndex = " + (-m));
} catch (ArrayIndexOutOfBoundsException aoe) {
try {
sortingHelper.sort(a, 0, a.length + m);
- fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
- "as expected: toIndex = " + (a.length + m));
- } catch (ArrayIndexOutOfBoundsException expected) {}
+ fail(sortingHelper + " must throw ArrayIndexOutOfBoundsException: " +
+ "toIndex = " + (a.length + m));
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ }
}
}
}
- private void checkRange(short[] a, int m) {
- try {
- sortingHelper.sort(a, m + 1, m);
- fail(sortingHelper + " does not throw IllegalArgumentException " +
- "as expected: fromIndex = " + (m + 1) + " toIndex = " + m);
- } catch (IllegalArgumentException iae) {
- try {
- sortingHelper.sort(a, -m, a.length);
- fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
- "as expected: fromIndex = " + (-m));
- } catch (ArrayIndexOutOfBoundsException aoe) {
- try {
- sortingHelper.sort(a, 0, a.length + m);
- fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
- "as expected: toIndex = " + (a.length + m));
- } catch (ArrayIndexOutOfBoundsException expected) {}
- }
+ private void copy(Object dst, Object src) {
+ switch (src) {
+ case float[] sf -> System.arraycopy(sf, 0, dst, 0, sf.length);
+ case double[] sd -> System.arraycopy(sd, 0, dst, 0, sd.length);
+ default -> fail(src);
}
}
- private void checkRange(float[] a, int m) {
- try {
- sortingHelper.sort(a, m + 1, m);
- fail(sortingHelper + " does not throw IllegalArgumentException " +
- "as expected: fromIndex = " + (m + 1) + " toIndex = " + m);
- } catch (IllegalArgumentException iae) {
- try {
- sortingHelper.sort(a, -m, a.length);
- fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
- "as expected: fromIndex = " + (-m));
- } catch (ArrayIndexOutOfBoundsException aoe) {
- try {
- sortingHelper.sort(a, 0, a.length + m);
- fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
- "as expected: toIndex = " + (a.length + m));
- } catch (ArrayIndexOutOfBoundsException expected) {}
- }
- }
- }
+ private void createArray(int length) {
+ gold = new Object[]{
+ new int[length], new long[length],
+ new byte[length], new char[length], new short[length],
+ new float[length], new double[length]
+ };
- private void checkRange(double[] a, int m) {
- try {
- sortingHelper.sort(a, m + 1, m);
- fail(sortingHelper + " does not throw IllegalArgumentException " +
- "as expected: fromIndex = " + (m + 1) + " toIndex = " + m);
- } catch (IllegalArgumentException iae) {
- try {
- sortingHelper.sort(a, -m, a.length);
- fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
- "as expected: fromIndex = " + (-m));
- } catch (ArrayIndexOutOfBoundsException aoe) {
- try {
- sortingHelper.sort(a, 0, a.length + m);
- fail(sortingHelper + " does not throw ArrayIndexOutOfBoundsException " +
- "as expected: toIndex = " + (a.length + m));
- } catch (ArrayIndexOutOfBoundsException expected) {}
- }
- }
+ test = new Object[]{
+ new int[length], new long[length],
+ new byte[length], new char[length], new short[length],
+ new float[length], new double[length]
+ };
}
- private void copy(Object dst, Object src) {
- if (src instanceof float[]) {
- copy((float[]) dst, (float[]) src);
- } else if (src instanceof double[]) {
- copy((double[]) dst, (double[]) src);
- } else {
- fail("Unknown type of array: " + src.getClass().getName());
+ private void convertArray(int m) {
+ int[] a = (int[]) gold[0];
+
+ for (int i = 0; i < m; ++i) {
+ a[i] = A380;
+ }
+ for (int i = a.length - m; i < a.length; ++i) {
+ a[i] = B747;
+ }
+ for (int i = 0; i < gold.length; ++i) {
+ TypeConverter converter = TypeConverter.values()[i];
+ converter.convert(a, gold[i], withMin, m);
+ }
+ for (int i = 0; i < gold.length; ++i) {
+ System.arraycopy(gold[i], 0, test[i], 0, a.length);
}
}
- private void copy(float[] dst, float[] src) {
- System.arraycopy(src, 0, dst, 0, src.length);
+ private String hex(long a, int b) {
+ return ": " + Long.toHexString(a) + ", must be " + Integer.toHexString(b);
}
- private void copy(double[] dst, double[] src) {
- System.arraycopy(src, 0, dst, 0, src.length);
+ private void printTestName(String test, int length, String message) {
+ out.println("[" + sortingHelper + "] '" + test + "' length = " + length + message);
}
- private void printTestName(String test, TestRandom random, int length) {
- printTestName(test, random, length, "");
+ private void printTestName(String test, TestRandom random, int length, String message) {
+ out.println("[" + sortingHelper + "] '" + test +
+ "' length = " + length + ", random = " + random + message);
}
- private void createData(int length) {
- gold = new Object[] {
- new int[length], new long[length],
- new byte[length], new char[length], new short[length],
- new float[length], new double[length]
- };
-
- test = new Object[] {
- new int[length], new long[length],
- new byte[length], new char[length], new short[length],
- new float[length], new double[length]
- };
+ private void fail(Object a) {
+ fail("Unknown type: " + a.getClass().getName());
}
- private void convertData(int length) {
- for (int i = 1; i < gold.length; i++) {
- TypeConverter converter = TypeConverter.values()[i - 1];
- converter.convert((int[])gold[0], gold[i]);
- }
-
- for (int i = 0; i < gold.length; i++) {
- System.arraycopy(gold[i], 0, test[i], 0, length);
- }
+ private void fail(String message) {
+ err.format("*** TEST FAILED ***\n\n%s\n\n", message);
+ throw new RuntimeException("Test failed");
}
- private String hex(long a, int b) {
- return ": " + Long.toHexString(a) + ", must be " + Integer.toHexString(b);
+ private static long toLong(int i) {
+ return (((long) i) << 32) | i;
}
- private void printTestName(String test, TestRandom random, int length, String message) {
- out.println( "[" + sortingHelper + "] '" + test +
- "' length = " + length + ", random = " + random + message);
+ private static double toDouble(int i) {
+ long v = toLong(i);
+ v = (v > 0) ? ~v : v & ~(1L << 63);
+ double d = Double.longBitsToDouble(v);
+ return Double.isNaN(d) ? 0.0d : d;
}
- private static enum TypeConverter {
+ private enum TypeConverter {
+ INT {
+ @Override
+ void convert(int[] src, Object dst, boolean withMin, int m) {
+ if (withMin) {
+ src[m] = Integer.MIN_VALUE;
+ }
+ }
+ },
+
LONG {
- void convert(int[] src, Object dst) {
+ @Override
+ void convert(int[] src, Object dst, boolean withMin, int m) {
long[] b = (long[]) dst;
- for (int i = 0; i < src.length; i++) {
- b[i] = (long) src[i];
+ for (int i = 0; i < src.length; ++i) {
+ b[i] = toLong(src[i]);
+ }
+ if (withMin) {
+ b[m] = Long.MIN_VALUE;
}
}
},
BYTE {
- void convert(int[] src, Object dst) {
+ @Override
+ void convert(int[] src, Object dst, boolean withMin, int m) {
byte[] b = (byte[]) dst;
- for (int i = 0; i < src.length; i++) {
+ for (int i = 0; i < src.length; ++i) {
b[i] = (byte) src[i];
}
+ if (withMin) {
+ b[m] = Byte.MIN_VALUE;
+ }
}
},
CHAR {
- void convert(int[] src, Object dst) {
+ @Override
+ void convert(int[] src, Object dst, boolean withMin, int m) {
char[] b = (char[]) dst;
- for (int i = 0; i < src.length; i++) {
+ for (int i = 0; i < src.length; ++i) {
b[i] = (char) src[i];
}
+ if (withMin) {
+ b[m] = Character.MIN_VALUE;
+ }
}
},
SHORT {
- void convert(int[] src, Object dst) {
+ @Override
+ void convert(int[] src, Object dst, boolean withMin, int m) {
short[] b = (short[]) dst;
- for (int i = 0; i < src.length; i++) {
+ for (int i = 0; i < src.length; ++i) {
b[i] = (short) src[i];
}
+ if (withMin) {
+ b[m] = Short.MIN_VALUE;
+ }
}
},
FLOAT {
- void convert(int[] src, Object dst) {
+ @Override
+ void convert(int[] src, Object dst, boolean withMin, int m) {
float[] b = (float[]) dst;
- for (int i = 0; i < src.length; i++) {
- b[i] = (float) src[i];
+ for (int i = 0; i < src.length; ++i) {
+ b[i] = src[i];
+ }
+ if (withMin) {
+ b[m] = Float.NEGATIVE_INFINITY;
}
}
},
DOUBLE {
- void convert(int[] src, Object dst) {
+ @Override
+ void convert(int[] src, Object dst, boolean withMin, int m) {
double[] b = (double[]) dst;
- for (int i = 0; i < src.length; i++) {
- b[i] = (double) src[i];
+ for (int i = 0; i < src.length / 2; ++i) {
+ b[i] = toDouble(src[i]);
+ }
+ for (int i = src.length / 2; i < src.length; ++i) {
+ b[i] = src[i];
+ }
+ if (withMin) {
+ b[m] = Double.NEGATIVE_INFINITY;
}
}
};
- abstract void convert(int[] src, Object dst);
+ abstract void convert(int[] src, Object dst, boolean withMin, int m);
}
- private static enum SortedBuilder {
+ private enum SortedBuilder {
STEPS {
+ @Override
void build(int[] a, int m) {
- for (int i = 0; i < m; i++) {
+ for (int i = 0; i < m; ++i) {
a[i] = 0;
}
- for (int i = m; i < a.length; i++) {
+ for (int i = m; i < a.length; ++i) {
a[i] = 1;
}
}
@@ -1642,40 +1309,73 @@ void build(int[] a, int m) {
abstract void build(int[] a, int m);
}
- private static enum UnsortedBuilder {
+ private enum UnsortedBuilder {
RANDOM {
+ @Override
void build(int[] a, int m, Random random) {
- for (int i = 0; i < a.length; i++) {
+ for (int i = 0; i < a.length; ++i) {
a[i] = random.nextInt();
}
}
},
- ASCENDING {
+ PERMUTATION {
+ @Override
void build(int[] a, int m, Random random) {
- for (int i = 0; i < a.length; i++) {
- a[i] = m + i;
+ int mask = ~(0x000000FF << (random.nextInt(4) * 2));
+
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = i & mask;
+ }
+ for (int i = a.length; i > 1; --i) {
+ int k = random.nextInt(i);
+ int t = a[i - 1];
+ a[i - 1] = a[k];
+ a[k] = t;
}
}
},
- DESCENDING {
+ UNIFORM {
+ @Override
void build(int[] a, int m, Random random) {
- for (int i = 0; i < a.length; i++) {
- a[i] = a.length - m - i;
+ int mask = (m << 15) - 1;
+
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = random.nextInt() & mask;
}
}
},
- EQUAL {
+ STAGGER {
+ @Override
void build(int[] a, int m, Random random) {
- for (int i = 0; i < a.length; i++) {
- a[i] = m;
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = (i * m + i) % a.length;
}
}
},
- SAW {
+ REPEATED {
+ @Override
+ void build(int[] a, int m, Random random) {
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = i % m;
+ }
+ }
+ },
+
+ DUPLICATED {
+ @Override
+ void build(int[] a, int m, Random random) {
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = random.nextInt(m);
+ }
+ }
+ },
+
+ SAWTOOTH {
+ @Override
void build(int[] a, int m, Random random) {
int incCount = 1;
int decCount = a.length;
@@ -1683,7 +1383,7 @@ void build(int[] a, int m, Random random) {
int period = m--;
while (true) {
- for (int k = 1; k <= period; k++) {
+ for (int k = 1; k <= period; ++k) {
if (i >= a.length) {
return;
}
@@ -1691,7 +1391,7 @@ void build(int[] a, int m, Random random) {
}
period += m;
- for (int k = 1; k <= period; k++) {
+ for (int k = 1; k <= period; ++k) {
if (i >= a.length) {
return;
}
@@ -1702,161 +1402,139 @@ void build(int[] a, int m, Random random) {
}
},
- REPEATED {
- void build(int[] a, int m, Random random) {
- for (int i = 0; i < a.length; i++) {
- a[i] = i % m;
- }
- }
- },
-
- DUPLICATED {
+ SHUFFLE {
+ @Override
void build(int[] a, int m, Random random) {
- for (int i = 0; i < a.length; i++) {
- a[i] = random.nextInt(m);
+ for (int i = 0, j = 0, k = 1; i < a.length; ++i) {
+ a[i] = random.nextInt(m) > 0 ? (j += 2) : (k += 2);
}
}
- },
-
- ORGAN_PIPES {
- void build(int[] a, int m, Random random) {
- int middle = a.length / (m + 1);
+ };
- for (int i = 0; i < middle; i++) {
- a[i] = i;
- }
+ abstract void build(int[] a, int m, Random random);
+ }
- for (int i = middle; i < a.length; i++) {
- a[i] = a.length - i - 1;
+ private enum StructuredBuilder {
+ ASCENDING {
+ @Override
+ void build(int[] a, int m) {
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = m + i;
}
}
},
- STAGGER {
- void build(int[] a, int m, Random random) {
- for (int i = 0; i < a.length; i++) {
- a[i] = (i * m + i) % a.length;
+ DESCENDING {
+ @Override
+ void build(int[] a, int m) {
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = a.length - m - i;
}
}
},
- PLATEAU {
- void build(int[] a, int m, Random random) {
- for (int i = 0; i < a.length; i++) {
- a[i] = Math.min(i, m);
- }
+ EQUAL {
+ @Override
+ void build(int[] a, int m) {
+ Arrays.fill(a, m);
}
},
- SHUFFLE {
- void build(int[] a, int m, Random random) {
- int x = 0, y = 0;
-
- for (int i = 0; i < a.length; i++) {
- a[i] = random.nextBoolean() ? (x += 2) : (y += 2);
+ SHIFTED {
+ @Override
+ void build(int[] a, int m) {
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = i << 10;
}
}
},
- LATCH {
- void build(int[] a, int m, Random random) {
- int max = a.length / m;
- max = max < 2 ? 2 : max;
+ ORGAN_PIPES {
+ @Override
+ void build(int[] a, int m) {
+ int middle = a.length / (m + 1);
- for (int i = 0; i < a.length; i++) {
- a[i] = i % max;
+ for (int i = 0; i < middle; ++i) {
+ a[i] = i;
+ }
+ for (int i = middle; i < a.length; ++i) {
+ a[i] = a.length - i - 1;
}
}
- };
-
- abstract void build(int[] a, int m, Random random);
- }
+ },
- private static enum MergingBuilder {
- ASCENDING {
+ PLATEAU {
+ @Override
void build(int[] a, int m) {
- int period = a.length / m;
- int v = 1, i = 0;
-
- for (int k = 0; k < m; k++) {
- v = 1;
-
- for (int p = 0; p < period; p++) {
- a[i++] = v++;
- }
- }
-
- for (int j = i; j < a.length - 1; j++) {
- a[j] = v++;
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = Math.min(i, m);
}
-
- a[a.length - 1] = 0;
}
},
- DESCENDING {
+ LATCH {
+ @Override
void build(int[] a, int m) {
- int period = a.length / m;
- int v = -1, i = 0;
-
- for (int k = 0; k < m; k++) {
- v = -1;
-
- for (int p = 0; p < period; p++) {
- a[i++] = v--;
- }
- }
+ int max = Math.max(a.length / m, 2);
- for (int j = i; j < a.length - 1; j++) {
- a[j] = v--;
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = i % max;
}
-
- a[a.length - 1] = 0;
}
},
POINT {
+ @Override
void build(int[] a, int m) {
- for (int i = 0; i < a.length; i++) {
- a[i] = 0;
- }
+ Arrays.fill(a, 0);
a[a.length / 2] = m;
}
},
LINE {
+ @Override
void build(int[] a, int m) {
- for (int i = 0; i < a.length; i++) {
+ for (int i = 0; i < a.length; ++i) {
a[i] = i;
}
- reverse(a, 0, a.length - 1);
+ reverse(a, Math.max(0, a.length - m), a.length);
}
},
PEARL {
+ @Override
void build(int[] a, int m) {
- for (int i = 0; i < a.length; i++) {
+ for (int i = 0; i < a.length; ++i) {
a[i] = i;
}
- reverse(a, 0, 2);
+ reverse(a, 0, Math.min(m, a.length));
+ }
+ },
+
+ TRAPEZIUM {
+ @Override
+ void build(int[] a, int m) {
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = i;
+ }
+ reverse(a, m, a.length - m);
}
},
RING {
+ @Override
void build(int[] a, int m) {
int k1 = a.length / 3;
int k2 = a.length / 3 * 2;
int level = a.length / 3;
- for (int i = 0, k = level; i < k1; i++) {
+ for (int i = 0, k = level; i < k1; ++i) {
a[i] = k--;
}
-
- for (int i = k1; i < k2; i++) {
+ for (int i = k1; i < k2; ++i) {
a[i] = 0;
}
-
- for (int i = k2, k = level; i < a.length; i++) {
+ for (int i = k2, k = level; i < a.length; ++i) {
a[i] = k--;
}
}
@@ -1873,22 +1551,24 @@ private static void reverse(int[] a, int lo, int hi) {
}
}
- private static enum NegativeZeroBuilder {
+ private enum NegativeZeroBuilder {
FLOAT {
+ @Override
void build(Object o, Random random) {
float[] a = (float[]) o;
- for (int i = 0; i < a.length; i++) {
+ for (int i = 0; i < a.length; ++i) {
a[i] = random.nextBoolean() ? -0.0f : 0.0f;
}
}
},
DOUBLE {
+ @Override
void build(Object o, Random random) {
double[] a = (double[]) o;
- for (int i = 0; i < a.length; i++) {
+ for (int i = 0; i < a.length; ++i) {
a[i] = random.nextBoolean() ? -0.0d : 0.0d;
}
}
@@ -1897,109 +1577,98 @@ void build(Object o, Random random) {
abstract void build(Object o, Random random);
}
- private static enum FloatingPointBuilder {
+ private enum FloatingPointBuilder {
FLOAT {
- void build(Object o, int a, int g, int z, int n, int p, Random random) {
+ @Override
+ void build(Object o, int k, int g, int z, int n, int p, Random random) {
float negativeValue = -random.nextFloat();
- float positiveValue = random.nextFloat();
- float[] x = (float[]) o;
+ float positiveValue = random.nextFloat();
+ float[] a = (float[]) o;
int fromIndex = 0;
- writeValue(x, negativeValue, fromIndex, n);
+ fillWithValue(a, Float.NEGATIVE_INFINITY, fromIndex, 1);
+ fromIndex += 1;
+
+ fillWithValue(a, -Float.MAX_VALUE, fromIndex, 1);
+ fromIndex += 1;
+
+ fillWithValue(a, negativeValue, fromIndex, n);
fromIndex += n;
- writeValue(x, -0.0f, fromIndex, g);
+ fillWithValue(a, -0.0f, fromIndex, g);
fromIndex += g;
- writeValue(x, 0.0f, fromIndex, z);
+ fillWithValue(a, 0.0f, fromIndex, z);
fromIndex += z;
- writeValue(x, positiveValue, fromIndex, p);
+ fillWithValue(a, positiveValue, fromIndex, p);
fromIndex += p;
- writeValue(x, Float.NaN, fromIndex, a);
+ fillWithValue(a, Float.MAX_VALUE, fromIndex, 1);
+ fromIndex += 1;
+
+ fillWithValue(a, Float.POSITIVE_INFINITY, fromIndex, 1);
+ fromIndex += 1;
+
+ fillWithValue(a, Float.NaN, fromIndex, k);
}
},
DOUBLE {
- void build(Object o, int a, int g, int z, int n, int p, Random random) {
+ @Override
+ void build(Object o, int k, int g, int z, int n, int p, Random random) {
double negativeValue = -random.nextFloat();
- double positiveValue = random.nextFloat();
- double[] x = (double[]) o;
+ double positiveValue = random.nextFloat();
+ double[] a = (double[]) o;
int fromIndex = 0;
- writeValue(x, negativeValue, fromIndex, n);
+ fillWithValue(a, Double.NEGATIVE_INFINITY, fromIndex, 1);
+ fromIndex++;
+
+ fillWithValue(a, -Double.MAX_VALUE, fromIndex, 1);
+ fromIndex++;
+
+ fillWithValue(a, negativeValue, fromIndex, n);
fromIndex += n;
- writeValue(x, -0.0d, fromIndex, g);
+ fillWithValue(a, -0.0d, fromIndex, g);
fromIndex += g;
- writeValue(x, 0.0d, fromIndex, z);
+ fillWithValue(a, 0.0d, fromIndex, z);
fromIndex += z;
- writeValue(x, positiveValue, fromIndex, p);
+ fillWithValue(a, positiveValue, fromIndex, p);
fromIndex += p;
- writeValue(x, Double.NaN, fromIndex, a);
+ fillWithValue(a, Double.MAX_VALUE, fromIndex, 1);
+ fromIndex += 1;
+
+ fillWithValue(a, Double.POSITIVE_INFINITY, fromIndex, 1);
+ fromIndex += 1;
+
+ fillWithValue(a, Double.NaN, fromIndex, k);
}
};
- abstract void build(Object o, int a, int g, int z, int n, int p, Random random);
+ abstract void build(Object o, int k, int g, int z, int n, int p, Random random);
- private static void writeValue(float[] a, float value, int fromIndex, int count) {
- for (int i = fromIndex; i < fromIndex + count; i++) {
+ private static void fillWithValue(float[] a, float value, int fromIndex, int count) {
+ for (int i = fromIndex; i < fromIndex + count; ++i) {
a[i] = value;
}
}
- private static void writeValue(double[] a, double value, int fromIndex, int count) {
- for (int i = fromIndex; i < fromIndex + count; i++) {
+ private static void fillWithValue(double[] a, double value, int fromIndex, int count) {
+ for (int i = fromIndex; i < fromIndex + count; ++i) {
a[i] = value;
}
}
}
- private static Comparator pairComparator = new Comparator() {
-
- @Override
- public int compare(Pair p1, Pair p2) {
- return p1.compareTo(p2);
- }
- };
-
- private static class Pair implements Comparable {
-
- private Pair(int key, int value) {
- this.key = key;
- this.value = value;
- }
-
- int getKey() {
- return key;
- }
-
- int getValue() {
- return value;
- }
-
- @Override
- public int compareTo(Pair pair) {
- return Integer.compare(key, pair.key);
- }
-
- @Override
- public String toString() {
- return "(" + key + ", " + value + ")";
- }
-
- private int key;
- private int value;
- }
-
private static class TestRandom extends Random {
- private static final TestRandom BABA = new TestRandom(0xBABA);
private static final TestRandom DEDA = new TestRandom(0xDEDA);
+ private static final TestRandom BABA = new TestRandom(0xBABA);
private static final TestRandom C0FFEE = new TestRandom(0xC0FFEE);
private TestRandom(long seed) {
@@ -2012,6 +1681,6 @@ public String toString() {
return seed;
}
- private String seed;
+ private final String seed;
}
}
diff --git a/test/jdk/java/util/Arrays/java.base/java/util/SortingHelper.java b/test/jdk/java/util/Arrays/java.base/java/util/SortingHelper.java
index a8318b6e3747d..e65cf92e5d142 100644
--- a/test/jdk/java/util/Arrays/java.base/java/util/SortingHelper.java
+++ b/test/jdk/java/util/Arrays/java.base/java/util/SortingHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -24,305 +24,207 @@
package java.util;
/**
- * This class provides access to package-private
- * methods of DualPivotQuicksort class.
+ * This class provides access to package-private methods of DualPivotQuicksort class.
*
* @author Vladimir Yaroslavskiy
*
- * @version 2019.09.19
+ * @version 2024.06.14
*
- * @since 14
+ * @since 14 * 20 ^ 26
*/
public enum SortingHelper {
- DUAL_PIVOT_QUICKSORT("Dual-Pivot Quicksort") {
-
+ INSERTION_SORT("Insertion sort") {
@Override
- public void sort(Object a) {
- if (a instanceof int[]) {
- DualPivotQuicksort.sort((int[]) a, SEQUENTIAL, 0, ((int[]) a).length);
- } else if (a instanceof long[]) {
- DualPivotQuicksort.sort((long[]) a, SEQUENTIAL, 0, ((long[]) a).length);
- } else if (a instanceof byte[]) {
- DualPivotQuicksort.sort((byte[]) a, 0, ((byte[]) a).length);
- } else if (a instanceof char[]) {
- DualPivotQuicksort.sort((char[]) a, SEQUENTIAL, 0, ((char[]) a).length);
- } else if (a instanceof short[]) {
- DualPivotQuicksort.sort((short[]) a, SEQUENTIAL, 0, ((short[]) a).length);
- } else if (a instanceof float[]) {
- DualPivotQuicksort.sort((float[]) a, SEQUENTIAL, 0, ((float[]) a).length);
- } else if (a instanceof double[]) {
- DualPivotQuicksort.sort((double[]) a, SEQUENTIAL, 0, ((double[]) a).length);
- } else {
- fail(a);
+ public void sort(Object a, int low, int high) {
+ switch(a) {
+ case int[] ai -> DualPivotQuicksort.insertionSort(ai, low, high);
+ case long[] al -> DualPivotQuicksort.insertionSort(al, low, high);
+ case byte[] ab -> DualPivotQuicksort.insertionSort(ab, low, high);
+ case char[] ac -> DualPivotQuicksort.insertionSort(ac, low, high);
+ case short[] as -> DualPivotQuicksort.insertionSort(as, low, high);
+ case float[] af -> DualPivotQuicksort.insertionSort(af, low, high);
+ case double[] ad -> DualPivotQuicksort.insertionSort(ad, low, high);
+ default -> fail(a);
}
}
+ },
+ MIXED_INSERTION_SORT("Mixed insertion sort") {
@Override
public void sort(Object a, int low, int high) {
- if (a instanceof int[]) {
- DualPivotQuicksort.sort((int[]) a, SEQUENTIAL, low, high);
- } else if (a instanceof long[]) {
- DualPivotQuicksort.sort((long[]) a, SEQUENTIAL, low, high);
- } else if (a instanceof byte[]) {
- DualPivotQuicksort.sort((byte[]) a, low, high);
- } else if (a instanceof char[]) {
- DualPivotQuicksort.sort((char[]) a, SEQUENTIAL, low, high);
- } else if (a instanceof short[]) {
- DualPivotQuicksort.sort((short[]) a, SEQUENTIAL, low, high);
- } else if (a instanceof float[]) {
- DualPivotQuicksort.sort((float[]) a, SEQUENTIAL, low, high);
- } else if (a instanceof double[]) {
- DualPivotQuicksort.sort((double[]) a, SEQUENTIAL, low, high);
- } else {
- fail(a);
+ switch(a) {
+ case int[] ai -> DualPivotQuicksort.mixedInsertionSort(ai, low, high);
+ case long[] al -> DualPivotQuicksort.mixedInsertionSort(al, low, high);
+ case byte[] ab -> DualPivotQuicksort.sort(ab, low, high);
+ case char[] ac -> DualPivotQuicksort.sort(ac, low, high);
+ case short[] as -> DualPivotQuicksort.sort(as, low, high);
+ case float[] af -> DualPivotQuicksort.mixedInsertionSort(af, low, high);
+ case double[] ad -> DualPivotQuicksort.mixedInsertionSort(ad, low, high);
+ default -> fail(a);
}
}
-
- @Override
- public void sort(Object[] a) {
- fail(a);
- }
-
- @Override
- public void sort(Object[] a, Comparator comparator) {
- fail(a);
- }
},
- PARALLEL_SORT("Parallel sort") {
-
+ MERGING_SORT("Merging sort") {
@Override
- public void sort(Object a) {
- if (a instanceof int[]) {
- DualPivotQuicksort.sort((int[]) a, PARALLEL, 0, ((int[]) a).length);
- } else if (a instanceof long[]) {
- DualPivotQuicksort.sort((long[]) a, PARALLEL, 0, ((long[]) a).length);
- } else if (a instanceof byte[]) {
- DualPivotQuicksort.sort((byte[]) a, 0, ((byte[]) a).length);
- } else if (a instanceof char[]) {
- DualPivotQuicksort.sort((char[]) a, PARALLEL, 0, ((char[]) a).length);
- } else if (a instanceof short[]) {
- DualPivotQuicksort.sort((short[]) a, PARALLEL, 0, ((short[]) a).length);
- } else if (a instanceof float[]) {
- DualPivotQuicksort.sort((float[]) a, PARALLEL, 0, ((float[]) a).length);
- } else if (a instanceof double[]) {
- DualPivotQuicksort.sort((double[]) a, PARALLEL, 0, ((double[]) a).length);
- } else {
- fail(a);
+ public void sort(Object a, int low, int high) {
+ switch(a) {
+ case int[] ai -> check("Merging", DualPivotQuicksort.tryMergingSort(null, ai, low, high - low));
+ case long[] al -> check("Merging", DualPivotQuicksort.tryMergingSort(null, al, low, high - low));
+ case byte[] ab -> DualPivotQuicksort.sort(ab, low, high);
+ case char[] ac -> DualPivotQuicksort.sort(ac, low, high);
+ case short[] as -> DualPivotQuicksort.sort(as, low, high);
+ case float[] af -> check("Merging", DualPivotQuicksort.tryMergingSort(null, af, low, high - low));
+ case double[] ad -> check("Merging", DualPivotQuicksort.tryMergingSort(null, ad, low, high - low));
+ default -> fail(a);
}
}
+ },
+ COUNTING_SORT("Counting sort") {
@Override
public void sort(Object a, int low, int high) {
- if (a instanceof int[]) {
- DualPivotQuicksort.sort((int[]) a, PARALLEL, low, high);
- } else if (a instanceof long[]) {
- DualPivotQuicksort.sort((long[]) a, PARALLEL, low, high);
- } else if (a instanceof byte[]) {
- DualPivotQuicksort.sort((byte[]) a, low, high);
- } else if (a instanceof char[]) {
- DualPivotQuicksort.sort((char[]) a, PARALLEL, low, high);
- } else if (a instanceof short[]) {
- DualPivotQuicksort.sort((short[]) a, PARALLEL, low, high);
- } else if (a instanceof float[]) {
- DualPivotQuicksort.sort((float[]) a, PARALLEL, low, high);
- } else if (a instanceof double[]) {
- DualPivotQuicksort.sort((double[]) a, PARALLEL, low, high);
- } else {
- fail(a);
+ switch(a) {
+ case int[] ai -> DualPivotQuicksort.sort(ai, 0, low, high);
+ case long[] al -> DualPivotQuicksort.sort(al, 0, low, high);
+ case byte[] ab -> DualPivotQuicksort.countingSort(ab, low, high);
+ case char[] ac -> DualPivotQuicksort.countingSort(ac, low, high);
+ case short[] as -> DualPivotQuicksort.countingSort(as, low, high);
+ case float[] af -> DualPivotQuicksort.sort(af, 0, low, high);
+ case double[] ad -> DualPivotQuicksort.sort(ad, 0, low, high);
+ default -> fail(a);
}
}
-
- @Override
- public void sort(Object[] a) {
- fail(a);
- }
-
- @Override
- public void sort(Object[] a, Comparator comparator) {
- fail(a);
- }
},
HEAP_SORT("Heap sort") {
-
@Override
- public void sort(Object a) {
- if (a instanceof int[]) {
- DualPivotQuicksort.sort(null, (int[]) a, BIG_DEPTH, 0, ((int[]) a).length);
- } else if (a instanceof long[]) {
- DualPivotQuicksort.sort(null, (long[]) a, BIG_DEPTH, 0, ((long[]) a).length);
- } else if (a instanceof byte[]) {
- DualPivotQuicksort.sort((byte[]) a, 0, ((byte[]) a).length);
- } else if (a instanceof char[]) {
- DualPivotQuicksort.sort((char[]) a, BIG_DEPTH, 0, ((char[]) a).length);
- } else if (a instanceof short[]) {
- DualPivotQuicksort.sort((short[]) a, BIG_DEPTH, 0, ((short[]) a).length);
- } else if (a instanceof float[]) {
- DualPivotQuicksort.sort(null, (float[]) a, BIG_DEPTH, 0, ((float[]) a).length);
- } else if (a instanceof double[]) {
- DualPivotQuicksort.sort(null, (double[]) a, BIG_DEPTH, 0, ((double[]) a).length);
- } else {
- fail(a);
+ public void sort(Object a, int low, int high) {
+ switch(a) {
+ case int[] ai -> DualPivotQuicksort.heapSort(ai, low, high);
+ case long[] al -> DualPivotQuicksort.heapSort(al, low, high);
+ case byte[] ab -> DualPivotQuicksort.sort(ab, low, high);
+ case char[] ac -> DualPivotQuicksort.sort(ac, low, high);
+ case short[] as -> DualPivotQuicksort.sort(as, low, high);
+ case float[] af -> DualPivotQuicksort.heapSort(af, low, high);
+ case double[] ad -> DualPivotQuicksort.heapSort(ad, low, high);
+ default -> fail(a);
}
}
+ },
+ DUAL_PIVOT_QUICKSORT("Dual-Pivot Quicksort") {
@Override
public void sort(Object a, int low, int high) {
- if (a instanceof int[]) {
- DualPivotQuicksort.sort(null, (int[]) a, BIG_DEPTH, low, high);
- } else if (a instanceof long[]) {
- DualPivotQuicksort.sort(null, (long[]) a, BIG_DEPTH, low, high);
- } else if (a instanceof byte[]) {
- DualPivotQuicksort.sort((byte[]) a, low, high);
- } else if (a instanceof char[]) {
- DualPivotQuicksort.sort((char[]) a, BIG_DEPTH, low, high);
- } else if (a instanceof short[]) {
- DualPivotQuicksort.sort((short[]) a, BIG_DEPTH, low, high);
- } else if (a instanceof float[]) {
- DualPivotQuicksort.sort(null, (float[]) a, BIG_DEPTH, low, high);
- } else if (a instanceof double[]) {
- DualPivotQuicksort.sort(null, (double[]) a, BIG_DEPTH, low, high);
- } else {
- fail(a);
+ switch(a) {
+ case int[] ai -> DualPivotQuicksort.sort(ai, 0, low, high);
+ case long[] al -> DualPivotQuicksort.sort(al, 0, low, high);
+ case byte[] ab -> DualPivotQuicksort.sort(ab, low, high);
+ case char[] ac -> DualPivotQuicksort.sort(ac, low, high);
+ case short[] as -> DualPivotQuicksort.sort(as, low, high);
+ case float[] af -> DualPivotQuicksort.sort(af, 0, low, high);
+ case double[] ad -> DualPivotQuicksort.sort(ad, 0, low, high);
+ default -> fail(a);
}
}
+ },
- @Override
- public void sort(Object[] a) {
- fail(a);
- }
+ PARALLEL_QUICKSORT("Parallel Quicksort") {
+ final int parallel = 4;
@Override
- public void sort(Object[] a, Comparator comparator) {
- fail(a);
+ public void sort(Object a, int low, int high) {
+ switch(a) {
+ case int[] ai -> DualPivotQuicksort.sort(ai, parallel, low, high);
+ case long[] al -> DualPivotQuicksort.sort(al, parallel, low, high);
+ case byte[] ab -> DualPivotQuicksort.sort(ab, low, high);
+ case char[] ac -> DualPivotQuicksort.sort(ac, low, high);
+ case short[] as -> DualPivotQuicksort.sort(as,low, high);
+ case float[] af -> DualPivotQuicksort.sort(af, parallel, low, high);
+ case double[] ad -> DualPivotQuicksort.sort(ad, parallel, low, high);
+ default -> fail(a);
+ }
}
},
ARRAYS_SORT("Arrays.sort") {
-
@Override
public void sort(Object a) {
- if (a instanceof int[]) {
- Arrays.sort((int[]) a);
- } else if (a instanceof long[]) {
- Arrays.sort((long[]) a);
- } else if (a instanceof byte[]) {
- Arrays.sort((byte[]) a);
- } else if (a instanceof char[]) {
- Arrays.sort((char[]) a);
- } else if (a instanceof short[]) {
- Arrays.sort((short[]) a);
- } else if (a instanceof float[]) {
- Arrays.sort((float[]) a);
- } else if (a instanceof double[]) {
- Arrays.sort((double[]) a);
- } else {
- fail(a);
+ switch(a) {
+ case int[] ai -> Arrays.sort(ai);
+ case long[] al -> Arrays.sort(al);
+ case byte[] ab -> Arrays.sort(ab);
+ case char[] ac -> Arrays.sort(ac);
+ case short[] as -> Arrays.sort(as);
+ case float[] af -> Arrays.sort(af);
+ case double[] ad -> Arrays.sort(ad);
+ default -> fail(a);
}
}
@Override
public void sort(Object a, int low, int high) {
- if (a instanceof int[]) {
- Arrays.sort((int[]) a, low, high);
- } else if (a instanceof long[]) {
- Arrays.sort((long[]) a, low, high);
- } else if (a instanceof byte[]) {
- Arrays.sort((byte[]) a, low, high);
- } else if (a instanceof char[]) {
- Arrays.sort((char[]) a, low, high);
- } else if (a instanceof short[]) {
- Arrays.sort((short[]) a, low, high);
- } else if (a instanceof float[]) {
- Arrays.sort((float[]) a, low, high);
- } else if (a instanceof double[]) {
- Arrays.sort((double[]) a, low, high);
- } else {
- fail(a);
+ switch(a) {
+ case int[] ai -> Arrays.sort(ai, low, high);
+ case long[] al -> Arrays.sort(al, low, high);
+ case byte[] ab -> Arrays.sort(ab, low, high);
+ case char[] ac -> Arrays.sort(ac, low, high);
+ case short[] as -> Arrays.sort(as, low, high);
+ case float[] af -> Arrays.sort(af, low, high);
+ case double[] ad -> Arrays.sort(ad, low, high);
+ default -> fail(a);
}
}
-
- @Override
- public void sort(Object[] a) {
- Arrays.sort(a);
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public void sort(Object[] a, Comparator comparator) {
- Arrays.sort(a, comparator);
- }
},
ARRAYS_PARALLEL_SORT("Arrays.parallelSort") {
-
@Override
public void sort(Object a) {
- if (a instanceof int[]) {
- Arrays.parallelSort((int[]) a);
- } else if (a instanceof long[]) {
- Arrays.parallelSort((long[]) a);
- } else if (a instanceof byte[]) {
- Arrays.parallelSort((byte[]) a);
- } else if (a instanceof char[]) {
- Arrays.parallelSort((char[]) a);
- } else if (a instanceof short[]) {
- Arrays.parallelSort((short[]) a);
- } else if (a instanceof float[]) {
- Arrays.parallelSort((float[]) a);
- } else if (a instanceof double[]) {
- Arrays.parallelSort((double[]) a);
- } else {
- fail(a);
+ switch(a) {
+ case int[] ai -> Arrays.parallelSort(ai);
+ case long[] al -> Arrays.parallelSort(al);
+ case byte[] ab -> Arrays.parallelSort(ab);
+ case char[] ac -> Arrays.parallelSort(ac);
+ case short[] as -> Arrays.parallelSort(as);
+ case float[] af -> Arrays.parallelSort(af);
+ case double[] ad -> Arrays.parallelSort(ad);
+ default -> fail(a);
}
}
@Override
public void sort(Object a, int low, int high) {
- if (a instanceof int[]) {
- Arrays.parallelSort((int[]) a, low, high);
- } else if (a instanceof long[]) {
- Arrays.parallelSort((long[]) a, low, high);
- } else if (a instanceof byte[]) {
- Arrays.parallelSort((byte[]) a, low, high);
- } else if (a instanceof char[]) {
- Arrays.parallelSort((char[]) a, low, high);
- } else if (a instanceof short[]) {
- Arrays.parallelSort((short[]) a, low, high);
- } else if (a instanceof float[]) {
- Arrays.parallelSort((float[]) a, low, high);
- } else if (a instanceof double[]) {
- Arrays.parallelSort((double[]) a, low, high);
- } else {
- fail(a);
+ switch(a) {
+ case int[] ai -> Arrays.parallelSort(ai, low, high);
+ case long[] al -> Arrays.parallelSort(al, low, high);
+ case byte[] ab -> Arrays.parallelSort(ab, low, high);
+ case char[] ac -> Arrays.parallelSort(ac, low, high);
+ case short[] as -> Arrays.parallelSort(as, low, high);
+ case float[] af -> Arrays.parallelSort(af, low, high);
+ case double[] ad -> Arrays.parallelSort(ad, low, high);
+ default -> fail(a);
}
}
-
- @Override
- @SuppressWarnings("unchecked")
- public void sort(Object[] a) {
- Arrays.parallelSort((Comparable[]) a);
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public void sort(Object[] a, Comparator comparator) {
- Arrays.parallelSort(a, comparator);
- }
};
- abstract public void sort(Object a);
-
- abstract public void sort(Object a, int low, int high);
-
- abstract public void sort(Object[] a);
+ SortingHelper(String name) {
+ this.name = name;
+ }
- abstract public void sort(Object[] a, Comparator comparator);
+ public abstract void sort(Object a, int low, int high);
- private SortingHelper(String name) {
- this.name = name;
+ public void sort(Object a) {
+ switch(a) {
+ case int[] ai -> sort(ai, 0, ai.length);
+ case long[] al -> sort(al, 0, al.length);
+ case byte[] ab -> sort(ab, 0, ab.length);
+ case char[] ac -> sort(ac, 0, ac.length);
+ case short[] as -> sort(as, 0, as.length);
+ case float[] af -> sort(af, 0, af.length);
+ case double[] ad -> sort(ad, 0, ad.length);
+ default -> fail(a);
+ }
}
@Override
@@ -330,21 +232,19 @@ public String toString() {
return name;
}
- private static void fail(Object a) {
- throw new RuntimeException("Unexpected type of array: " + a.getClass().getName());
+ private static void check(String name, boolean result) {
+ if (!result) {
+ fail(name + " sort must return true");
+ }
}
- private String name;
+ private static void fail(Object a) {
+ fail("Unknown array: " + a.getClass().getName());
+ }
- /**
- * Parallelism level for sequential and parallel sorting.
- */
- private static final int SEQUENTIAL = 0;
- private static final int PARALLEL = 87;
+ private static void fail(String message) {
+ throw new RuntimeException(message);
+ }
- /**
- * Heap sort will be invoked, if recursion depth is too big.
- * Value is taken from DualPivotQuicksort.MAX_RECURSION_DEPTH.
- */
- private static final int BIG_DEPTH = 64 * (3 << 1);
+ private final String name;
}
diff --git a/test/micro/org/openjdk/bench/java/util/ArraysSort.java b/test/micro/org/openjdk/bench/java/util/ArraysSort.java
index 3a1d5f9baec8b..3dbe7465073d9 100644
--- a/test/micro/org/openjdk/bench/java/util/ArraysSort.java
+++ b/test/micro/org/openjdk/bench/java/util/ArraysSort.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -20,144 +20,273 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
-package org.openjdk.bench.java.lang;
+
+package org.openjdk.bench.java.util;
+
+import java.util.Arrays;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
-import org.openjdk.jmh.annotations.OperationsPerInvocation;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
-import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Warmup;
-import org.openjdk.jmh.infra.Blackhole;
-import java.util.Arrays;
-import java.util.Random;
-import java.util.concurrent.TimeUnit;
-import java.io.UnsupportedEncodingException;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.reflect.Method;
/**
- * Performance test of Arrays.sort() methods
+ * Microbenchmarking of Arrays.sort() and Arrays.parallelSort().
+ *
+ * @author Vladimir Yaroslavskiy
+ *
+ * @version 2024.06.14
+ *
+ * @since 26
*/
-@Fork(value=1, jvmArgs={"-XX:CompileThreshold=1", "-XX:-TieredCompilation"})
+@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
-@State(Scope.Thread)
-@Warmup(iterations = 3, time=5)
-@Measurement(iterations = 3, time=3)
+@Warmup(iterations = 2, time = 4, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 4, time = 5, timeUnit = TimeUnit.SECONDS)
+@Fork(value = 1, jvmArgsAppend = {"-XX:CompileThreshold=1", "-XX:-TieredCompilation"})
public class ArraysSort {
- @Param({"10","25","50","75","100", "1000", "10000", "100000", "1000000"})
- private int size;
-
- private int[] ints_unsorted;
- private long[] longs_unsorted;
- private float[] floats_unsorted;
- private double[] doubles_unsorted;
-
- private int[] ints_sorted;
- private long[] longs_sorted;
- private float[] floats_sorted;
- private double[] doubles_sorted;
-
-
- public void initialize() {
- Random rnd = new Random(42);
-
- ints_unsorted = new int[size];
- longs_unsorted = new long[size];
- floats_unsorted = new float[size];
- doubles_unsorted = new double[size];
-
- int[] intSpecialCases = {Integer.MIN_VALUE, Integer.MAX_VALUE};
- long[] longSpecialCases = {Long.MIN_VALUE, Long.MAX_VALUE};
- float[] floatSpecialCases = {+0.0f, -0.0f, Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NaN};
- double[] doubleSpecialCases = {+0.0, -0.0, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN};
-
- for (int i = 0; i < size; i++) {
- ints_unsorted[i] = rnd.nextInt();
- longs_unsorted[i] = rnd.nextLong();
- if (i % 10 != 0) {
- ints_unsorted[i] = rnd.nextInt();
- longs_unsorted[i] = rnd.nextLong();
- floats_unsorted[i] = rnd.nextFloat();
- doubles_unsorted[i] = rnd.nextDouble();
- } else {
- ints_unsorted[i] = intSpecialCases[rnd.nextInt(intSpecialCases.length)];
- longs_unsorted[i] = longSpecialCases[rnd.nextInt(longSpecialCases.length)];
- floats_unsorted[i] = floatSpecialCases[rnd.nextInt(floatSpecialCases.length)];
- doubles_unsorted[i] = doubleSpecialCases[rnd.nextInt(doubleSpecialCases.length)];
- }
- }
- }
+ private static final int PARALLELISM = java.util.concurrent.ForkJoinPool.getCommonPoolParallelism();
+
+ @Param({ "600", "3000", "40000", "800000", "5000000" })
+ int size;
+
+ @Param
+ Builder builder;
+
+ int[] b;
@Setup
- public void setup() throws UnsupportedEncodingException, ClassNotFoundException, NoSuchMethodException, Throwable {
- initialize();
+ public void init() {
+ b = new int[size];
}
- @Setup(Level.Invocation)
- public void clear() {
- ints_sorted = ints_unsorted.clone();
- longs_sorted = longs_unsorted.clone();
- floats_sorted = floats_unsorted.clone();
- doubles_sorted = doubles_unsorted.clone();
- }
+ public enum Builder {
- @Benchmark
- public int[] intSort() throws Throwable {
- Arrays.sort(ints_sorted);
- return ints_sorted;
- }
+ REPEATED {
+ @Override
+ void build(int[] b) {
+ Random random = new Random(0x111);
+
+ for (int i = 0; i < b.length; ++i) {
+ b[i] = random.nextInt(5);
+ }
+ }
+ },
- @Benchmark
- public int[] intParallelSort() throws Throwable {
- Arrays.parallelSort(ints_sorted);
- return ints_sorted;
+ STAGGER {
+ @Override
+ void build(int[] b) {
+ for (int i = 0; i < b.length; ++i) {
+ b[i] = (i * 8) % b.length;
+ }
+ }
+ },
+
+ SHUFFLE {
+ @Override
+ void build(int[] b) {
+ Random random = new Random(0x999);
+
+ for (int i = 0, j = 0, k = 1; i < b.length; ++i) {
+ b[i] = random.nextInt(11) > 0 ? (j += 2) : (k += 2);
+ }
+ }
+ },
+
+ RANDOM {
+ @Override
+ void build(int[] b) {
+ Random random = new Random(0x777);
+
+ for (int i = 0; i < b.length; ++i) {
+ b[i] = random.nextInt();
+ }
+ }
+ };
+
+ abstract void build(int[] b);
}
- @Benchmark
- public long[] longSort() throws Throwable {
- Arrays.sort(longs_sorted);
- return longs_sorted;
+ public static class Int extends ArraysSort {
+
+ @Setup(Level.Invocation)
+ public void build() {
+ builder.build(b);
+ }
+
+ @Benchmark
+ public void testSort() {
+ Arrays.sort(b);
+ }
+
+ @Benchmark
+ public void testParallelSort() {
+ Arrays.parallelSort(b);
+ }
}
- @Benchmark
- public long[] longParallelSort() throws Throwable {
- Arrays.parallelSort(longs_sorted);
- return longs_sorted;
+ public static class Long extends ArraysSort {
+ long[] a;
+
+ @Setup
+ public void setup() {
+ a = new long[size];
+ }
+
+ @Setup(Level.Invocation)
+ public void build() {
+ builder.build(b);
+
+ for (int i = 0; i < size; ++i) {
+ a[i] = b[i];
+ }
+ }
+
+ @Benchmark
+ public void testSort() {
+ Arrays.sort(a);
+ }
+
+ @Benchmark
+ public void testParallelSort() {
+ Arrays.parallelSort(a);
+ }
}
- @Benchmark
- public float[] floatSort() throws Throwable {
- Arrays.sort(floats_sorted);
- return floats_sorted;
+ public static class Short extends ArraysSort {
+ short[] a;
+
+ @Setup
+ public void setup() {
+ a = new short[size];
+ }
+
+ @Setup(Level.Invocation)
+ public void build() {
+ builder.build(b);
+
+ for (int i = 0; i < size; ++i) {
+ a[i] = (short) b[i];
+ }
+ }
+
+ @Benchmark
+ public void testSort() {
+ Arrays.sort(a);
+ }
}
- @Benchmark
- public float[] floatParallelSort() throws Throwable {
- Arrays.parallelSort(floats_sorted);
- return floats_sorted;
+ public static class Byte extends ArraysSort {
+ byte[] a;
+
+ @Setup
+ public void setup() {
+ a = new byte[size];
+ }
+
+ @Setup(Level.Invocation)
+ public void build() {
+ builder.build(b);
+
+ for (int i = 0; i < size; ++i) {
+ a[i] = (byte) b[i];
+ }
+ }
+
+ @Benchmark
+ public void testSort() {
+ Arrays.sort(a);
+ }
}
- @Benchmark
- public double[] doubleSort() throws Throwable {
- Arrays.sort(doubles_sorted);
- return doubles_sorted;
+ public static class Char extends ArraysSort {
+ char[] a;
+
+ @Setup
+ public void setup() {
+ a = new char[size];
+ }
+
+ @Setup(Level.Invocation)
+ public void build() {
+ builder.build(b);
+
+ for (int i = 0; i < size; ++i) {
+ a[i] = (char) b[i];
+ }
+ }
+
+ @Benchmark
+ public void testSort() {
+ Arrays.sort(a);
+ }
}
- @Benchmark
- public double[] doubleParallelSort() throws Throwable {
- Arrays.parallelSort(doubles_sorted);
- return doubles_sorted;
+ public static class Float extends ArraysSort {
+ float[] a;
+
+ @Setup
+ public void setup() {
+ a = new float[size];
+ }
+
+ @Setup(Level.Invocation)
+ public void build() {
+ builder.build(b);
+
+ for (int i = 0; i < size; ++i) {
+ a[i] = b[i];
+ }
+ }
+
+ @Benchmark
+ public void testSort() {
+ Arrays.sort(a);
+ }
+
+ @Benchmark
+ public void testParallelSort() {
+ Arrays.parallelSort(a);
+ }
}
+ public static class Double extends ArraysSort {
+ double[] a;
+
+ @Setup
+ public void setup() {
+ a = new double[size];
+ }
+
+ @Setup(Level.Invocation)
+ public void build() {
+ builder.build(b);
+
+ for (int i = 0; i < size; ++i) {
+ a[i] = b[i];
+ }
+ }
+
+ @Benchmark
+ public void testSort() {
+ Arrays.sort(a);
+ }
+
+ @Benchmark
+ public void testParallelSort() {
+ Arrays.parallelSort(a);
+ }
+ }
}