* This constant should be a power of two. It was 64 in Tim Peter's C * implementation, but 32 was empirically determined to work better in * this implementation. In the unlikely event that you set this constant * to be a number that's not a power of two, you'll need to change the * {@link #minRunLength} computation. - * + *
* If you decrease this constant, you must change the stackLen
* computation in the TimSort constructor, or you risk an
* ArrayOutOfBounds exception. See listsort.txt for a discussion
* of the minimum stack length required as a function of the length
* of the array being sorted and the minimum merge sequence length.
*/
- private static final int MIN_MERGE = 32;
+ static final int MIN_MERGE = 32;
/**
* The array being sorted.
*/
private final T[] a;
+ private final int rangeSize;
/**
* The comparator for this sort.
@@ -95,7 +99,7 @@ class FasterFinnSort
+ * runBase[i] + runLen[i] == runBase[i + 1]
+ *
* so we could cut the storage for this, but it's a minor amount,
* and keeping all the info explicit simplifies the code.
*/
- private int stackSize = 0; // Number of pending runs on stack
+ int stackSize = 0; // Number of pending runs on stack
private final int[] runBase;
private final int[] runLen;
+ private final int[] runPower;
/**
* Creates a TimSort instance to maintain the state of an ongoing sort.
*
- * @param a the array to be sorted
- * @param c the comparator to determine the order of the sort
- * @param work a workspace array (slice)
+ * @param a the array to be sorted
+ * @param c the comparator to determine the order of the sort
+ * @param work a workspace array (slice)
* @param workBase origin of usable space in work array
- * @param workLen usable size of work array
+ * @param workLen usable size of work array
*/
- private FasterFinnSort(T[] a, Comparator super T> c, T[] work, int workBase, int workLen) {
+ FasterFinnSort(T[] a, Comparator super T> c, T[] work, int workBase, int workLen, int rangeSize) {
this.a = a;
this.c = c;
-
+ this.rangeSize = rangeSize;
// Allocate temp storage (which may be increased later if necessary)
int len = a.length;
int tlen = (len < 2 * INITIAL_TMP_STORAGE_LENGTH) ?
len >>> 1 : INITIAL_TMP_STORAGE_LENGTH;
if (work == null || workLen < tlen || workBase + tlen > work.length) {
@SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"})
- T[] newArray = (T[])java.lang.reflect.Array.newInstance
+ T[] newArray = (T[]) java.lang.reflect.Array.newInstance
(a.getClass().getComponentType(), tlen);
tmp = newArray;
tmpBase = 0;
tmpLen = tlen;
- }
- else {
+ } else {
tmp = work;
tmpBase = workBase;
tmpLen = workLen;
}
- /*
- * Allocate runs-to-be-merged stack (which cannot be expanded). The
- * stack length requirements are described in listsort.txt. The C
- * version always uses the same stack length (85), but this was
- * measured to be too expensive when sorting "mid-sized" arrays (e.g.,
- * 100 elements) in Java. Therefore, we use smaller (but sufficiently
- * large) stack lengths for smaller arrays. The "magic numbers" in the
- * computation below must be changed if MIN_MERGE is decreased. See
- * the MIN_MERGE declaration above for more information.
- * The maximum value of 49 allows for an array up to length
- * Integer.MAX_VALUE-4, if array is filled by the worst case stack size
- * increasing scenario. More explanations are given in section 4 of:
- * http://envisage-project.eu/wp-content/uploads/2015/02/sorting.pdf
- */
- int stackLen = (len < 120 ? 5 :
- len < 1542 ? 10 :
- len < 119151 ? 24 : 49);
+ // TODO: Verify if this is correct
+ int stackLen = ((int) Math.ceil(Math.log(rangeSize) / Math.log(2))) + 2;
runBase = new int[stackLen];
runLen = new int[stackLen];
+ runPower = new int[stackLen];
}
/*
@@ -200,24 +190,24 @@ class FasterFinnSort
* If the initial part of the specified range is already sorted,
* this method can take advantage of it: the method assumes that the
* elements from index {@code lo}, inclusive, to {@code start},
* exclusive are already sorted.
*
- * @param a the array in which a range is to be sorted
- * @param lo the index of the first element in the range to be sorted
- * @param hi the index after the last element in the range to be sorted
+ * @param a the array in which a range is to be sorted
+ * @param lo the index of the first element in the range to be sorted
+ * @param hi the index after the last element in the range to be sorted
* @param start the index of the first element in the range that is
- * not already known to be sorted ({@code lo <= start <= hi})
- * @param c comparator to used for the sort
+ * not already known to be sorted ({@code lo <= start <= hi})
+ * @param c comparator to used for the sort
*/
@SuppressWarnings("fallthrough")
- private static
* A run is the longest ascending sequence with:
- *
- * a[lo] <= a[lo + 1] <= a[lo + 2] <= ...
- *
+ *
+ * a[lo] <= a[lo + 1] <= a[lo + 2] <= ...
+ *
* or the longest descending sequence with:
- *
- * a[lo] > a[lo + 1] > a[lo + 2] > ...
- *
+ *
+ * a[lo] > a[lo + 1] > a[lo + 2] > ...
+ *
* For its intended use in a stable mergesort, the strictness of the
* definition of "descending" is needed so that the call can safely
* reverse a descending sequence without violating stability.
*
- * @param a the array in which a run is to be counted and possibly reversed
+ * @param a the array in which a run is to be counted and possibly reversed
* @param lo index of the first element in the run
* @param hi index after the last element that may be contained in the run.
- * It is required that {@code lo < hi}.
- * @param c the comparator to used for the sort
- * @return the length of the run beginning at the specified position in
- * the specified array
+ * It is required that {@code lo < hi}.
+ * @param c the comparator to used for the sort
+ * @return the length of the run beginning at the specified position in
+ * the specified array
*/
- private static
* Roughly speaking, the computation is:
- *
- * If n < MIN_MERGE, return n (it's too small to bother with fancy stuff).
- * Else if n is an exact power of 2, return MIN_MERGE/2.
- * Else return an int k, MIN_MERGE/2 <= k <= MIN_MERGE, such that n/k
- * is close to, but strictly less than, an exact power of 2.
- *
+ *
+ * If n < MIN_MERGE, return n (it's too small to bother with fancy stuff).
+ * Else if n is an exact power of 2, return MIN_MERGE/2.
+ * Else return an int k, MIN_MERGE/2 <= k <= MIN_MERGE, such that n/k
+ * is close to, but strictly less than, an exact power of 2.
+ *
* For the rationale, see listsort.txt.
*
* @param n the length of the array to be sorted
* @return the length of the minimum run to be merged
*/
- private static int minRunLength(int n) {
+ int minRunLength(int n) {
assert n >= 0;
int r = 0; // Becomes 1 if any 1 bits are shifted off
while (n >= MIN_MERGE) {
@@ -415,41 +407,115 @@ class FasterFinnSort
+ * 1. runLen[i - 3] > runLen[i - 2] + runLen[i - 1]
+ * 2. runLen[i - 2] > runLen[i - 1]
+ *
* This method is called each time a new run is pushed onto the stack,
* so the invariants are guaranteed to hold for i < stackSize upon
* entry to the method.
- *
+ *
* Thanks to Stijn de Gouw, Jurriaan Rot, Frank S. de Boer,
* Richard Bubel and Reiner Hahnle, this is fixed with respect to
* the analysis in "On the Worst-Case Complexity of TimSort" by
* Nicolas Auger, Vincent Jug, Cyril Nicaud, and Carine Pivoteau.
*/
- private void mergeCollapse() {
+ void mergeCollapse(int power) {
while (stackSize > 1) {
- int n = stackSize - 2;
- if (n > 0 && runLen[n-1] <= runLen[n] + runLen[n+1] ||
- n > 1 && runLen[n-2] <= runLen[n] + runLen[n-1]) {
- if (runLen[n - 1] < runLen[n + 1])
- n--;
- } else if (n < 0 || runLen[n] > runLen[n + 1]) {
+ if (power < runPower[stackSize - 1]) {
+ mergeAt(stackSize - 2);
+ } else {
break; // Invariant is established
}
- mergeAt(n);
}
}
+/*
+ private int power(int stackSize) {
+ if (stackSize == 0) {
+ return 0;
+ }
+ // int = (right - left + 1); = RangeSize
+ long l = runLen[stackSize - 1]; // + (long) runBase[stackSize]; // - ((long) left << 1); // 2*middleA
+ long r = runLen[stackSize]; // - ((long) left << 1); // 2*middleB
+ int a = (int) ((l << 30) / rangeSize); // middleA / 2n
+ int b = (int) ((r << 30) / rangeSize); // middleB / 2n
+ return Integer.numberOfLeadingZeros(a ^ b);
+ }
+*/
+ int power(int stackSize, int runLen) {
+ /*
+ System.out.println(Arrays.toString(runBase));
+ System.out.println(Arrays.toString(runLen));
+ System.out.println(Arrays.toString(runPower));
+ System.out.println(stackSize);
+ System.out.println(rangeSize);
+ System.out.println();
+ */
+ if (stackSize == 0)
+ return 0;
+
+ int n_1 = this.runLen[stackSize - 1];
+ int n_2 = runLen;
+
+ int a = 2 * this.runBase[stackSize - 1] + n_1;
+ int b = a + n_1 + n_2;
+
+ int result = 0;
+
+ while (true) {
+ ++result;
+ if (a >= rangeSize) {
+ a -= rangeSize;
+ b -= rangeSize;
+ }
+ if (b >= rangeSize) {
+ break;
+ }
+ a <<= 1;
+ b <<= 1;
+ }
+ return result;
+ }
+
+/*
+ public int power(int stackSize) {
+
+ System.out.println(Arrays.toString(runBase));
+ System.out.println(Arrays.toString(runLen));
+ System.out.println(Arrays.toString(runPower));
+ System.out.println(stackSize);
+ System.out.println(rangeSize);
+ System.out.println();
+
+
+ if (stackSize == 0)
+ return 0;
+
+ int n_1 = this.runLen[stackSize - 1];
+ int n_2 = this.runLen[stackSize];
+
+ double a = ((double) this.runBase[stackSize - 1] + 0.5d * n_1 - 1d) / this.rangeSize;
+ double b = ((double) this.runBase[stackSize] + 0.5d * n_2 - 1d) / this.rangeSize;
+ int l = 0;
+ while ((int) (a * pow(2, l)) == (int) (b * pow(2 ,l))) {
+
+ l++;
+ }
+ return l;
+ }
+ */
+
+
/*
Backup mergeCollapse() von TimSort:
@@ -473,7 +539,7 @@ class FasterFinnSort