Quellcode durchsuchen

Swich geometry sorting to tim sort.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/branches/gradle-restructure@10998 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
rem..om vor 11 Jahren
Ursprung
Commit
af58df2fc0

+ 9 - 15
jme3-core/src/main/java/com/jme3/renderer/queue/GeometryList.java

@@ -33,7 +33,7 @@ package com.jme3.renderer.queue;
 
 import com.jme3.renderer.Camera;
 import com.jme3.scene.Geometry;
-import com.jme3.util.SortUtil;
+import com.jme3.util.ListSort;
 
 /**
  * This class is a special purpose list of {@link Geometry} objects for render
@@ -47,8 +47,8 @@ public class GeometryList {
 
     private static final int DEFAULT_SIZE = 32;
 
-    private Geometry[] geometries;
-    private Geometry[] geometries2;
+    private Geometry[] geometries;    
+    private ListSort listSort;
     private int size;
     private GeometryComparator comparator;
 
@@ -60,9 +60,9 @@ public class GeometryList {
      */
     public GeometryList(GeometryComparator comparator) {
         size = 0;
-        geometries = new Geometry[DEFAULT_SIZE];
-        geometries2 = new Geometry[DEFAULT_SIZE];
+        geometries = new Geometry[DEFAULT_SIZE];      
         this.comparator = comparator;
+        listSort = new ListSort<Geometry>();
     }
 
     public void setComparator(GeometryComparator comparator) {
@@ -118,8 +118,6 @@ public class GeometryList {
             Geometry[] temp = new Geometry[size * 2];
             System.arraycopy(geometries, 0, temp, 0, size);
             geometries = temp; // original list replaced by double-size list
-
-            geometries2 = new Geometry[size * 2];
         }
         geometries[size++] = g;
     }
@@ -141,14 +139,10 @@ public class GeometryList {
     public void sort() {
         if (size > 1) {
             // sort the spatial list using the comparator
-
-//            SortUtil.qsort(geometries, 0, size, comparator);
-//            Arrays.sort(geometries, 0, size, comparator);
-
-            System.arraycopy(geometries, 0, geometries2, 0, size);
-            SortUtil.msort(geometries2, geometries, 0, size-1, comparator);
-
-
+            if(listSort.getLength() != size){
+                listSort.allocateStack(size);
+            }                       
+            listSort.sort(geometries,comparator);
         }
     }
 }

+ 1012 - 0
jme3-core/src/main/java/com/jme3/util/ListSort.java

@@ -0,0 +1,1012 @@
+/*
+ * 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 veriables 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 &lt; 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 &lt; 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 &lt;= k &lt;= 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 firts 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("Inconsistant comparison function");
+        } 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("Inconsistant comparison function");
+        } 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();
+    }
+
+   
+    
+}
+