/**
 * SeekQuarry/Yioop --
 * Open Source Pure PHP Search Engine, Crawler, and Indexer
 *
 * Copyright (C) 2009 - 2026    Chris Pollett chris@pollett.org
 *
 * LICENSE:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.    If not, see <https://www.gnu.org/licenses/>.
 *
 * END LICENSE
 *
 * @author Akash Patel (edited by Chris Pollett chris@pollett.org)
 *     Ideas adapted Leemon Baird's bigint.js
 * @license https://www.gnu.org/licenses/ GPL3
 * @link https://www.seekquarry.com/
 * @copyright 2009 - 2026
 * @filesource
 */
/*
 * Constant for number of bits per element
 */
var bits_per_element = 15;
/*
 * Constant to mask the overflow value
 */
var mask = 32767;
/*
 * Constant to define radix
 */
var radix = mask + 1;
/*
 * To add BigInt and int number
 *
 * @param BigInt x first operand
 * @param int n second operand
 * @return BigInt result
 */
function addInteger(x, n)
{
    var new_x = expandBigInt(x, x.length + 1);
    addBigIntToInt(new_x, n);
    return trimBigInt(new_x, 1);
}
/*
 * Returns the BigInt with given number of leading zeroes
 *
 * @param BigInt x input number
 * @param int k expected number of leading zeroes
 * @return BigInt result
 */
function trimBigInt(x, k)
{
    var i = x.length;
    while(i > 0 && !x[i - 1]) {
        i--;
    }
    var y = new Array(i + k);
    copyBigIntFromBigInt(y, x);
    return y;
}
/*
 * Expands a BigInt to at least an n element array,
 * adding zeroes if needed
 *
 * @param BigInt x first operand
 * @param int n expected number of elements
 * @return BigInt result
 */
function expandBigInt(x, n)
{
    var bits = (x.length > n ? x.length : n) * bits_per_element;
    var result = int2BigInt(0, bits, 0);
    copyBigIntFromBigInt(result, x);
    return result;
}
/*
 * Converts a normal int to BigInt. It pads the array with leading zeros so
 * that it has at least minSize elements
 *
 * @param int t input number
 * @param int bits expected number of bits
 * @param int min_size minimum size of the BigInt.
 * @return Array stores the BigInt in bits_per_element-bit chunks,
 *    little endian
 */
function int2BigInt(t, bits, min_size)
{
    var size_of_array = Math.max(
        Math.ceil(bits / bits_per_element) + 1, min_size);
    var buffer = new Array(size_of_array);
    copyBigIntFromInt(buffer, t);
    return buffer;
}
/*
 * Copies one BigInt to another BigInt
 * x must be an array at least as big as y
 *
 * @param BigInt x input number
 * @param BigInt bits expected number of bits
 * @return BigInt result
 */
function copyBigIntFromBigInt(x, y)
{
    var len = (x.length < y.length) ? x.length : y.length;
    for (var i = 0; i < len; i++) {
        x[i] = y[i];
    }
    for (var i = len; i < x.length ; i++) {
        x[i] = 0;
    }
}
/*
 * Makes a Big Integer out of the supplied int
 *
 * @param BigInt x input number
 * @param int n input number
 * @return BigInt result
 */
function copyBigIntFromInt(x, n)
{
    var c = n;
    for (var i = 0; i < x.length; i++) {
        x[i] = c & mask;
        c >>= bits_per_element;
    }
}
/*
 * To perform x = x + n where x is a BigInt and n is an integer
 *
 * @param BigInt x input number
 * @param int n input number
 * @return BigInt result of the summation
 */
function addBigIntToInt(x, n)
{
    var i, c, b;
    x[0] += n;
    c = 0;
    for (i = 0; i < x.length; i++) {
        c += x[i];
        b = 0;
        if (c < 0) {
            b =- (c >> bits_per_element);
            c += b * radix;
        }
        x[i] = c & mask;
        c = (c >> bits_per_element) - b;
        if (!c) {
            return;
        }
    }
}
/*
 * Converts a string into a BigInt. It pads the array with leading zeros
 * so that it has at least min_size elements.
 * The array will always have at least one leading zero, unless base=-1
 * If base is less than 36 we use the digit_str below to convert (say for hex
 * or decimal numbers). Otherwise, we assume the base is 256 and just use
 * charCode
 *
 * @param String s input string
 * @param int base base of the output number
 * @param int min_size minimum size of the BigInt
 * @return BigInt
 */
