/**
 * @constructor
 * @param {Sk.builtin.list=} list
 * @param {number=} length optional
 * @extends Sk.builtin.object
 */
Sk.builtin.timSort = function (list, length) {
    this.list = new Sk.builtin.list(list.v);
    // When we get into galloping mode, we stay there until both runs win less
    // often than MIN_GALLOP consecutive times.  See listsort.txt for more info.
    this.MIN_GALLOP = 7;
    if (length) {
        this.listlength = length;
    } else {
        this.listlength = list.sq$length();
    }
};

Sk.builtin.timSort.prototype.lt = function (a, b) {
    return Sk.misceval.richCompareBool(a, b, "Lt");
};

Sk.builtin.timSort.prototype.le = function (a, b) {
    return !this.lt(b, a);
};

Sk.builtin.timSort.prototype.setitem = function (item, value) {
    this.list.v[item] = value;
};

/*
 # binarysort is the best method for sorting small arrays: it does
 # few compares, but can do data movement quadratic in the number of
 # elements.
 # "a" is a contiguous slice of a list, and is sorted via binary insertion.
 # This sort is stable.
 # On entry, the first "sorted" elements are already sorted.
 # Even in case of error, the output slice will be some permutation of
 # the input (nothing is lost or duplicated)
 */
Sk.builtin.timSort.prototype.binary_sort = function (a, sorted) {
    var pivot;
    var p;
    var r;
    var l;
    var start;
    for (start = a.base + sorted; start < a.base + a.len; start++) {
        l = a.base;
        r = start;
        pivot = a.getitem(r);
        // Invariants:
        // pivot >= all in [base, l).
        // pivot  < all in [r, start).
        // The second is vacuously true at the start.
        while (l < r) {
            p = l + ((r - l) >> 1);
            if (this.lt(pivot, a.getitem(p))) {
                r = p;
            } else {
                l = p + 1;
            }
        }
        Sk.asserts.assert(l === r);
        // The invariants still hold, so pivot >= all in [base, l) and
        // pivot < all in [l, start), so pivot belongs at l.  Note
        // that if there are elements equal to pivot, l points to the
        // first slot after them -- that's why this sort is stable.
        // Slide over to make room.
        for (p = start; p > l; p--) {
            a.setitem(p, a.getitem(p - 1));
        }
        a.setitem(l, pivot);
    }
};

Sk.builtin.timSort.prototype.count_run = function (a) {
    /*
     # Compute the length of the run in the slice "a".
     # "A run" is the longest ascending sequence, with
     #
     #     a[0] <= a[1] <= a[2] <= ...
     #
     # or the longest descending sequence, with
     #
     #     a[0] > a[1] > a[2] > ...
     #
     # Return (run, descending) where descending is False in the former case,
     # or True in the latter.
     # For its intended use in a stable mergesort, the strictness of the defn of
     # "descending" is needed so that the caller can safely reverse a descending
     # sequence without violating stability (strict > ensures there are no equal
     # elements to get out of order).
     */
    var n;
    var p;
    var descending;
    if (a.len <= 1) {
        n = a.len;
        descending = false;
    } else {
        n = 2;
        if (this.lt(a.getitem(a.base + 1), a.getitem(a.base))) {
            descending = true;
            for (p = a.base + 2; p < a.base + a.len; p++) {
                if (this.lt(a.getitem(p), a.getitem(p - 1))) {
                    n++;
                } else {
                    break;
                }
            }
        } else {
            descending = false;
            for (p = a.base + 2; p < a.base + a.len; p++) {
                if (this.lt(a.getitem(p), a.getitem(p - 1))) {
                    break;
                } else {
                    n++;
                }
            }
        }
    }
    return {"run": new Sk.builtin.listSlice(a.list, a.base, n), "descending": descending};
};

