Timsort's Fastest Sorting Algorithms

background

Timsort is a hybrid, stable and efficient sorting algorithm, which originates from merge sorting and insert sorting, and aims to deal with a variety of real data well. It was implemented by Tim Peters in 2002 in Python programming language. The algorithm finds the subsequences of sorted data and uses this knowledge to sort the rest of the data more effectively. This is accomplished by merging identified subsequences (called runs) with existing runs until certain conditions are met. Timsort has been the standard sorting algorithm for Python since version 2.3. Today, Timsort is the default sorting algorithm for Python, Java, Android platforms and GNU Octave.

performance analysis


Photo Source: https://www.bigocheatsheet.com/

It is not difficult to see that Timsort and Mergesort have similar time complexity and are faster than other sorting algorithms.
Timsort actually borrows from the methods of insertion sort and merge sort.

principle

Run (dividing slot)

In fact, in reality, most of the given arrays are usually partially ordered (ascending or descending), which Timsort takes advantage of by ascending (the latter element is greater than or equal to the former element, a [i] <= a [i + 1]) and descending strictly (the former element is larger than the latter element a [i] > a [i + 1]). When the array is decomposed into several runs, the runs of ascending order remain unchanged, and the runs of strictly descending order are reversed. Finally, several runs of ascending order are obtained.
For example:

  1. Now there is an array of goals [1, 5, 9, 8, 6, 4, 5, 6, 7]
  2. We can see that [1, 5, 9] conforms to ascending order, [8, 6, 4] conforms to strict descending order, [4, 5, 6, 7] conforms to ascending order.
  3. Flip the strictly descending run and then piece together a new array [1, 5, 9, 6, 8, 4, 5, 6, 7]

Merge run

Let's assume that the new array is [... 6, 7, 8, 9, 10, 1, 2, 3, 4, 5...]
If we merge two run s using Mergesort alone

run1: [6, 7, 8, 9, 10]
run2: [1, 2, 3, 4, 5]

Mergesort compares the first element of the two run s

1 < 6, get 1
 2 < 6, get 2
 3 < 6, get 3
 4 < 6, get 4
 5 < 6, get 5

We found that traversing the entire run directly would consume performance if the run was long.
So since every run is in ascending order, there's no need to compare them one by one.
Here we introduce a new concept Galloping.

Galloping

For example, we will merge the following two run s

run1: [101, 102, 103, ... 200]
run2: [1, 2, 3, ..., 100]

Here, instead of comparing the elements one by one, we compare them by increasing 2^n (the n th power of 2).

run1[0] > run2[0]
run1[0] > run2[1]
run1[0] > run2[3]
run1[0] > run2[7]
run1[0] > run2[15]
       ...
run1[0] > run2[2^n - 1]
run1[0] <= run2[2^(n+1) - 1]

So we get a result run2 [2 ^ n - 1] < run1[0] <= run2 [2 ^ (n + 1) - 1], so we can know the general position of run1[0] in run2.
Since run2 is ascending order, we define left = run2[2^n - 1], right boundary run2[2^(n+1) - 1], and then use Binary Search (Binary Search) to locate run1[0] in run2 very efficiently.

In this way, the number of times we merge two run s is changed from O(N) to O(lgN).

But can we just skip Galloping and do Binary Search?
Let's give an example:

run1: [1, 3, 5, 7, 9 ... 2n+1]
run2: [0, 2, 4, 6, 8 ... 2n]

In this way, run1[0] is only larger than run2[0]. Each time we do Binary Search, we can only get a number of results, plus n times of Binary Search, so the time complexity changes from O(N) to O(NlgN).

The order of merging run s

When the original array becomes a set of several ascending runs, we need to merge two runs, but if we merge a long run with a short run, it will take a long time and only get a short run.

So Timsort maintains a stack, putting all runs on the stack, while satisfying that the length of run on the back stack is longer than the sum of the length of run on the first two stacks, so that the length of run decreases.

[... runA, runB, runC]

runA > runB + runC
runB > runC

Avoid run merges with too much difference in length.

Insertion sort

When the length of two run s is shorter (less than 32), the Binary Insertion Search is relatively fast.
The length of the specific run is short, and this amount will change dynamically in the calculation.

summary

This sort makes full use of the ordered parts of the original array, thus increasing the time consumed by the sort.
Timsort is not applicable if the data is very random or if there are many duplicate arrays.
This video will be better to watch in conjunction with this one. https://www.youtube.com/watch... (Need to turn over the wall)