function str2BigInt(s, base, min_size)
{
    var d, i, j, x, y, kk;
    var digits_str =
        '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    var k = s.length;
    var x = int2BigInt(0, base * k, 0);

    for (i = 0; i < k; i++) {
        d = (base > 36) ? s.charCodeAt(i) :
            digits_str.indexOf(s.substring(i, i + 1), 0);
        if (base <= 36 && d >= 36) {
            d -= 26; //lower to upper
        }
        if (d >= base || d < 0) {
            break;
        }
        multInt(x, base);
        addBigIntToInt(x, d);
    }
    k = x.length;
    while(k > 0 && !x[k - 1]) {
         k--;
    }
    k = min_size > k + 1 ? min_size : k + 1;
    y = new Array(k);
    kk = k < x.length ? k : x.length;
    for (i = 0; i < kk; i++){
        y[i] = x[i];
    }
    // copy rest as 0
    for (j = i; j < k; j++) {
        y[j] = 0;
    }
    return y;
}
/*
 * Converts a BigInt into a string in a given base, from base 2 up to base 95
 *
 * @param BigInt x input number
 * @param int base base of the output number
 * @return String result
 */
function bigInt2Str(x, base)
{
    var i = "";
    var t = "";
    var s = "";
    var digits_str =
        '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    var temp_array = new Array();
    if (temp_array.length != x.length){
        temp_array = dup(x);
    } else {
        copyBigIntFromBigInt(temp_array, x);
    }
    while (!isZero(temp_array)) {
            t = divInt(temp_array, base);    //t=s % base; s=floor(s/base);
            s = digits_str.substring(t, t + 1) + s;
        }
     if (s.length == 0){
        s = "0";
     }
    return s;
}
/*
 * Makes a copy of a BigInt
 *
 * @param BigInt x input number
 * @return BigInt buffer copy of the BigInt x
 */
function dup(x)
{
    var buffer = new Array(x.length);
    copyBigIntFromBigInt(buffer, x);
    return buffer;
}
/*
 * Checks whether BigInt is zero or not.
 * Returns 0 if the BigInt is zero otherwise return 1
 * @param BigInt x input number
 * @return int
 */
function isZero(x) {
    for (var i = 0; i < x.length; i++){
        if (x[i]) {
            return 0;
        }
    }
    return 1;
}
/*
 * Computes x = floor(x/n) for BigInt x and integer n, and
 *
 * @param BigInt x numerator
 * @param int n denomenator
 * @return int r reminder
 */
function divInt(x, n)
{
    var r = 0;
    var s;
    for (var i = x.length - 1; i >= 0; i--) {
        s = r*radix + x[i];
        x[i] = Math.floor(s/n);
        r = s % n;
    }
    return r;
}
/*
 * Multiplies BigInt with Int.
 *
 * @param BigInt x input number
 * @param int n input number
 */
function multInt(x, n)
{
    if (n == 0){
        return;
    }
    var i, carry, borrow;
    carry = 0;
    for (i = 0; i < x.length ; i++) {
        carry += x[i] * n;
        borrow = 0;
        if (carry < 0) {
            borrow =- (carry >> bits_per_element);
            carry += borrow * radix;
        }
        x[i] = carry & mask;
        carry = (carry >> bits_per_element) - borrow;
    }
}
/*
 * Performs x * y on BigInt's.
 *
 * @param BigInt x input number
 * @param BigInt y input number
 * @return BigInt ans result of multiplication
 */
function mult(x, y)
{

    var ans = expandBigInt(x, x.length + y.length);
    multEquals(ans, y);
    return trimBigInt(ans, 1);
}
/*
 * Performs x *= y (so the result is x)
 *
 * @param BigInt x input number
 * @param BigInt y input number
 */
function multEquals(x, y)
{
    var result = new Array(2 * x.length);
    copyBigIntFromInt(result, 0);
    for (var i = 0; i< y.length; i++) {
        if (y[i]) {
            linearCombShift(result, x, y[i], i);
        }
    }
    copyBigIntFromBigInt(x, result);
}
/*
 * Computes x += y*b*d^{ys}, where d is our base.
 * This corresponds to one row of table needed to compute x*y
 *
 * @param BigInt x to store the result
 * @param BigInt y input number
 * @param integer b digit position in the second number
 * @param integer ys to get bit shift operator
 */