Sk.builtin.timSort.prototype.sort = function () {
    /*
     # ____________________________________________________________
     # Entry point.
     */

    var minrun;
    var cr;
    var sorted;
    var remaining = new Sk.builtin.listSlice(this.list, 0, this.listlength);
    if (remaining.len < 2) {
        return;
    }

    // March over the array once, left to right, finding natural runs,
    // and extending short natural runs to minrun elements.
    this.merge_init();
    minrun = this.merge_compute_minrun(remaining.len);
    while (remaining.len > 0) {
        // Identify next run.
        cr = this.count_run(remaining);
        if (cr.descending) {
            cr.run.reverse();
        }
        // If short, extend to min(minrun, nremaining).
        if (cr.run.len < minrun) {
            sorted = cr.run.len;
            if (minrun < remaining.len) {
                cr.run.len = minrun;
            } else {
                cr.run.len = remaining.len;
            }
            this.binary_sort(cr.run, sorted);
        }
        // Advance remaining past this run.
        remaining.advance(cr.run.len);
        // Push run onto pending-runs stack, and maybe merge.
        this.pending.push(cr.run);
        this.merge_collapse();
    }
    Sk.asserts.assert(remaining.base == this.listlength);

    this.merge_force_collapse();
    Sk.asserts.assert(this.pending.length == 1);
    Sk.asserts.assert(this.pending[0].base === 0);
    Sk.asserts.assert(this.pending[0].len == this.listlength);
};

/*
 # Locate the proper position of key in a sorted vector; if the vector
 # contains an element equal to key, return the position immediately to the
 # left of the leftmost equal element -- or to the right of the rightmost
 # equal element if the flag "rightmost" is set.
 #
 # "hint" is an index at which to begin the search, 0 <= hint < a.len.
 # The closer hint is to the final result, the faster this runs.
 #
 # The return value is the index 0 <= k <= a.len such that
 #
 #     a[k-1] < key <= a[k]      (if rightmost is False)
 #     a[k-1] <= key < a[k]      (if rightmost is True)
 #
 # as long as the indices are in bound.  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.
 */
Sk.builtin.timSort.prototype.gallop = function (key, a, hint, rightmost) {
    var lower;
    var self;
    var p;
    var lastofs;
    var ofs;
    var maxofs;
    var hintminofs;
    var hintminlastofs;
    var m;
    Sk.asserts.assert(0 <= hint && hint < a.len);
    self = this;
    if (rightmost) {
        lower = function (a, b) {
            return self.le(a, b);
        }; // search for the largest k for which a[k] <= key
    } else {
        lower = function (a, b) {
            return self.lt(a, b);
        }; // search for the largest k for which a[k] < key
    }
    p = a.base + hint;
    lastofs = 0;
    ofs = 1;
    if (lower(a.getitem(p), key)) {
        // a[hint] < key -- gallop right, until
        // a[hint + lastofs] < key <= a[hint + ofs]

        maxofs = a.len - hint; // a[a.len-1] is highest
        while (ofs < maxofs) {
            if (lower(a.getitem(p + ofs), key)) {
                lastofs = ofs;
                try {
                    ofs = (ofs << 1) + 1;
                } catch (err) {
                    ofs = maxofs;
                }
            } else {
                // key <= a[hint + ofs]
                break;
            }
        }
        if (ofs > maxofs) {
            ofs = maxofs;
        }
        // Translate back to offsets relative to a.
        lastofs += hint;
        ofs += hint;
    } else {
        // key <= a[hint] -- gallop left, until
        // a[hint - ofs] < key <= a[hint - lastofs]
        maxofs = hint + 1;   // a[0] is lowest
        while (ofs < maxofs) {
            if (lower(a.getitem(p - ofs), key)) {
                break;
            } else {
                // key <= a[hint - ofs]
                lastofs = ofs;
                try {
                    ofs = (ofs << 1) + 1;
                } catch (err) {
                    ofs = maxofs;
                }
            }
        }
        if (ofs > maxofs) {
            ofs = maxofs;
        }
        // Translate back to positive offsets relative to a.
        hintminofs = hint - ofs;
        hintminlastofs = hint - lastofs;
        lastofs = hintminofs;
        ofs = hintminlastofs;
    }
    Sk.asserts.assert(-1 <= lastofs < ofs <= a.len);

    // Now a[lastofs] < key <= a[ofs], so key belongs somewhere to the
    // right of lastofs but no farther right than ofs.  Do a binary
    // search, with invariant a[lastofs-1] < key <= a[ofs].

    lastofs += 1;
    while (lastofs < ofs) {
        m = lastofs + ((ofs - lastofs) >> 1);
        if (lower(a.getitem(a.base + m), key)) {
            lastofs = m + 1;   // a[m] < key
        } else {
            ofs = m;         // key <= a[m]
        }
    }
    Sk.asserts.assert(lastofs == ofs);         // so a[ofs-1] < key <= a[ofs]
    return ofs;
};