JavaScript implementation version

Code source https://stackoverflow.com/que...

Array.prototype.timsort = function(comp){

    var global_a=this
    var MIN_MERGE = 32;
    var MIN_GALLOP = 7
    var runBase=[];
    var runLen=[];
    var stackSize = 0;
    var compare = comp;

    sort(this,0,this.length,compare);

    /*
         * The next two methods (which are package private and static) constitute the entire API of this class. Each of these methods
         * obeys the contract of the public method with the same signature in java.util.Arrays.
         */

    function sort (a, lo, hi, compare) {

        if (typeof compare != "function") {
            throw new Error("Compare is not a function.");
            return;
        }

        stackSize = 0;
        runBase=[];
        runLen=[];

        rangeCheck(a.length, lo, hi);
        var nRemaining = hi - lo;
        if (nRemaining < 2) return; // Arrays of size 0 and 1 are always sorted

        // If array is small, do a "mini-TimSort" with no merges
        if (nRemaining < MIN_MERGE) {
            var initRunLen = countRunAndMakeAscending(a, lo, hi, compare);
            binarySort(a, lo, hi, lo + initRunLen, compare);
            return;
        }

        /**
                 * March over the array once, left to right, finding natural runs, extending short natural runs to minRun elements, and
                 * merging runs to maintain stack invariant.
                 */
        var ts = [];
        var minRun = minRunLength(nRemaining);
        do {
            // Identify next run
            var runLenVar = countRunAndMakeAscending(a, lo, hi, compare);

            // If run is short, extend to min(minRun, nRemaining)
            if (runLenVar < minRun) {
                var force = nRemaining <= minRun ? nRemaining : minRun;
                binarySort(a, lo, lo + force, lo + runLenVar, compare);
                runLenVar = force;
            }

            // Push run onto pending-run stack, and maybe merge
            pushRun(lo, runLenVar);
            mergeCollapse();

            // Advance to find next run
            lo += runLenVar;
            nRemaining -= runLenVar;
        } while (nRemaining != 0);

        // Merge all remaining runs to complete sort
        mergeForceCollapse();
    }


    /**
         * Sorts the specified portion of the specified array using a binary insertion sort. This is the best method for sorting small
         * numbers of elements. It requires O(n log n) compares, but O(n^2) data movement (worst case).
         *
         * 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 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
         */
    function binarySort (a, lo, hi, start, compare) {
        if (start == lo) start++;
        for (; start < hi; start++) {
            var pivot = a[start];

            // Set left (and right) to the index where a[start] (pivot) belongs
            var left = lo;
            var right = start;
            /*
            * Invariants: pivot >= all in [lo, left). pivot < all in [right, start).
            */
            while (left < right) {
                var mid = (left + right) >>> 1;
                if (compare(pivot, a[mid]) < 0)
                    right = mid;
                else
                    left = mid + 1;
            }
            /*
            * The invariants still hold: pivot >= all in [lo, 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 elements over to make room to make room for pivot.
            */
            var n = start - left; // The number of elements to move
            // Switch is just an optimization for arraycopy in default case
            switch (n) {
            case 2:
                a[left + 2] = a[left + 1];
            case 1:
                a[left + 1] = a[left];
                break;
            default:
            arraycopy(a, left, a, left + 1, n);
            }
            a[left] = pivot;
        }
    }


    /**
         * Returns the length of the run beginning at the specified position in the specified array and reverses the run if it is
         * descending (ensuring that the run will always be ascending when the method returns).
         *
         * A run is the longest ascending sequence with:
         *
         * a[lo] <= a[lo + 1] <= a[lo + 2] <= ...
         *
         * or the longest descending sequence with:
         *
         * 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 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
         */
    function countRunAndMakeAscending (a, lo, hi, compare) {
        var runHi = lo + 1;

        // Find end of run, and reverse range if descending
        if (compare(a[runHi++], a[lo]) < 0) { // Descending
            while (runHi < hi && compare(a[runHi], a[runHi - 1]) < 0){
                runHi++;
            }
            reverseRange(a, lo, runHi);
        } else { // Ascending
            while (runHi < hi && compare(a[runHi], a[runHi - 1]) >= 0){
                runHi++;
            }
        }

        return runHi - lo;
    }

    /**
         * Reverse the specified range of the specified array.
         *
         * @param a the array in which a range is to be reversed
         * @param lo the index of the first element in the range to be reversed
         * @param hi the index after the last element in the range to be reversed
         */
    function /*private static void*/ reverseRange (/*Object[]*/ a, /*int*/ lo, /*int*/ hi) {
        hi--;
        while (lo < hi) {
            var t = a[lo];
            a[lo++] = a[hi];
            a[hi--] = t;
        }
    }


    /**
         * Returns the minimum acceptable run length for an array of the specified length. Natural runs shorter than this will be
         * extended with {@link #binarySort}.
         *
         * 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.
         *
         * 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
         */
    function /*private static int*/ minRunLength (/*int*/ n) {
        //var v=0;
        var r = 0; // Becomes 1 if any 1 bits are shifted off
        /*while (n >= MIN_MERGE) { v++;
            r |= (n & 1);
            n >>= 1;
        }*/
        //console.log("minRunLength("+n+") "+v+" vueltas, result="+(n+r));
        //return n + r;
        return n + 1;
    }

    /**
         * Pushes the specified run onto the pending-run stack.
         *
         * @param runBase index of the first element in the run
         * @param runLen the number of elements in the run
         */
    function pushRun (runBaseArg, runLenArg) {
        //console.log("pushRun("+runBaseArg+","+runLenArg+")");
        //this.runBase[stackSize] = runBase;
        //runBase.push(runBaseArg);
        runBase[stackSize] = runBaseArg;

        //this.runLen[stackSize] = runLen;
        //runLen.push(runLenArg);
        runLen[stackSize] = runLenArg;
        stackSize++;
    }

    /**
         * Examines the stack of runs waiting to be merged and merges adjacent runs until the stack invariants are reestablished:
         *
         * 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.
         */
    function mergeCollapse () {
        while (stackSize > 1) {
            var n = stackSize - 2;
            if (n > 0 && runLen[n - 1] <= runLen[n] + runLen[n + 1]) {
                if (runLen[n - 1] < runLen[n + 1]) n--;
                mergeAt(n);
            } else if (runLen[n] <= runLen[n + 1]) {
                mergeAt(n);
            } else {
                break; // Invariant is established
            }
        }
    }

    /**
         * Merges all runs on the stack until only one remains. This method is called once, to complete the sort.
         */
    function mergeForceCollapse () {
        while (stackSize > 1) {
            var n = stackSize - 2;
            if (n > 0 && runLen[n - 1] < runLen[n + 1]) n--;
            mergeAt(n);
        }
    }


    /**
         * Merges the two runs at stack indices i and i+1. Run i must be the penultimate or antepenultimate run on the stack. In other
         * words, i must be equal to stackSize-2 or stackSize-3.
         *
         * @param i stack index of the first of the two runs to merge
         */
    function mergeAt (i) {

        var base1 = runBase[i];
        var len1 = runLen[i];
        var base2 = runBase[i + 1];
        var len2 = runLen[i + 1];

        /*
        * Record the length of the combined runs; if i is the 3rd-last run now, also slide over the last run (which isn't involved
        * in this merge). The current run (i+1) goes away in any case.
        */
        //var stackSize = runLen.length;
        runLen[i] = len1 + len2;
        if (i == stackSize  - 3) {
            runBase[i + 1] = runBase[i + 2];
            runLen[i + 1] = runLen[i + 2];
        }
        stackSize--;

        /*
        * Find where the first element of run2 goes in run1. Prior elements in run1 can be ignored (because they're already in
        * place).
        */

        var k = gallopRight(global_a[base2], global_a, base1, len1, 0, compare);
        base1 += k;
        len1 -= k;
        if (len1 == 0) return;

        /*
        * Find where the last element of run1 goes in run2. Subsequent elements in run2 can be ignored (because they're already in
        * place).
        */
        len2 = gallopLeft(global_a[base1 + len1 - 1], global_a, base2, len2, len2 - 1, compare);

        if (len2 == 0) return;

        // Merge remaining runs, using tmp array with min(len1, len2) elements
        if (len1 <= len2)
            mergeLo(base1, len1, base2, len2);
        else
            mergeHi(base1, len1, base2, len2);
    }


    /**
         * Locates the position at which to insert the specified key into the specified sorted range; if the range contains an element
         * equal to key, returns the index of the leftmost equal element.
         *
         * @param key the key whose insertion point to search for
         * @param a the array in which to search
         * @param base the index of the first element in the range
         * @param len the length of the range; must be > 0
         * @param hint the index at which to begin the search, 0 <= hint < n. The closer hint is to the result, the faster this method
         *           will run.
         * @param c the comparator used to order the range, and to search
         * @return the int k, 0 <= k <= n such that a[b + k - 1] < key <= a[b + k], pretending that a[b - 1] is minus infinity and a[b
         *         + n] is infinity. In other words, key belongs at index b + k; or in other words, the first k elements of a should
         *         precede key, and the last n - k should follow it.
         */
    function gallopLeft (key, a, base, len, hint, compare) {
        var lastOfs = 0;
        var ofs = 1;
        if (compare(key, a[base + hint]) > 0) {
            // Gallop right until a[base+hint+lastOfs] < key <= a[base+hint+ofs]
            var maxOfs = len - hint;
            while (ofs < maxOfs && compare(key, a[base + hint + ofs]) > 0) {
                lastOfs = ofs;
                ofs = (ofs << 1) + 1;
                if (ofs <= 0) // int overflow
                    ofs = maxOfs;
            }
            if (ofs > maxOfs) ofs = maxOfs;

            // Make offsets relative to base
            lastOfs += hint;
            ofs += hint;
        } else { // key <= a[base + hint]
            // Gallop left until a[base+hint-ofs] < key <= a[base+hint-lastOfs]
            var maxOfs = hint + 1;
            while (ofs < maxOfs && compare(key, a[base + hint - ofs]) <= 0) {
                lastOfs = ofs;
                ofs = (ofs << 1) + 1;
                if (ofs <= 0) // int overflow
                    ofs = maxOfs;
            }
            if (ofs > maxOfs) ofs = maxOfs;

            // Make offsets relative to base
            var tmp = lastOfs;
            lastOfs = hint - ofs;
            ofs = hint - tmp;
        }

        /*
        * Now a[base+lastOfs] < key <= a[base+ofs], so key belongs somewhere to the right of lastOfs but no farther right than ofs.
        * Do a binary search, with invariant a[base + lastOfs - 1] < key <= a[base + ofs].
        */
        lastOfs++;
        while (lastOfs < ofs) {
            var m = lastOfs + ((ofs - lastOfs) >>> 1);

            if (compare(key, a[base + m]) > 0)
                lastOfs = m + 1; // a[base + m] < key
            else
                ofs = m; // key <= a[base + m]
        }
        return ofs;
    }

    /**
         * Like gallopLeft, except that if the range contains an element equal to key, gallopRight returns the index after the
         * rightmost equal element.
         *
         * @param key the key whose insertion point to search for
         * @param a the array [] in which to search
         * @param base the index of the first element in the range
         * @param len the length of the range; must be > 0
         * @param hint the index at which to begin the search, 0 <= hint < n. The closer hint is to the result, the faster this method
         *           will run.
         * @param c the comparator used to order the range, and to search
         * @return the int k, 0 <= k <= n such that a[b + k - 1] <= key < a[b + k]
         */
    function gallopRight (key, a, base, len, hint,  compare) {

        var ofs = 1;
        var lastOfs = 0;
        if (compare(key, a[base + hint]) < 0) {
            // Gallop left until a[b+hint - ofs] <= key < a[b+hint - lastOfs]
            var maxOfs = hint + 1;
            while (ofs < maxOfs && compare(key, a[base + hint - ofs]) < 0) {
                lastOfs = ofs;
                ofs = (ofs << 1) + 1;
                if (ofs <= 0) // int overflow
                    ofs = maxOfs;
            }
            if (ofs > maxOfs) ofs = maxOfs;

            // Make offsets relative to b
            var tmp = lastOfs;
            lastOfs = hint - ofs;
            ofs = hint - tmp;
        } else { // a[b + hint] <= key
            // Gallop right until a[b+hint + lastOfs] <= key < a[b+hint + ofs]
            var maxOfs = len - hint;
            while (ofs < maxOfs && compare(key, a[base + hint + ofs]) >= 0) {
                lastOfs = ofs;
                ofs = (ofs << 1) + 1;
                if (ofs <= 0) // int overflow
                    ofs = maxOfs;
            }
            if (ofs > maxOfs) ofs = maxOfs;

            // Make offsets relative to b
            lastOfs += hint;
            ofs += hint;
        }

        /*
        * Now a[b + lastOfs] <= key < a[b + ofs], so key belongs somewhere to the right of lastOfs but no farther right than ofs.
        * Do a binary search, with invariant a[b + lastOfs - 1] <= key < a[b + ofs].
        */
        lastOfs++;
        while (lastOfs < ofs) {
            var m = lastOfs + ((ofs - lastOfs) >>> 1);

            if (compare(key, a[base + m]) < 0)
                ofs = m; // key < a[b + m]
            else
                lastOfs = m + 1; // a[b + m] <= key
        }
        return ofs;
    }

    /**
    * Merges two adjacent runs in place, in a stable fashion. The first element of the first run must be greater than the first
    * element of the second run (a[base1] > a[base2]), and the last element of the first run (a[base1 + len1-1]) must be greater
    * than all elements of the second run.
    *
    * For performance, this method should be called only when len1 <= len2; its twin, mergeHi should be called if len1 >= len2.
    * (Either method may be called if len1 == len2.)
    *
    * @param base1 index of first element in first run to be merged
    * @param len1 length of first run to be merged (must be > 0)
    * @param base2 index of first element in second run to be merged (must be aBase + aLen)
    * @param len2 length of second run to be merged (must be > 0)
    */
    function mergeLo (base1, len1, base2, len2) {

        // Copy first run into temp array
        var a = global_a;// For performance
        var tmp=a.slice(base1,base1+len1);

        var cursor1 = 0; // Indexes into tmp array
        var cursor2 = base2; // Indexes int a
        var dest = base1; // Indexes int a

        // Move first element of second run and deal with degenerate cases
        a[dest++] = a[cursor2++];
        if (--len2 == 0) {
            arraycopy(tmp, cursor1, a, dest, len1);
            return;
        }
        if (len1 == 1) {
            arraycopy(a, cursor2, a, dest, len2);
            a[dest + len2] = tmp[cursor1]; // Last elt of run 1 to end of merge
            return;
        }

        var c = compare;// Use local variable for performance

        var minGallop = MIN_GALLOP; // "    " "     " "
        outer:
        while (true) {
            var count1 = 0; // Number of times in a row that first run won
            var count2 = 0; // Number of times in a row that second run won

            /*
            * Do the straightforward thing until (if ever) one run starts winning consistently.
            */
            do {
                if (compare(a[cursor2], tmp[cursor1]) < 0) {
                    a[dest++] = a[cursor2++];
                    count2++;
                    count1 = 0;
                    if (--len2 == 0) break outer;
                } else {
                    a[dest++] = tmp[cursor1++];
                    count1++;
                    count2 = 0;
                    if (--len1 == 1) break outer;
                }
            } while ((count1 | count2) < 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 {
                count1 = gallopRight(a[cursor2], tmp, cursor1, len1, 0, c);
                if (count1 != 0) {
                    arraycopy(tmp, cursor1, a, dest, count1);
                    dest += count1;
                    cursor1 += count1;
                    len1 -= count1;
                    if (len1 <= 1) // len1 == 1 || len1 == 0
                        break outer;
                }
                a[dest++] = a[cursor2++];
                if (--len2 == 0) break outer;

                count2 = gallopLeft(tmp[cursor1], a, cursor2, len2, 0, c);
                if (count2 != 0) {
                    arraycopy(a, cursor2, a, dest, count2);
                    dest += count2;
                    cursor2 += count2;
                    len2 -= count2;
                    if (len2 == 0) break outer;
                }
                a[dest++] = tmp[cursor1++];
                if (--len1 == 1) break outer;
                minGallop--;
            } while (count1 >= MIN_GALLOP | count2 >= MIN_GALLOP);
            if (minGallop < 0) minGallop = 0;
            minGallop += 2; // Penalize for leaving gallop mode
        } // End of "outer" loop
        this.minGallop = minGallop < 1 ? 1 : minGallop; // Write back to field

        if (len1 == 1) {
            arraycopy(a, cursor2, a, dest, len2);
            a[dest + len2] = tmp[cursor1]; // Last elt of run 1 to end of merge
        } else if (len1 == 0) {
            throw new Error("IllegalArgumentException. Comparison method violates its general contract!");
        } else {
            arraycopy(tmp, cursor1, a, dest, len1);
        }
    }


    /**
         * Like mergeLo, except that this method should be called only if len1 >= len2; mergeLo should be called if len1 <= len2.
         * (Either method may be called if len1 == len2.)
         *
         * @param base1 index of first element in first run to be merged
         * @param len1 length of first run to be merged (must be > 0)
         * @param base2 index of first element in second run to be merged (must be aBase + aLen)
         * @param len2 length of second run to be merged (must be > 0)
         */
    function mergeHi ( base1, len1, base2, len2) {

        // Copy second run into temp array
        var a = global_a;// For performance
        var tmp=a.slice(base2, base2+len2);

        var cursor1 = base1 + len1 - 1; // Indexes into a
        var cursor2 = len2 - 1; // Indexes into tmp array
        var dest = base2 + len2 - 1; // Indexes into a

        // Move last element of first run and deal with degenerate cases
        a[dest--] = a[cursor1--];
        if (--len1 == 0) {
            arraycopy(tmp, 0, a, dest - (len2 - 1), len2);
            return;
        }
        if (len2 == 1) {
            dest -= len1;
            cursor1 -= len1;
            arraycopy(a, cursor1 + 1, a, dest + 1, len1);
            a[dest] = tmp[cursor2];
            return;
        }

        var c = compare;// Use local variable for performance

        var minGallop = MIN_GALLOP; // "    " "     " "
        outer:
        while (true) {
            var count1 = 0; // Number of times in a row that first run won
            var count2 = 0; // Number of times in a row that second run won

            /*
            * Do the straightforward thing until (if ever) one run appears to win consistently.
            */
            do {
                if (compare(tmp[cursor2], a[cursor1]) < 0) {
                    a[dest--] = a[cursor1--];
                    count1++;
                    count2 = 0;
                    if (--len1 == 0) break outer;
                    } else {
                        a[dest--] = tmp[cursor2--];
                        count2++;
                        count1 = 0;
                        if (--len2 == 1) break outer;
                    }
            } while ((count1 | count2) < 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 {
                count1 = len1 - gallopRight(tmp[cursor2], a, base1, len1, len1 - 1, c);
                if (count1 != 0) {
                    dest -= count1;
                    cursor1 -= count1;
                    len1 -= count1;
                    arraycopy(a, cursor1 + 1, a, dest + 1, count1);
                    if (len1 == 0) break outer;
                }
                a[dest--] = tmp[cursor2--];
                if (--len2 == 1) break outer;

                count2 = len2 - gallopLeft(a[cursor1], tmp, 0, len2, len2 - 1, c);
                if (count2 != 0) {
                    dest -= count2;
                    cursor2 -= count2;
                    len2 -= count2;
                    arraycopy(tmp, cursor2 + 1, a, dest + 1, count2);
                    if (len2 <= 1) // len2 == 1 || len2 == 0
                        break outer;
                }
                a[dest--] = a[cursor1--];
                if (--len1 == 0) break outer;
                    minGallop--;
            } while (count1 >= MIN_GALLOP | count2 >= MIN_GALLOP);
            if (minGallop < 0) minGallop = 0;
            minGallop += 2; // Penalize for leaving gallop mode
        } // End of "outer" loop
        this.minGallop = minGallop < 1 ? 1 : minGallop; // Write back to field

        if (len2 == 1) {
            dest -= len1;
            cursor1 -= len1;
            arraycopy(a, cursor1 + 1, a, dest + 1, len1);
            a[dest] = tmp[cursor2]; // Move first elt of run2 to front of merge
        } else if (len2 == 0) {
            throw new Error("IllegalArgumentException. Comparison method violates its general contract!");
        } else {
            arraycopy(tmp, 0, a, dest - (len2 - 1), len2);
        }
    }


    /**
    * Checks that fromIndex and toIndex are in range, and throws an appropriate exception if they aren't.
    *
    * @param arrayLen the length of the array
    * @param fromIndex the index of the first element of the range
    * @param toIndex the index after the last element of the range
    * @throws IllegalArgumentException if fromIndex > toIndex
    * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 or toIndex > arrayLen
    */
    function rangeCheck (arrayLen, fromIndex, toIndex) {
        if (fromIndex > toIndex) throw new Error( "IllegalArgument fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
        if (fromIndex < 0) throw new Error( "ArrayIndexOutOfBounds "+fromIndex);
        if (toIndex > arrayLen) throw new Error( "ArrayIndexOutOfBounds "+toIndex);
    }
}

// java System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
function arraycopy(s,spos,d,dpos,len){
    var a=s.slice(spos,spos+len);
    while(len--){
        d[dpos+len]=a[len];
    }
}

Keywords: Java Python less Programming

Added by CreativeWebDsign on Thu, 05 Sep 2019 09:40:46 +0300