function linearCombShift(x, y, b, ys)
{
    var i, c, k;
    k = x.length < ys + y.length ? x.length : ys + y.length;
    for (c = 0, i = ys; i < k; i++) {
        c += x[i] + b * y[i - ys];
        x[i] = c & mask;
        c >>= bits_per_element;
    }
    for (i = k; c && i < x.length; i++) {
        c  += x[i];
        x[i] = c & mask;
        c >>= bits_per_element;
    }
}
/*
 * Performs BigInt divide operation
 *
 * @param BigInt x Dividend
 * @param BigInt y Divisor
 * @param BigInt q to store the quotient
 * @param BigInt r to store the reminder
 */
function divide(x, y, q, r)
{
    var kx, ky;
    var i, j, y1, y2, c, a, b;
    var tmp, out_check;
    copyBigIntFromBigInt(r, x);
    ky = y.length;
    while(y[ky - 1] == 0) { // find first non-zero position
        ky--;
    }
    b = y[ky - 1];
    a = 0;
    while(b > 0) {
        b >>= 1;
        a++;
    }
    a = bits_per_element - a;
    leftShift(y, a);
    leftShift(r, a);
    kx = r.length;
    while(r[kx - 1] == 0 && kx > ky) { // find first non-zero position
        kx--;
    }
    copyBigIntFromInt(q, 0, x.length);
    while (!greaterShift(y, r, kx - ky)) {
        subShift(r, y, kx-ky);
        q[kx - ky]++;
    }
    for (i = kx - 1; i >= ky; i--) {
        if (r[i] == y[ky-1]) {
            q[i - ky] = mask;
        } else {
            q[i - ky] = Math.floor((r[i] * radix + r[i-1]) / y[ky-1]);
        }
        while(true) {
            y2 = (ky > 1 ? y[ky - 2] : 0) * q[i-ky];
            c = y2 >> bits_per_element;
            y2 = y2 & mask;
            y1 =c + q[i-ky] * y[ky-1];
            c = y1 >> bits_per_element;
            y1 = y1 & mask;
            if (c == r[i]) {
                if (y1 == r[i - 1]) {
                    tmp = (i > 1) ? r[i - 2] : 0
                    out_check = (y2 > tmp);
                } else {
                    out_check = (y1 > r[i - 1]);
                }
            } else {
                out_check = (c > r[i]);
            }
            if (out_check) {
                q[i - ky]--;
            } else {
                break;
            }
        }
        linearCombShift(r, y, -q[i - ky], i - ky);
        if (negative(r)) {
            addShift(r, y, i - ky);
            q[i-ky]--;
        }
    }
    rightShift(y, a);
    rightShift(r, a);
}

/*
 * Performs left shift operation on BigInt by given number of bits
 *
 * @param BigInt x input number
 * @param integer n number of bits to be shifted
 */
function leftShift(x, n)
{
    var i;
    var len = Math.floor(n / bits_per_element);
    if (len) {
        for (i = x.length; i >= len; i--){
            x[i] = x[i - len];
        }
        for (;i >= 0; i--) {
            x[i] = 0;
        }
        n %= bits_per_element;
    }
    if (!n) { return; }
    for (i = x.length - 1; i > 0; i--) {
        x[i] = mask & ((x[i] << n) | (x[i-1] >> (bits_per_element - n)));
    }
    x[i] = mask & (x[i] << n);
}
/*
 * Performs right shift operation on BigInt by given number of bits
 * @param BigInt x input number
 * @param integer n number of bits to be shifted
 */
function rightShift(x, n)
{
    var i;
    var len = Math.floor(n / bits_per_element);
    if (len) {
        for (i = 0; i< x.length - len; i++) {
            x[i] = x[i + len];
        }
        for (; i< x.length; i++){
            x[i] = 0;
        }
        n %= bits_per_element;
    }
    for (i = 0; i < x.length - 1; i++) {
        x[i] = mask & ((x[i + 1] <<(bits_per_element - n)) | (x[i] >> n));
    }
    x[i] >>= n;
}
/*
 * Used to check whether BigInt is negative or not
 *
 * @return integer output 1 if it is negative otherwise 0
 */
function negative(x)
{
    var result = (x[x.length - 1] >> (bits_per_element - 1)) & 1;
    return result;
}
/*
 * Used to perform a shift operation on y and add it to the x
 *
 * @param BigInt x first input number
 * @param BigInt y second input number
 * @return integer output 1 if it is negative otherwise 0
 */