// ____________________________________________________________

Sk.builtin.timSort.prototype.merge_init = function () {
    // This controls when we get *into* galloping mode.  It's initialized
    // to MIN_GALLOP.  merge_lo and merge_hi tend to nudge it higher for
    // random data, and lower for highly structured data.
    this.min_gallop = this.MIN_GALLOP;

    // A stack of n pending runs yet to be merged.  Run #i starts at
    // address pending[i].base and extends for pending[i].len elements.
    // It's always true (so long as the indices are in bounds) that
    //
    //     pending[i].base + pending[i].len == pending[i+1].base
    //
    // so we could cut the storage for this, but it's a minor amount,
    // and keeping all the info explicit simplifies the code.
    this.pending = [];
};

// Merge the slice "a" with the slice "b" in a stable way, in-place.
// a.len <= b.len.  See listsort.txt for more info.
// a.len and b.len must be > 0, and a.base + a.len == b.base.
// Must also have that b.list[b.base] < a.list[a.base], that
// a.list[a.base+a.len-1] belongs at the end of the merge, and should have

Sk.builtin.timSort.prototype.merge_lo = function (a, b) {
    var min_gallop;
    var dest;
    var acount, bcount;
    var p;
    Sk.asserts.assert(a.len > 0 && b.len > 0 && a.base + a.len == b.base);
    min_gallop = this.min_gallop;
    dest = a.base;
    a = a.copyitems();

    // Invariant: elements in "a" are waiting to be reinserted into the list
    // at "dest".  They should be merged with the elements of "b".
    // b.base == dest + a.len.
    // We use a finally block to ensure that the elements remaining in
    // the copy "a" are reinserted back into this.list in all cases.
    try {
        this.setitem(dest, b.popleft());

        dest++;
        if (a.len == 1 || b.len === 0) {
            return;
        }

        while (true) {
            acount = 0;   // number of times A won in a row
            bcount = 0;   // number of times B won in a row

            // Do the straightforward thing until (if ever) one run
            // appears to win consistently.
            while (true) {
                if (this.lt(b.getitem(b.base), a.getitem(a.base))) {
                    this.setitem(dest, b.popleft());
                    dest++;
                    if (b.len === 0) {
                        return;
                    }
                    bcount++;
                    acount = 0;
                    if (bcount >= min_gallop) {
                        break;
                    }
                } else {
                    this.setitem(dest, a.popleft());
                    dest++;
                    if (a.len == 1) {
                        return;
                    }
                    acount++;
                    bcount = 0;
                    if (acount >= min_gallop) {
                        break;
                    }
                }
            }

            // 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.
            min_gallop += 1;

            while (true) {
                min_gallop -= min_gallop > 1;
                this.min_gallop = min_gallop;
                acount = this.gallop(b.getitem(b.base), a, 0, true);
                for (p = a.base; p < a.base + acount; p++) {
                    this.setitem(dest, a.getitem(p));
                    dest++;
                }

                a.advance(acount);

                if (a.len <= 1) {
                    return;
                }

                this.setitem(dest, b.popleft());
                dest++;

                // a.len==0 is impossible now if the comparison
                // function is consistent, but we can't assume
                // that it is.
                if (b.len === 0) {
                    return;
                }

                bcount = this.gallop(a.getitem(a.base), b, 0, false);

                for (p = b.base; p < b.base + bcount; p++) {
                    this.setitem(dest, b.getitem(p));
                    dest++;
                }

                b.advance(bcount);
                if (b.len === 0) {
                    return;
                }
                this.setitem(dest, a.popleft());
                dest++;

                if (a.len == 1) {
                    return;
                }

                if (acount < this.MIN_GALLOP && bcount < this.MIN_GALLOP) {
                    break;
                }

                min_gallop++;  // penalize it for leaving galloping mode
                this.min_gallop = min_gallop;
            }
        }
    } finally {
        // The last element of a belongs at the end of the merge, so we copy
        // the remaining elements of b before the remaining elements of a.
        Sk.asserts.assert(a.len >= 0 && b.len >= 0);
        for (p = b.base; p < b.base + b.len; p++) {
            this.setitem(dest, b.getitem(p));
            dest++;
        }
        for (p = a.base; p < a.base + a.len; p++) {
            this.setitem(dest, a.getitem(p));
            dest++;
        }
    }
};

