/*
 *  Copyright 2018 Alexey Andreev.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
"use strict";

var Long_divRem;
var Long_udivRem;
var Long_shiftLeft16;
var Long_shiftRight16;
var LongInt;
var LongInt_mul;
var LongInt_sub;
var LongInt_add;
var LongInt_inc;
var LongInt_dec;
var LongInt_ucompare;
var LongInt_numOfLeadingZeroBits;
var LongInt_shl;
var LongInt_shr;
var LongInt_copy;
var LongInt_div;
var Long_eq;
var Long_ne;
var Long_gt;
var Long_ge;
var Long_lt;
var Long_le;
var Long_compare;
var Long_ucompare;
var Long_add;
var Long_sub;
var Long_inc;
var Long_dec;
var Long_mul;
var Long_div;
var Long_rem;
var Long_udiv;
var Long_urem;
var Long_neg;
var Long_and;
var Long_or;
var Long_xor;
var Long_shl;
var Long_shr;
var Long_shru;
var Long_not;

if (typeof BigInt !== 'function') {
    Long_eq = function(a, b) {
        return a.hi === b.hi && a.lo === b.lo;
    }

    Long_ne = function(a, b) {
        return a.hi !== b.hi || a.lo !== b.lo;
    }

    Long_gt = function(a, b) {
        if (a.hi < b.hi) {
            return false;
        }
        if (a.hi > b.hi) {
            return true;
        }
        var x = a.lo >>> 1;
        var y = b.lo >>> 1;
        if (x !== y) {
            return x > y;
        }
        return (a.lo & 1) > (b.lo & 1);
    }

    Long_ge = function(a, b) {
        if (a.hi < b.hi) {
            return false;
        }
        if (a.hi > b.hi) {
            return true;
        }
        var x = a.lo >>> 1;
        var y = b.lo >>> 1;
        if (x !== y) {
            return x >= y;
        }
        return (a.lo & 1) >= (b.lo & 1);
    }

    Long_lt = function(a, b) {
        if (a.hi > b.hi) {
            return false;
        }
        if (a.hi < b.hi) {
            return true;
        }
        var x = a.lo >>> 1;
        var y = b.lo >>> 1;
        if (x !== y) {
            return x < y;
        }
        return (a.lo & 1) < (b.lo & 1);
    }

    Long_le = function(a, b) {
        if (a.hi > b.hi) {
            return false;
        }
        if (a.hi < b.hi) {
            return true;
        }
        var x = a.lo >>> 1;
        var y = b.lo >>> 1;
        if (x !== y) {
            return x <= y;
        }
        return (a.lo & 1) <= (b.lo & 1);
    }

    Long_add = function(a, b) {
        if (a.hi === (a.lo >> 31) && b.hi === (b.lo >> 31)) {
            return Long_fromNumber(a.lo + b.lo);
        } else if (Math.abs(a.hi) < Long_MAX_NORMAL && Math.abs(b.hi) < Long_MAX_NORMAL) {
            return Long_fromNumber(Long_toNumber(a) + Long_toNumber(b));
        }
        var a_lolo = a.lo & 0xFFFF;
        var a_lohi = a.lo >>> 16;
        var a_hilo = a.hi & 0xFFFF;
        var a_hihi = a.hi >>> 16;
        var b_lolo = b.lo & 0xFFFF;
        var b_lohi = b.lo >>> 16;
        var b_hilo = b.hi & 0xFFFF;
        var b_hihi = b.hi >>> 16;

        var lolo = (a_lolo + b_lolo) | 0;
        var lohi = (a_lohi + b_lohi + (lolo >> 16)) | 0;
        var hilo = (a_hilo + b_hilo + (lohi >> 16)) | 0;
        var hihi = (a_hihi + b_hihi + (hilo >> 16)) | 0;
        return new Long((lolo & 0xFFFF) | ((lohi & 0xFFFF) << 16), (hilo & 0xFFFF) | ((hihi & 0xFFFF) << 16));
    }

    Long_inc = function(a) {
        var lo = (a.lo + 1) | 0;
        var hi = a.hi;
        if (lo === 0) {
            hi = (hi + 1) | 0;
        }
        return new Long(lo, hi);
    }

    Long_dec = function(a) {
        var lo = (a.lo - 1) | 0;
        var hi = a.hi;
        if (lo === -1) {
            hi = (hi - 1) | 0;
        }
        return new Long(lo, hi);
    }

    Long_neg = function(a) {
        return Long_inc(new Long(a.lo ^ 0xFFFFFFFF, a.hi ^ 0xFFFFFFFF));
    }

    Long_sub = function(a, b) {
        if (a.hi === (a.lo >> 31) && b.hi === (b.lo >> 31)) {
            return Long_fromNumber(a.lo - b.lo);
        }
        var a_lolo = a.lo & 0xFFFF;
        var a_lohi = a.lo >>> 16;
        var a_hilo = a.hi & 0xFFFF;
        var a_hihi = a.hi >>> 16;
        var b_lolo = b.lo & 0xFFFF;
        var b_lohi = b.lo >>> 16;
        var b_hilo = b.hi & 0xFFFF;
        var b_hihi = b.hi >>> 16;

        var lolo = (a_lolo - b_lolo) | 0;
        var lohi = (a_lohi - b_lohi + (lolo >> 16)) | 0;
        var hilo = (a_hilo - b_hilo + (lohi >> 16)) | 0;
        var hihi = (a_hihi - b_hihi + (hilo >> 16)) | 0;
        return new Long((lolo & 0xFFFF) | ((lohi & 0xFFFF) << 16), (hilo & 0xFFFF) | ((hihi & 0xFFFF) << 16));
    }

    Long_compare = function(a, b) {
        var r = a.hi - b.hi;
        if (r !== 0) {
            return r;
        }
        r = (a.lo >>> 1) - (b.lo >>> 1);
        if (r !== 0) {
            return r;
        }
        return (a.lo & 1) - (b.lo & 1);
    }

    Long_ucompare = function(a, b) {
        var r = $rt_ucmp(a.hi, b.hi);
        if (r !== 0) {
            return r;
        }
        r = (a.lo >>> 1) - (b.lo >>> 1);
        if (r !== 0) {
            return r;
        }
        return (a.lo & 1) - (b.lo & 1);
    }

    Long_mul = function(a, b) {
        var positive = Long_isNegative(a) === Long_isNegative(b);
        if (Long_isNegative(a)) {
            a = Long_neg(a);
        }
        if (Long_isNegative(b)) {
            b = Long_neg(b);
        }
        var a_lolo = a.lo & 0xFFFF;
        var a_lohi = a.lo >>> 16;
        var a_hilo = a.hi & 0xFFFF;
        var a_hihi = a.hi >>> 16;
        var b_lolo = b.lo & 0xFFFF;
        var b_lohi = b.lo >>> 16;
        var b_hilo = b.hi & 0xFFFF;
        var b_hihi = b.hi >>> 16;

        var lolo = 0;
        var lohi = 0;
        var hilo = 0;
        var hihi = 0;
        lolo = (a_lolo * b_lolo) | 0;
        lohi = lolo >>> 16;
        lohi = ((lohi & 0xFFFF) + a_lohi * b_lolo) | 0;
        hilo = (hilo + (lohi >>> 16)) | 0;
        lohi = ((lohi & 0xFFFF) + a_lolo * b_lohi) | 0;
        hilo = (hilo + (lohi >>> 16)) | 0;
        hihi = hilo >>> 16;
        hilo = ((hilo & 0xFFFF) + a_hilo * b_lolo) | 0;
        hihi = (hihi + (hilo >>> 16)) | 0;
        hilo = ((hilo & 0xFFFF) + a_lohi * b_lohi) | 0;
        hihi = (hihi + (hilo >>> 16)) | 0;
        hilo = ((hilo & 0xFFFF) + a_lolo * b_hilo) | 0;
        hihi = (hihi + (hilo >>> 16)) | 0;
        hihi = (hihi + a_hihi * b_lolo + a_hilo * b_lohi + a_lohi * b_hilo + a_lolo * b_hihi) | 0;
        var result = new Long((lolo & 0xFFFF) | (lohi << 16), (hilo & 0xFFFF) | (hihi << 16));
        return positive ? result : Long_neg(result);
    }

    Long_div = function(a, b) {
        if (Math.abs(a.hi) < Long_MAX_NORMAL && Math.abs(b.hi) < Long_MAX_NORMAL) {
            return Long_fromNumber(Long_toNumber(a) / Long_toNumber(b));
        }
        return Long_divRem(a, b)[0];
    }

    Long_udiv = function(a, b) {
        if (a.hi >= 0 && a.hi < Long_MAX_NORMAL && b.hi >= 0 && b.hi < Long_MAX_NORMAL) {
            return Long_fromNumber(Long_toNumber(a) / Long_toNumber(b));
        }
        return Long_udivRem(a, b)[0];
    }

    Long_rem = function(a, b) {
        if (Math.abs(a.hi) < Long_MAX_NORMAL && Math.abs(b.hi) < Long_MAX_NORMAL) {
            return Long_fromNumber(Long_toNumber(a) % Long_toNumber(b));
        }
        return Long_divRem(a, b)[1];
    }

    Long_urem = function(a, b) {
        if (a.hi >= 0 && a.hi < Long_MAX_NORMAL && b.hi >= 0 && b.hi < Long_MAX_NORMAL) {
            return Long_fromNumber(Long_toNumber(a) / Long_toNumber(b));
        }
        return Long_udivRem(a, b)[1];
    }

    Long_divRem = function(a, b) {
        if (b.lo === 0 && b.hi === 0) {
            throw new Error("Division by zero");
        }
        var positive = Long_isNegative(a) === Long_isNegative(b);
        if (Long_isNegative(a)) {
            a = Long_neg(a);
        }
        if (Long_isNegative(b)) {
            b = Long_neg(b);
        }
        a = new LongInt(a.lo, a.hi, 0);
        b = new LongInt(b.lo, b.hi, 0);
        var q = LongInt_div(a, b);
        a = new Long(a.lo, a.hi);
        q = new Long(q.lo, q.hi);
        return positive ? [q, a] : [Long_neg(q), Long_neg(a)];
    };

    Long_udivRem = function(a, b) {
        if (b.lo === 0 && b.hi === 0) {
            throw new Error("Division by zero");
        }
        a = new LongInt(a.lo, a.hi, 0);
        b = new LongInt(b.lo, b.hi, 0);
        var q = LongInt_div(a, b);
        a = new Long(a.lo, a.hi);
        q = new Long(q.lo, q.hi);
        return [q, a];
    };

    Long_shiftLeft16 = function(a) {
        return new Long(a.lo << 16, (a.lo >>> 16) | (a.hi << 16));
    };

    Long_shiftRight16 = function(a) {
        return new Long((a.lo >>> 16) | (a.hi << 16), a.hi >>> 16);
    };

    Long_and = function(a, b) {
        return new Long(a.lo & b.lo, a.hi & b.hi);
    }

    Long_or = function(a, b) {
        return new Long(a.lo | b.lo, a.hi | b.hi);
    }

    Long_xor = function(a, b) {
        return new Long(a.lo ^ b.lo, a.hi ^ b.hi);
    }

    Long_shl = function(a, b) {
        b &= 63;
        if (b === 0) {
            return a;
        } else if (b < 32) {
            return new Long(a.lo << b, (a.lo >>> (32 - b)) | (a.hi << b));
        } else if (b === 32) {
            return new Long(0, a.lo);
        } else {
            return new Long(0, a.lo << (b - 32));
        }
    }

    Long_shr = function(a, b) {
        b &= 63;
        if (b === 0) {
            return a;
        } else if (b < 32) {
            return new Long((a.lo >>> b) | (a.hi << (32 - b)), a.hi >> b);
        } else if (b === 32) {
            return new Long(a.hi, a.hi >> 31);
        } else {
            return new Long((a.hi >> (b - 32)), a.hi >> 31);
        }
    }

    Long_shru = function(a, b) {
        b &= 63;
        if (b === 0) {
            return a;
        } else if (b < 32) {
            return new Long((a.lo >>> b) | (a.hi << (32 - b)), a.hi >>> b);
        } else if (b === 32) {
            return new Long(a.hi, 0);
        } else {
            return new Long((a.hi >>> (b - 32)), 0);
        }
    }

    Long_not = function(a) {
        return new Long(~a.hi, ~a.lo);
    }

    // Represents a mutable 80-bit unsigned integer
    LongInt = function(lo, hi, sup) {
        this.lo = lo;
        this.hi = hi;
        this.sup = sup;
    };

    LongInt_mul = function(a, b) {
        var a_lolo = ((a.lo & 0xFFFF) * b) | 0;
        var a_lohi = ((a.lo >>> 16) * b) | 0;
        var a_hilo = ((a.hi & 0xFFFF) * b) | 0;
        var a_hihi = ((a.hi >>> 16) * b) | 0;
        var sup = (a.sup * b) | 0;

        a_lohi = (a_lohi + (a_lolo >>> 16)) | 0;
        a_hilo = (a_hilo + (a_lohi >>> 16)) | 0;
        a_hihi = (a_hihi + (a_hilo >>> 16)) | 0;
        sup = (sup + (a_hihi >>> 16)) | 0;
        a.lo = (a_lolo & 0xFFFF) | (a_lohi << 16);
        a.hi = (a_hilo & 0xFFFF) | (a_hihi << 16);
        a.sup = sup & 0xFFFF;
    };

    LongInt_sub = function(a, b) {
        var a_lolo = a.lo & 0xFFFF;
        var a_lohi = a.lo >>> 16;
        var a_hilo = a.hi & 0xFFFF;
        var a_hihi = a.hi >>> 16;
        var b_lolo = b.lo & 0xFFFF;
        var b_lohi = b.lo >>> 16;
        var b_hilo = b.hi & 0xFFFF;
        var b_hihi = b.hi >>> 16;

        a_lolo = (a_lolo - b_lolo) | 0;
        a_lohi = (a_lohi - b_lohi + (a_lolo >> 16)) | 0;
        a_hilo = (a_hilo - b_hilo + (a_lohi >> 16)) | 0;
        a_hihi = (a_hihi - b_hihi + (a_hilo >> 16)) | 0;
        var sup = (a.sup - b.sup + (a_hihi >> 16)) | 0;
        a.lo = (a_lolo & 0xFFFF) | (a_lohi << 16);
        a.hi = (a_hilo & 0xFFFF) | (a_hihi << 16);
        a.sup = sup;
    };

    LongInt_add = function(a, b) {
        var a_lolo = a.lo & 0xFFFF;
        var a_lohi = a.lo >>> 16;
        var a_hilo = a.hi & 0xFFFF;
        var a_hihi = a.hi >>> 16;
        var b_lolo = b.lo & 0xFFFF;
        var b_lohi = b.lo >>> 16;
        var b_hilo = b.hi & 0xFFFF;
        var b_hihi = b.hi >>> 16;

        a_lolo = (a_lolo + b_lolo) | 0;
        a_lohi = (a_lohi + b_lohi + (a_lolo >> 16)) | 0;
        a_hilo = (a_hilo + b_hilo + (a_lohi >> 16)) | 0;
        a_hihi = (a_hihi + b_hihi + (a_hilo >> 16)) | 0;
        var sup = (a.sup + b.sup + (a_hihi >> 16)) | 0;
        a.lo = (a_lolo & 0xFFFF) | (a_lohi << 16);
        a.hi = (a_hilo & 0xFFFF) | (a_hihi << 16);
        a.sup = sup;
    };

    LongInt_inc = function(a) {
        a.lo = (a.lo + 1) | 0;
        if (a.lo === 0) {
            a.hi = (a.hi + 1) | 0;
            if (a.hi === 0) {
                a.sup = (a.sup + 1) & 0xFFFF;
            }
        }
    };

    LongInt_dec = function(a) {
        a.lo = (a.lo - 1) | 0;
        if (a.lo === -1) {
            a.hi = (a.hi - 1) | 0;
            if (a.hi === -1) {
                a.sup = (a.sup - 1) & 0xFFFF;
            }
        }
    };

    LongInt_ucompare = function(a, b) {
        var r = (a.sup - b.sup);
        if (r !== 0) {
            return r;
        }
        r = (a.hi >>> 1) - (b.hi >>> 1);
        if (r !== 0) {
            return r;
        }
        r = (a.hi & 1) - (b.hi & 1);
        if (r !== 0) {
            return r;
        }
        r = (a.lo >>> 1) - (b.lo >>> 1);
        if (r !== 0) {
            return r;
        }
        return (a.lo & 1) - (b.lo & 1);
    };

    LongInt_numOfLeadingZeroBits = function(a) {
        var n = 0;
        var d = 16;
        while (d > 0) {
            if ((a >>> d) !== 0) {
                a >>>= d;
                n = (n + d) | 0;
            }
            d = (d / 2) | 0;
        }
        return 31 - n;
    };

    LongInt_shl = function(a, b) {
        if (b === 0) {
            return;
        }
        if (b < 32) {
            a.sup = ((a.hi >>> (32 - b)) | (a.sup << b)) & 0xFFFF;
            a.hi = (a.lo >>> (32 - b)) | (a.hi << b);
            a.lo <<= b;
        } else if (b === 32) {
            a.sup = a.hi & 0xFFFF;
            a.hi = a.lo;
            a.lo = 0;
        } else if (b < 64) {
            a.sup = ((a.lo >>> (64 - b)) | (a.hi << (b - 32))) & 0xFFFF;
            a.hi = a.lo << b;
            a.lo = 0;
        } else if (b === 64) {
            a.sup = a.lo & 0xFFFF;
            a.hi = 0;
            a.lo = 0;
        } else {
            a.sup = (a.lo << (b - 64)) & 0xFFFF;
            a.hi = 0;
            a.lo = 0;
        }
    };

    LongInt_shr = function(a, b) {
        if (b === 0) {
            return;
        }
        if (b === 32) {
            a.lo = a.hi;
            a.hi = a.sup;
            a.sup = 0;
        } else if (b < 32) {
            a.lo = (a.lo >>> b) | (a.hi << (32 - b));
            a.hi = (a.hi >>> b) | (a.sup << (32 - b));
            a.sup >>>= b;
        } else if (b === 64) {
            a.lo = a.sup;
            a.hi = 0;
            a.sup = 0;
        } else if (b < 64) {
            a.lo = (a.hi >>> (b - 32)) | (a.sup << (64 - b));
            a.hi = a.sup >>> (b - 32);
            a.sup = 0;
        } else {
            a.lo = a.sup >>> (b - 64);
            a.hi = 0;
            a.sup = 0;
        }
    };

    LongInt_copy = function(a) {
        return new LongInt(a.lo, a.hi, a.sup);
    };

    LongInt_div = function(a, b) {
        // Normalize divisor
        var bits = b.hi !== 0 ? LongInt_numOfLeadingZeroBits(b.hi) : LongInt_numOfLeadingZeroBits(b.lo) + 32;
        var sz = 1 + ((bits / 16) | 0);
        var dividentBits = bits % 16;
        LongInt_shl(b, bits);
        LongInt_shl(a, dividentBits);
        var q = new LongInt(0, 0, 0);
        while (sz-- > 0) {
            LongInt_shl(q, 16);
            // Calculate approximate q
            var digitA = (a.hi >>> 16) + (0x10000 * a.sup);
            var digitB = b.hi >>> 16;
            var digit = (digitA / digitB) | 0;
            var t = LongInt_copy(b);
            LongInt_mul(t, digit);
            // Adjust q either down or up
            if (LongInt_ucompare(t, a) >= 0) {
                while (LongInt_ucompare(t, a) > 0) {
                    LongInt_sub(t, b);
                    --digit;
                }
            } else {
                while (true) {
                    var nextT = LongInt_copy(t);
                    LongInt_add(nextT, b);
                    if (LongInt_ucompare(nextT, a) > 0) {
                        break;
                    }
                    t = nextT;
                    ++digit;
                }
            }
            LongInt_sub(a, t);
            q.lo |= digit;
            LongInt_shl(a, 16);
        }
        LongInt_shr(a, bits + 16);
        return q;
    };
} else {
    Long_eq = function(a, b) {
        return a === b;
    }

    Long_ne = function(a, b) {
        return a !== b;
    }

    Long_gt = function(a, b) {
        return a > b;
    }

    Long_ge = function(a, b) {
        return a >= b;
    }

    Long_lt = function(a, b) {
        return a < b;
    }

    Long_le = function(a, b) {
        return a <= b;
    }

    Long_add = function(a, b) {
        return BigInt.asIntN(64, a + b);
    }

    Long_inc = function(a) {
        return BigInt.asIntN(64, a + 1);
    }

    Long_dec = function(a) {
        return BigInt.asIntN(64, a - 1);
    }

    Long_neg = function(a) {
        return BigInt.asIntN(64, -a);
    }

    Long_sub = function(a, b) {
        return BigInt.asIntN(64, a - b);
    }

    Long_compare = function(a, b) {
        return a < b ? -1 : a > b ? 1 : 0;
    }
    Long_ucompare = function(a, b) {
        a = BigInt.asUintN(64, a);
        b = BigInt.asUintN(64, b);
        return a < b ? -1 : a > b ? 1 : 0;
    }

    Long_mul = function(a, b) {
        return BigInt.asIntN(64, a * b);
    }

    Long_div = function(a, b) {
        return BigInt.asIntN(64, a / b);
    }

    Long_udiv = function(a, b) {
        return BigInt.asIntN(64, BigInt.asUintN(64, a) / BigInt.asUintN(64, b));
    }

    Long_rem = function(a, b) {
        return BigInt.asIntN(64, a % b);
    }

    Long_urem = function(a, b) {
        return BigInt.asIntN(64, BigInt.asUintN(64, a) % BigInt.asUintN(64, b));
    }

    Long_and = function(a, b) {
        return BigInt.asIntN(64, a & b);
    }

    Long_or = function(a, b) {
        return BigInt.asIntN(64, a | b);
    }

    Long_xor = function(a, b) {
        return BigInt.asIntN(64, a ^ b);
    }

    Long_shl = function(a, b) {
        return BigInt.asIntN(64, a << BigInt(b & 63));
    }

    Long_shr = function(a, b) {
        return BigInt.asIntN(64, a >> BigInt(b & 63));
    }

    Long_shru = function(a, b) {
        return BigInt.asIntN(64, BigInt.asUintN(64, a) >> BigInt(b & 63));
    }

    Long_not = function(a) {
        return BigInt.asIntN(64, ~a);
    }
}