function addShift(x, y, ys)
{
    var i, sum;
    len = x.length < ys + y.length ? x.length : ys + y.length;
    for (i = ys; i < len; i++) {
        sum += x[i] + y[i-ys];
        x[i] = sum & mask;
        sum >>= bits_per_element;
    }
    for (i = len; sum && i < x.length; i++) {
        sum += x[i];
        x[i] = sum & mask;
        sum >>= bits_per_element;
    }
}
/*
 * Right shifts the x by given number of bits and check
 * whether it is greater than y
 *
 * @param BigInt x nonnegative input number
 * @param BigInt y nonnegative input number
 * @param integer shift nonnegative integer
 * @return integer output 1 if the check passes otherwise returns 0
 */
function greaterShift(x, y, shift)
{
    var i;
    var len = ((x.length + shift) < y.length) ? (x.length + shift):y.length;
    for (i = y.length - 1 - shift; i < x.length && i >= 0; i++)
    {
        if (x[i] > 0) {
            return 1;
        }
    }
    for (i = x.length - 1 + shift; i < y.length; i++)
    {
        if (y[i] > 0) {
            return 0;
        }
    }
    for (i = len - 1; i >= shift; i--)
    {
        if (x[i-shift] > y[i]) {
            return 1;
        } else if (x[i-shift] < y[i]) {
            return 0;
        }
 }
    return 0;
}
/*
 * Left shift y by given number of bits and performs
 * subtraction operation. The result is stored in x
 *
 * @param BigInt x BigInt number
 * @param BigInt y BigInt number
 * @param integer shift number of shift bits
 */
function subShift(x, y, ys)
{
    var i, sum;
    var len = x.length < ys + y.length ? x.length : ys + y.length;
    for (i = ys; i < len; i++) {
        sum += x[i] - y[i - ys];
        x[i] = sum & mask;
        sum >>= bits_per_element;
    }
    for (i = len; sum && i < x.length; i++) {
        sum += x[i];
        x[i] = sum & mask;
        sum >>= bits_per_element;
    }
}
/*
 * Computes x mod n
 *
 * @param BigInt x argument to modr
 * @param integer n modulus
 * @return result which equals x mod n
 */
function bigMod(x, n)
{
    var ans = dup(x);
    modCalculation(ans, n);
    var result = trim(ans, 1);
    return result;
}
/*
 * Computes x mod n with the result stored in x
 *
 * @param BigInt x argument to mod
 * @param BigInt n modulus
 */
function modCalculation(x, n)
{
    var dividend = new Array(0);
    var divisor = new Array(0);
    if (dividend.length != x.length) {
        dividend = dup(x);
    } else {
        copyBigIntFromBigInt(dividend, x);
    }
    if (divisor.length != x.length) {
        divisor = dup(x);
    }
    divide(dividend, n, divisor, x);
}
/*
 * Returns x with exactly k leading zeroes
 *
 * @param BigInt x BigInt number
 * @param integer k expected number of leading zeroes in x
 * @return result result=x mod n
 */
function trim(x, k)
{
    var i, y;
    for (i= x.length; i > 0 && !x[i-1]; i--);
    y = new Array(i + k);
    copyBigIntFromBigInt(y, x);
    return y;
}
/*
 * Computes modulus x*y mod n using BigInt's
 *
 * @param BigInt x first parameter in above expression
 * @param BigInt y second parameter in above expression
 * @param BigInt n modulus
 * @return BigInt x * y mod n
 */
function multMod(x, y, n)
{
    var result = expand(x, n.length);
    multModOperation(result, y, n);
    return trim(result, 1);
}
/*
 * Computes modulus x*y mod n using BigInt's stores the result in x potential
 * leaving  the output untrimmed (this is an auxiliary method for multMod)
 *
 * @param BigInt x first parameter in above expression
 * @param BigInt y second parameter in above expression
 * @param BigInt n modulus
 * @return BigInt x * y mod n
 */
 function multModOperation(x, y, n)
 {
    var input_number = new Array(2 * x.length);
    copyBigIntFromInt(input_number, 0);
    for (var i = 0; i < y.length; i++){
        if (y[i]) {
            linearCombShift(input_number, x, y[i], i);
        }
    }
    modCalculation(input_number, n);
    copyBigIntFromBigInt(x, input_number);
}
/*
 * Expands BigInt to the given number of elements.
 * Leading zeros are added
 *
 * @param BigInt x BigInt number
 * @param integer n expected number of elements
 * @return ans x * y mod n
 */
function expand(x, n)
{
    var ans = int2BigInt(0,
        (x.length > n ? x.length : n) * bits_per_element, 0);
    copyBigIntFromBigInt(ans, x);
    return ans;
}