Sk.builtin.timSort.prototype.merge_hi = function (a, b) {
    var min_gallop;
    var dest;
    var acount, bcount, nexta, nextb;
    var k;
    var p;
    Sk.asserts.assert(a.len > 0 && b.len > 0 && a.base + a.len == b.base);
    min_gallop = this.min_gallop;
    dest = b.base + b.len;
    b = b.copyitems();

    // Invariant: elements in "a" are waiting to be reinserted into the list
    // at "dest".  They should be merged with the elements of "b".
    // b.base == dest + a.len.
    // We use a finally block to ensure that the elements remaining in
    // the copy "a" are reinserted back into this.list in all cases.
    try {
        dest--;
        this.setitem(dest, a.popright());

        if (a.len === 0 || b.len == 1) {
            return;
        }

        while (true) {
            acount = 0;   // number of times A won in a row
            bcount = 0;   // number of times B won in a row

            // Do the straightforward thing until (if ever) one run
            // appears to win consistently.
            while (true) {
                nexta = a.getitem(a.base + a.len - 1);
                nextb = b.getitem(b.base + b.len - 1);
                if (this.lt(nextb, nexta)) {
                    dest--;
                    this.setitem(dest, nexta);
                    a.len--;
                    if (a.len === 0) {
                        return;
                    }
                    acount++;
                    bcount = 0;
                    if (acount >= min_gallop) {
                        break;
                    }
                } else {
                    dest--;
                    this.setitem(dest, nextb);
                    b.len--;
                    if (b.len == 1) {
                        return;
                    }
                    bcount++;
                    acount = 0;
                    if (bcount >= min_gallop) {
                        break;
                    }
                }
            }

            // 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.
            min_gallop += 1;

            while (true) {
                min_gallop -= min_gallop > 1;
                this.min_gallop = min_gallop;
                nextb = b.getitem(b.base + b.len - 1);
                k = this.gallop(nextb, a, a.len - 1, true);
                acount = a.len - k;
                for (p = a.base + a.len - 1; p > a.base + k - 1; p--) {
                    dest--;
                    this.setitem(dest, a.getitem(p));
                }
                a.len -= acount;
                if (a.len === 0) {
                    return;
                }

                dest--;
                this.setitem(dest, b.popright());
                if (b.len == 1) {
                    return;
                }

                nexta = a.getitem(a.base + a.len - 1);
                k = this.gallop(nexta, b, b.len - 1, false);
                bcount = b.len - k;
                for (p = b.base + b.len - 1; p > b.base + k - 1; p--) {
                    dest--;
                    this.setitem(dest, b.getitem(p));
                }

                b.len -= bcount;

                // b.len==0 is impossible now if the comparison
                // function is consistent, but we can't assume
                // that it is.
                if (b.len <= 1) {
                    return;
                }
                dest--;
                this.setitem(dest, a.popright());
                if (a.len === 0) {
                    return;
                }

                if (acount < this.MIN_GALLOP && bcount < this.MIN_GALLOP) {
                    break;
                }

                min_gallop++;  // penalize it for leaving galloping mode
                this.min_gallop = min_gallop;
            }
        }
    } finally {
        // The last element of a belongs at the end of the merge, so we copy
        // the remaining elements of b before the remaining elements of a.
        Sk.asserts.assert(a.len >= 0 && b.len >= 0);
        for (p = a.base + a.len - 1; p > a.base - 1; p--) {
            dest--;
            this.setitem(dest, a.getitem(p));
        }
        for (p = b.base + b.len - 1; p > b.base - 1; p--) {
            dest--;
            this.setitem(dest, b.getitem(p));
        }
    }
};

// Merge the two runs at stack indices i and i+1.

