/* * Copyright (c) 2009-2012 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'jMonkeyEngine' nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.jme3.util; import java.util.Comparator; /** * Fast, stable sort used to sort geometries * * It's adapted from Tim Peters's work on list sorting for Python. More details * here http://svn.python.org/projects/python/trunk/Objects/listsort.txt * * here is the C code from which this class is based * http://svn.python.org/projects/python/trunk/Objects/listobject.c * * This class was also greatly inspired from java 7 TimSort by Josh Blosh with the * difference that the temporary necessary memory space are allocated as the * geometry list grows and reused all along the application execution. * * Usage : ListSort has to be instanciated and kept with the geometry list ( or * w/e it may have to sort) Then the allocate method has to be called to * allocate necessary tmp space needed for the sort. This should be called once * for optimal performance, but can be called several times if the length of the * list changes * * Disclaimer : I was intrigued by the use of val >>> 1 in java 7 Timsort class * instead of val / 2 (integer division). Micro benching revealed that val >>> 1 * is twice faster than val / 2 in java 6 and has similar perf in java 7. The * following code uses val >>> 1 when ever a value needs to be divided by 2 and * rounded to its floor * * * @author Nehon */ public class ListSort<T> { /** * Threshold for binary sort vs merge. Original algorithm use 64, java7 * TimSort uses 32 and I used 128, see this post for explanations : * http://hub.jmonkeyengine.org/groups/development-discussion-jme3/forum/topic/i-got-that-sorted-out-huhuhu/ */ private static final int MIN_SIZE = 128; private T[] array; private T[] tmpArray; private Comparator<T> comparator; /** * attribute temp vars for merging. This was used to unroll the merge_lo & * merge_hi function of original implementations that used massive labeled * goto branching and was almost unreadable */ int iterA, iterB, dest, lengthA, lengthB; /** * Number of runs to merge */ private int nbRuns = 0; /* Try to used a kind of structure like in the original implementation. * Ended up using 2 arrays as done in the java 7 Timsort. * Original implementation use a struct, but instanciation of this inner * class + array was a convoluted pain. */ /** * array of start indices in the original array for runs : run i sarting * index is at runIndices[i] */ private int[] runsIndices = null; /** * array of runs length in the original array : run i length is at * runLength[i] */ private int[] runsLength = null; /** * Length of the array to sort.(the passed on array is allocated by chunks * of 32, so its length may be bigger than the actual useful data to sort) */ private int length = 0; /** * MIN_GALLOP set to 7 constant as described in listsort.txt. this magic * number indicates how many wins should trigger the switch from binary * search to gallopping mode */ private static final int MIN_GALLOP = 7; /** * This variable allows to adjust when switching to galloping mode. lowered * when the data are "naturally" structured highered when data are random. */ private int minGallop = MIN_GALLOP; /** * Creates a ListSort */ public ListSort() { } /** * Allocate temp variables for the given length This method should be called * at least once, but only if the length of the list to sort changed before * sorting * * @param len */ public final void allocateStack(int len) { length = len; /* * We allocate a temp array of half the size of the array to sort. * the original implementation had a 256 maximum size for this and made * the temp array grow on demand * * Timsort consumes half the size of the original array to merge at WORST. * But considering we use the same temp array over and over across frames * There is a good chance we stumble upon the worst case scenario one * moment or another. * So we just always take half of the original array size. */ int tmpLen = len >>> 1; //if the array is null or tmpLen is above the actual length we allocate the array if (tmpArray == null || tmpLen > tmpArray.length) { //has to use Object for temp storage tmpArray = (T[]) new Object[tmpLen]; } /* * this part was taken from java 7 TimSort. * The original implementation use a stack of length 85, but this seems * to boost up performance for mid sized arrays. * I changed the numbers so they fit our MIN_SIZE parameter. * * Those numbers can be computed using this formula : * MIN_SIZE * 1.618^n = N * Where n is the size of the stack, and N the number element of the array to sort * If MIN_SIZE is changed you have to recompute those values. */ int stackLen = (len < 1400 ? 5 : len < 15730 ? 10 : len < 1196194 ? 19 : 40); //Same remark as with the temp array if (runsIndices == null || stackLen > runsIndices.length) { runsIndices = new int[stackLen]; runsLength = new int[stackLen]; } } /** * reset the runs stack to 0 */ private void clean() { for (int i = 0; i < runsIndices.length; i++) { runsIndices[i] = 0; runsLength[i] = 0; } } /** * Sort the given array given the comparator * @param array the array to sort * @param comparator the comparator to compare elements of the array */ public void sort(T[] array, Comparator<T> comparator) { this.array = array; this.comparator = comparator; clean(); int low = 0; int high = length; int remaining = high - low; /* * If array's size is bellow min_size we perform a binary insertion sort * but first we check if some existing ordered pattern exists to reduce * the size of data to be sorted */ if (remaining < MIN_SIZE) { int runLength = getRunLength(array, low, high, comparator); binaryInsertionSort(array, low, high, low + runLength, comparator); return; } /* * main iteration : compute minrun length, then iterate through the * array to find runs and merge them until they can be binary sorted * if their length < minLength */ int minLength = mergeComputeMinRun(remaining); while (remaining != 0) { int runLength = getRunLength(array, low, high, comparator); /* if runlength is bellow the threshold we binary sort the remaining * elements */ if (runLength < minLength) { int newLength = remaining <= minLength ? remaining : minLength; binaryInsertionSort(array, low, low + newLength, low + runLength, comparator); runLength = newLength; } // Add run to pending runs to merge and merge if necessary. runsIndices[nbRuns] = low; runsLength[nbRuns] = runLength; nbRuns++; mergeCollapse(); // Advance to find next run low += runLength; remaining -= runLength; } // Merge all remaining runs to complete sort mergeForceCollapse(); } /** * Return the length of the run beginning at lastId, in the slice [lastId, * lastId]. firstId < lastId is required on entry. "A run" is the longest * ascending sequence, with * * array[0] <= array[1] <= array[2] <= ... * * or the longest descending sequence, with * * array[0] > array[1] > array[2] > ... * * The original algorithm is returning a "descending" boolean that allow the * caller to reverse the array. Here for simplicity we reverse the array * when the run is descending * * @param array the array to search for run length * @param firstId index of the first element of the run * @param lastId index+1 of the last element of the run * @param comparator the comparator * @return the length of the run beginning at the specified position in the * specified array */ private int getRunLength(T[] array, int firstId, int lastId, Comparator<T> comparator) { int runEnd = firstId + 1; if (runEnd < lastId) { // if the range is > 1 we search for the end index of the run if (comparator.compare(array[runEnd++], array[firstId]) >= 0) { while (runEnd < lastId && comparator.compare(array[runEnd], array[runEnd - 1]) >= 0) { runEnd++; } } else { while (runEnd < lastId && comparator.compare(array[runEnd], array[runEnd - 1]) < 0) { runEnd++; } // the run's order is descending, it has to be reversed // original algorithmm return a descending = 1 value and the //reverse is done in the sort method. Looks good to have it here though reverseArray(array, firstId, runEnd); } return runEnd - firstId; } //runEnd == lastId -> length = 1 return 1; } /** * binarysort is the best method for sorting small arrays: it does few * compares, but can do data movement quadratic in the number of elements. * [firstId, lastId] is a contiguous slice of a list, and is sorted via * binary insertion. This sort is stable. On entry, must have firstId <= * start <= lastId, and that [firstId, start) is already sorted (pass start * == firstId if you don't know!). * * @param array the array to sort * @param firstId the index of the first element to sort * @param lastId the index+ of the last element to sort * @param start the index of the element to start sorting range * [firstId,satrt]is assumed to be already sorted * @param comparator the comparator */ private void binaryInsertionSort(T[] array, int firstId, int lastId, int start, Comparator<T> comparator) { if (firstId == start) { start++; } while (start < lastId) { T pivot = array[start]; // set left to where start belongs int left = firstId; int right = start; /* Invariants: * pivot >= all in [firstId, left). * pivot < all in [right, start). * The second is vacuously true at the start. */ while (left < right) { int middle = (left + right) >>> 1; if (comparator.compare(pivot, array[middle]) < 0) { right = middle; } else { left = middle + 1; } } /* * The invariants still hold, so pivot >= all in [firstId, left) and * pivot < all in [left, start), so pivot belongs at left. Note * that if there are elements equal to pivot, left points to the * first slot after them -- that's why this sort is stable. * Slide over to make room. */ int nbElems = start - left; /* * grabbed from java7 TimSort, the swich is an optimization to * arraycopy in case there are 1 or 2 elements only to copy */ switch (nbElems) { case 2: array[left + 2] = array[left + 1]; case 1: array[left + 1] = array[left]; break; default: System.arraycopy(array, left, array, left + 1, nbElems); } array[left] = pivot; start++; } } /** * returns the minimum run length for merging * * see http://svn.python.org/projects/python/trunk/Objects/listobject.c * almost exact copy of merge_compute_minrun function * * If n < MIN_SIZE, return n (it's too small to bother with fancy stuff). * Else if n is an exact power of 2, return MIN_SIZE / 2. Else return an int * k, MIN_SIZE / 2 <= k <= MIN_SIZE , such that n/k is close to, but * strictly less than, an exact power of 2. * * @param n length of the array * @return the minimum run length for */ private int mergeComputeMinRun(int n) { int r = 0; /* becomes 1 if any 1 bits are shifted off */ while (n >= MIN_SIZE) { r |= (n & 1); n >>= 1; } return n + r; } /** * Examine the stack of runs waiting to be merged, merging adjacent runs * until the stack invariants are re-established: * * 1. len[-3] > len[-2] + len[-1] 2. len[-2] > len[-1] * * See http://svn.python.org/projects/python/trunk/Objects/listobject.c very * similar to merge_collapse * * see http://svn.python.org/projects/python/trunk/Objects/listsort.txt * search for The Merge Pattern */ private void mergeCollapse() { while (nbRuns > 1) { int n = nbRuns - 2; //searching for runs to merge from the end of the stack if (n > 0 && runsLength[n - 1] <= runsLength[n] + runsLength[n + 1]) { if (runsLength[n - 1] < runsLength[n + 1]) { n--; } mergeRuns(n); } else if (runsLength[n] <= runsLength[n + 1]) { mergeRuns(n); } else { break; } } } /** * Merge all the remaining runs to merge */ private void mergeForceCollapse() { while (nbRuns > 1) { int n = nbRuns - 2; if (n > 0 && runsLength[n - 1] < runsLength[n + 1]) { n--; } mergeRuns(n); } } /** * Merge runs A and B where A index in the stack is idx and B index is idx+1 * * @param idx index of the first of two runs to merge */ private void mergeRuns(int idx) { int indexA = runsIndices[idx]; int lenA = runsLength[idx]; int indexB = runsIndices[idx + 1]; int lenB = runsLength[idx + 1]; /* * Record the length of the combined runs; if idx is the 3rd-last * run now, also slide over the last run (which isn't involved * in this merge). The current run (idx+1) goes away in any case. */ runsLength[idx] = lenA + lenB; if (idx == nbRuns - 3) { runsIndices[idx + 1] = runsIndices[idx + 2]; runsLength[idx + 1] = runsLength[idx + 2]; } nbRuns--; /* Where does B start in A? Elements in A before that can be * ignored (already in place). */ //didn't find proper naming for k as it's used inthe original implementation int k = gallopRight(array[indexB], array, indexA, lenA, 0, comparator); indexA += k; lenA -= k; if (lenA == 0) { return; } /* Where does A end in B? Elements in B after that can be * ignored (already in place). */ lenB = gallopLeft(array[indexA + lenA - 1], array, indexB, lenB, lenB - 1, comparator); if (lenB == 0) { return; } /* Merge what remains of the runs, using a temp array with * min(lengthA, lengthB) elements. */ if (lenA <= lenB) { mergeLow(indexA, lenA, indexB, lenB); } else { mergeHigh(indexA, lenA, indexB, lenB); } } /** * * Locate the proper position of key in an array; if the array contains an * element equal to key, return the position immediately to the left of the * leftmost equal element. [gallopRight() does the same except returns the * position to the right of the rightmost equal element (if any).] * * @param key the key to search * @param array is a sorted array with n elements, starting at array[0]. n * must be > 0. * @param idx the index to start * @param length the length of the run * @param hint is an index at which to begin the search, 0 <= hint < n. The * closer hint is to the final result, the faster this runs. * @param comparator the comparator used to order the range, and to search * @return is the int k in 0..n such that * * array[k-1] < key <= array[k] * * pretending that *(a-1) is minus infinity and array[n] is plus infinity. * IOW, key belongs at index k; or, IOW, the first k elements of a should * precede key, and the last n-k should follow key. */ private int gallopLeft(T key, T[] array, int idx, int length, int hint, Comparator<T> comparator) { int lastOffset = 0; int offset = 1; if (comparator.compare(key, array[idx + hint]) > 0) { /* array[hint] < key -- gallop right, until * array[hint + lastOffset] < key <= array[hint + offset] */ int maxOffset = length - hint; while (offset < maxOffset && comparator.compare(key, array[idx + hint + offset]) > 0) { lastOffset = offset; offset = (offset << 1) + 1; /* int overflow. * Note : not sure if that can happen but it's here in both * original and java 7 TimSort implementation */ if (offset <= 0) { offset = maxOffset; } } if (offset > maxOffset) { offset = maxOffset; } // Translate back to offsets relative to idx. lastOffset += hint; offset += hint; } else { /* key <= array[hint] -- gallop left, until * array[hint - offset] < key <= array[hint - lastOffset] */ int maxOffset = hint + 1; while (offset < maxOffset && comparator.compare(key, array[idx + hint - offset]) <= 0) { lastOffset = offset; offset = (offset << 1) + 1; /* int overflow. * Note : not sure if that can happen but it's here in both * original and java 7 TimSort implementation */ if (offset <= 0) { offset = maxOffset; } } if (offset > maxOffset) { offset = maxOffset; } // Translate back to positive offsets relative to idx. int k = lastOffset; lastOffset = hint - offset; offset = hint - k; } /* * Now array[idx+lastOffset] < key <= array[idx+offset], so key belongs somewhere * to the right of lastOffset but no farther right than offset. Do a binary * search, with invariant array[idx + lastOffset - 1] < key <= array[idx + offset]. */ lastOffset++; while (lastOffset < offset) { int m = lastOffset + ((offset - lastOffset) >>> 1); if (comparator.compare(key, array[idx + m]) > 0) { lastOffset = m + 1; // array[idx + m] < key } else { offset = m; // key <= array[idx + m] } } return offset; } /** * Exactly like gallopLeft(), except that if key already exists in * array[0:n], finds the position immediately to the right of the rightmost * equal value. * * The code duplication is massive, but this is enough different given that * we're sticking to "<" comparisons that it's much harder to follow if * written as one routine with yet another "left or right?" flag. * * @param key the key to search * @param array is a sorted array with n elements, starting at array[0]. n * must be > 0. * @param idx the index to start * @param length the length of the run * @param hint is an index at which to begin the search, 0 <= hint < n. The * closer hint is to the final result, the faster this runs. * @param comparator the comparator used to order the range, and to search * @return value is the int k in 0..n such that array[k-1] <= key < array[k] */ private int gallopRight(T key, T[] array, int idx, int length, int hint, Comparator<T> comparator) { int offset = 1; int lastOffset = 0; if (comparator.compare(key, array[idx + hint]) < 0) { /* key < array[hint] -- gallop left, until * array[hint - offset] <= key < array[hint - lastOffset] */ int maxOffset = hint + 1; while (offset < maxOffset && comparator.compare(key, array[idx + hint - offset]) < 0) { lastOffset = offset; offset = (offset << 1) + 1; /* int overflow. * Note : not sure if that can happen but it's here in both * original and java 7 TimSort implementation */ if (offset <= 0) { offset = maxOffset; } } if (offset > maxOffset) { offset = maxOffset; } // Translate back to offsets relative to idx. int k = lastOffset; lastOffset = hint - offset; offset = hint - k; } else { /* array[hint] <= key -- gallop right, until * array[hint + lastOffset] <= key < array[hint + offset] */ int maxOffset = length - hint; while (offset < maxOffset && comparator.compare(key, array[idx + hint + offset]) >= 0) { lastOffset = offset; offset = (offset << 1) + 1; /* int overflow. * Note : not sure if that can happen but it's here in both * original and java 7 TimSort implementation */ if (offset <= 0) { offset = maxOffset; } } if (offset > maxOffset) { offset = maxOffset; } // Translate back to offsets relative to idx. lastOffset += hint; offset += hint; } /* Now array[lastOffset] <= key < array[offset], so key belongs somewhere to the * right of lastOffset but no farther right than offset. Do a binary * search, with invariant array[lastOffset-1] <= key < array[offset]. */ lastOffset++; while (lastOffset < offset) { int m = lastOffset + ((offset - lastOffset) >>> 1); if (comparator.compare(key, array[idx + m]) < 0) { offset = m; //key < array[idx + m] } else { lastOffset = m + 1; // array[idx + m] <= key } } return offset; } /** * Merge the lenA elements starting at idxA with the lenB elements starting * at idxB in a stable way, in-place. lenA and lenB must be > 0, and idxA + * lenA = idxB Must also have that array[idxB] < array[idxA], that * array[idxA+lenA - 1] belongs at the end of the merge, and should have * lenA <= lenB. See listsort.txt for more info. * * @param idxA index of first element in run A * @param lengthA length of run A * @param idxB index of first element in run B * @param lengthB length of run B */ private void mergeLow(int idxA, int lenA, int idxB, int lenB) { lengthA = lenA; lengthB = lenB; iterA = 0; // Indexes into tmp array iterB = idxB; // Indexes int a dest = idxA; // Indexes int a Comparator<T> comp = this.comparator; T[] arr = this.array; T[] tempArray = tmpArray; System.arraycopy(arr, idxA, tempArray, 0, lengthA); arr[dest] = arr[iterB]; dest++; iterB++; innerMergeLow(comp, arr, tempArray); //minGallop shouldn't be < 1 minGallop = minGallop < 1 ? 1 : minGallop; if (lengthA == 1) {//CopyB label System.arraycopy(arr, iterB, arr, dest, lengthB); // The last element of run A belongs at the end of the merge. arr[dest + lengthB] = tempArray[iterA]; } else if(lengthA == 0){ throw new UnsupportedOperationException("Compare function result changed! " + "Make sure you do not modify the scene from" + " another thread and that the comparisons are not based" + " on NaN values."); } else {//Fail label System.arraycopy(tempArray, iterA, arr, dest, lengthA); } } /** * Attempt to unroll "goto" style original implementation. * this method uses and change temp attributes of the class * @param comp comparator * @param arr the array * @param tempArray the temp array */ public void innerMergeLow(Comparator<T> comp, T[] arr, T[] tempArray) { lengthB--; if (lengthB == 0 || lengthA == 1) { return; } while (true) { // Number of wins by run A int aWins = 0; // Number of wins by run B int bWins = 0; /* * Do the straightforward thing until (if ever) one run starts * winning consistently. */ do { if (comp.compare(arr[iterB], tempArray[iterA]) < 0) { arr[dest] = arr[iterB]; dest++; iterB++; bWins++; aWins = 0; lengthB--; if (lengthB == 0) { return; } } else { arr[dest] = tempArray[iterA]; dest++; iterA++; aWins++; bWins = 0; lengthA--; if (lengthA == 1) { return; } } } while ((aWins | bWins) < minGallop); /* * One run is winning so consistently that galloping may be a * huge win. So try that, and continue galloping until (if ever) * neither run appears to be winning consistently anymore. */ do { aWins = gallopRight(arr[iterB], tempArray, iterA, lengthA, 0, comp); if (aWins != 0) { System.arraycopy(tempArray, iterA, arr, dest, aWins); dest += aWins; iterA += aWins; lengthA -= aWins; /* lengthA==0 is impossible now if the comparison * function is consistent, but we can't assume * that it is. * a propper error will be thrown in mergeLow if lengthA == 0 */ if (lengthA <= 1){ return; } } arr[dest] = arr[iterB]; dest++; iterB++; lengthB--; if (lengthB == 0) { return; } bWins = gallopLeft(tempArray[iterA], arr, iterB, lengthB, 0, comp); if (bWins != 0) { System.arraycopy(arr, iterB, arr, dest, bWins); dest += bWins; iterB += bWins; lengthB -= bWins; if (lengthB == 0) { return; } } arr[dest] = tempArray[iterA]; dest++; iterA++; lengthA--; if (lengthA == 1) { return; } minGallop--; } while (aWins >= MIN_GALLOP || bWins >= MIN_GALLOP); if (minGallop < 0) { minGallop = 0; } //original implementation uses +1 to penalize, Java7 Timsort uses +2 minGallop += 2; // Penalize for leaving gallop mode } } /** * Merge the lenA elements starting at idxA with the lenB elements starting * at idxB in a stable way, in-place. lenA and lenBb must be > 0, and idxA + * lenAa == idxB. Must also have that array[idxB] < array[idxA], that * array[idxA + Len1 - 1] belongs at the end of the merge, and should have * lenA >= lenB. See listsort.txt for more info. * * @param idxA index of first element in run A * @param lengthA length of run A * @param idxB index of first element in run B * @param lengthB length of run B */ private void mergeHigh(int idxA, int lenA, int idxB, int lenB) { lengthA = lenA; lengthB = lenB; iterA = idxA + lengthA - 1; iterB = lengthB - 1; dest = idxB + lengthB - 1; Comparator<T> comp = this.comparator; T[] arr = this.array; T[] tempArray = tmpArray; System.arraycopy(arr, idxB, tempArray, 0, lengthB); arr[dest] = arr[iterA]; dest--; iterA--; innerMergeHigh(comp, tempArray, arr, idxA); //minGallop shouldn't be < 1; minGallop = minGallop < 1 ? 1 : minGallop; if (lengthB == 1) {//CopyA label dest -= lengthA; iterA -= lengthA; System.arraycopy(arr, iterA + 1, arr, dest + 1, lengthA); // The first element of run B belongs at the front of the merge. arr[dest] = tempArray[iterB]; } else if (lengthB == 0) { throw new UnsupportedOperationException("Compare function result changed! " + "Make sure you do not modify the scene from another thread!"); } else {//Fail label System.arraycopy(tempArray, 0, arr, dest - (lengthB - 1), lengthB); } } /** * Attempt to unroll "goto" style original implementation. * this method uses and change temp attributes of the class * @param comp comparator * @param arr the array * @param tempArray the temp array * @param idxA the index of the first element of run A */ public void innerMergeHigh(Comparator<T> comp, T[] tempArray, T[] arr, int idxA) { lengthA--; if (lengthA == 0 || lengthB == 1) { return; } if (lengthB == 1) { return; } while (true) { // Number of wins by run A int aWins = 0; // Number of wins by run B int bWins = 0; /* * Do the straightforward thing until (if ever) one run * appears to win consistently. */ do { if (comp.compare(tempArray[iterB], arr[iterA]) < 0) { arr[dest] = arr[iterA]; dest--; iterA--; aWins++; bWins = 0; lengthA --; if (lengthA == 0) { return; } } else { arr[dest] = tempArray[iterB]; dest--; iterB--; bWins++; aWins = 0; lengthB--; if (lengthB == 1) { return; } } } while ((aWins | bWins) < minGallop); /* * One run is winning so consistently that galloping may be a * huge win. So try that, and continue galloping until (if ever) * neither run appears to be winning consistently anymore. */ do { aWins = lengthA - gallopRight(tempArray[iterB], arr, idxA, lengthA, lengthA - 1, comp); if (aWins != 0) { dest -= aWins; iterA -= aWins; lengthA -= aWins; System.arraycopy(arr, iterA + 1, arr, dest + 1, aWins); if (lengthA == 0) { return; } } arr[dest] = tempArray[iterB]; dest--; iterB--; lengthB--; if (lengthB == 1) { return; } bWins = lengthB - gallopLeft(arr[iterA], tempArray, 0, lengthB, lengthB - 1, comp); if (bWins != 0) { dest -= bWins; iterB -= bWins; lengthB -= bWins; System.arraycopy(tempArray, iterB + 1, arr, dest + 1, bWins); /* lengthB==0 is impossible now if the comparison * function is consistent, but we can't assume * that it is. * a propper error will be thrown in mergeLow if lengthB == 0 */ if (lengthB <= 1){ return; } } arr[dest] = arr[iterA]; dest--; iterA--; lengthA--; if (lengthA == 0) { return; } minGallop--; } while (aWins >= MIN_GALLOP || bWins >= MIN_GALLOP); if (minGallop < 0) { minGallop = 0; } //original implementation uses +1 to penalize, Java7 Timsort uses +2 minGallop += 2; // Penalize for leaving gallop mode } } /** * Reverse an array from firstId to lastId * * @param array the array to reverse * @param firstId the index where to start to reverse * @param lastId the index where to stop to reverse */ private static void reverseArray(Object[] array, int firstId, int lastId) { lastId--; while (firstId < lastId) { Object o = array[firstId]; array[firstId] = array[lastId]; array[lastId] = o; firstId++; lastId--; } } /** * return the useful length of the array being sorted * @return the length pass to the last allocateStack method */ public int getLength() { return length; } /* * test case */ public static void main(String[] argv) { Integer[] arr = new Integer[]{5, 6, 2, 9, 10, 11, 12, 8, 3, 12, 3, 7, 12, 32, 458, 12, 5, 3, 78, 45, 12, 32, 58, 45, 65, 45, 98, 45, 65, 2, 3, 47, 21, 35}; ListSort ls = new ListSort(); ls.allocateStack(34); ls.sort(arr, new Comparator<Integer>() { public int compare(Integer o1, Integer o2) { int x = o1 - o2; return (x == 0) ? 0 : (x > 0) ? 1 : -1; } }); for (Integer integer : arr) { System.err.print(integer + ","); } System.err.println(); } }