Sk.builtin.timSort.prototype.merge_at = function (i) {
    var a;
    var b;
    var k;
    if (i < 0) {
        i = this.pending.length + i;
    }

    a = this.pending[i];
    b = this.pending[i + 1];
    Sk.asserts.assert(a.len > 0 && b.len > 0);
    Sk.asserts.assert(a.base + a.len == b.base);

    // Record the length of the combined runs and remove the run b
    this.pending[i] = new Sk.builtin.listSlice(this.list, a.base, a.len + b.len);
    this.pending.splice(i + 1, 1);

    // Where does b start in a?  Elements in a before that can be
    // ignored (already in place).
    k = this.gallop(b.getitem(b.base), a, 0, true);
    a.advance(k);
    if (a.len === 0) {
        return;
    }

    // Where does a end in b?  Elements in b after that can be
    // ignored (already in place).
    b.len = this.gallop(a.getitem(a.base + a.len - 1), b, b.len - 1, false);
    if (b.len === 0) {
        return;
    }

    // Merge what remains of the runs.  The direction is chosen to
    // minimize the temporary storage needed.
    if (a.len <= b.len) {
        this.merge_lo(a, b);
    } else {
        this.merge_hi(a, b);
    }
};

// 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 listsort.txt for more info.
Sk.builtin.timSort.prototype.merge_collapse = function () {
    var p = this.pending;
    while (p.length > 1) {
        if (p.length >= 3 && p[p.length - 3].len <= p[p.length - 2].len + p[p.length - 1].len) {
            if (p[p.length - 3].len < p[p.length - 1].len) {
                this.merge_at(-3);
            } else {
                this.merge_at(-2);
            }
        } else if (p[p.length - 2].len <= p[p.length - 1].len) {
            this.merge_at(-2);
        } else {
            break;
        }
    }
};

// Regardless of invariants, merge all runs on the stack until only one
// remains.  This is used at the end of the mergesort.

Sk.builtin.timSort.prototype.merge_force_collapse = function () {
    var p = this.pending;
    while (p.length > 1) {
        if (p.length >= 3 && p[p.length - 3].len < p[p.length - 1].len) {
            this.merge_at(-3);
        } else {
            this.merge_at(-2);
        }
    }
};
// Compute a good value for the minimum run length; natural runs shorter
// than this are boosted artificially via binary insertion.
//
// If n < 64, return n (it's too small to bother with fancy stuff).
// Else if n is an exact power of 2, return 32.
// Else return an int k, 32 <= k <= 64, such that n/k is close to, but
// strictly less than, an exact power of 2.
//
// See listsort.txt for more info.

Sk.builtin.timSort.prototype.merge_compute_minrun = function (n) {
    var r = 0;    // becomes 1 if any 1 bits are shifted off
    while (n >= 64) {
        r = r | n & 1;
        n >>= 1;
    }
    return n + r;
};

//ListSlice
/**
 * @constructor
 * @param {Sk.builtin.list=} list
 * @param {number=} base
 * @param {number=} len
 * @extends Sk.builtin.object
 */
Sk.builtin.listSlice = function (list, base, len) {
    this.list = list;
    this.base = base;
    this.len = len;
};

Sk.builtin.listSlice.prototype.copyitems = function () {
    //Make a copy of the slice of the original list
    var start = this.base;
    var stop = this.base + this.len;
    Sk.asserts.assert(0 <= start <= stop);
    return new Sk.builtin.listSlice(new Sk.builtin.list(this.list.v.slice(start, stop)), 0, this.len);
};

Sk.builtin.listSlice.prototype.advance = function (n) {
    this.base += n;
    this.len -= n;
    Sk.asserts.assert(this.base <= this.list.sq$length());
};

Sk.builtin.listSlice.prototype.getitem = function (item) {
    return this.list.v[item];
};

Sk.builtin.listSlice.prototype.setitem = function (item, value) {
    this.list.v[item] = value;
};

Sk.builtin.listSlice.prototype.popleft = function () {
    var result = this.list.v[this.base];
    this.base++;
    this.len--;
    return result;
};

Sk.builtin.listSlice.prototype.popright = function () {
    this.len--;
    return this.list.v[this.base + this.len];
};

Sk.builtin.listSlice.prototype.reverse = function () {
    // Reverse the slice in-place.
    var list_hi;
    var list_lo;
    var list = this.list;
    var lo = this.base;
    var hi = lo + this.len - 1;
    while (lo < hi) {
        list_hi = list.v[hi];
        list_lo = list.v[lo];
        list.v[lo] = list_hi;
        list.v[hi] = list_lo;
        lo++;
        hi--;
    }
};

Sk.exportSymbol("Sk.builtin.listSlice", Sk.builtin.listSlice);
Sk.exportSymbol("Sk.builtin.timSort", Sk.builtin.timSort);
