releases-comm-central/chat/modules/BigInteger.jsm

690 строки
24 KiB
JavaScript

/*
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* For more information, please refer to <http://unlicense.org>
*/
"use strict";
this.EXPORTED_SYMBOLS = ["bigInt"];
var bigInt = (function () {
var base = 10000000, logBase = 7;
var sign = {
positive: false,
negative: true
};
function BigInteger(value, sign) {
this.value = value;
this.sign = sign;
}
function trim(value) {
var i = value.length - 1;
while (value[i] === 0 && i > 0) i--;
return value.slice(0, i + 1);
}
function fastAdd(a, b) {
var sign = b < 0;
if (a.sign !== sign) {
if(sign) return fastSubtract(a.abs(), -b);
return fastSubtract(a.abs(), b).negate();
}
if (sign) b = -b;
var value = a.value,
result = [],
carry = 0;
for (var i = 0; i < value.length || carry > 0; i++) {
var sum = (value[i] || 0) + (i > 0 ? 0 : b) + carry;
carry = sum >= base ? 1 : 0;
result.push(sum % base);
}
return new BigInteger(result, a.sign);
}
function fastSubtract(a, b) {
if (a.sign !== (b < 0)) return fastAdd(a, -b);
var sign = false;
if (a.sign) sign = true;
var value = a.value;
if (value.length === 1 && value[0] < b) return new BigInteger([b - value[0]], !sign);
if (sign) b = -b;
var result = [],
borrow = 0;
for (var i = 0; i < value.length; i++) {
var tmp = value[i] - borrow - (i > 0 ? 0 : b);
borrow = tmp < 0 ? 1 : 0;
result.push((borrow * base) + tmp);
}
return new BigInteger(result, sign);
}
function fastMultiply(a, b) {
var value = a.value,
sign = b < 0,
result = [],
carry = 0;
if (sign) b = -b;
for (var i = 0; i < value.length || carry > 0; i++) {
var product = (value[i] || 0) * b + carry;
carry = (product / base) | 0;
result.push(product % base);
}
return new BigInteger(result, sign ? !a.sign : a.sign);
}
function fastDivMod(a, b) {
if (b === 0) throw new Error("Cannot divide by zero.");
var value = a.value,
sign = b < 0,
result = [],
remainder = 0;
if (sign) b = -b;
for (var i = value.length - 1; i >= 0; i--) {
var divisor = remainder * base + value[i];
remainder = divisor % b;
result.push(divisor / b | 0);
}
return {
quotient: new BigInteger(trim(result.reverse()), sign ? !a.sign : a.sign),
remainder: new BigInteger([remainder], a.sign)
};
}
function isSmall(n) {
return ((typeof n === "number" || typeof n === "string") && +n <= base) ||
(n instanceof BigInteger && n.value.length <= 1);
}
BigInteger.prototype.negate = function () {
return new BigInteger(this.value, !this.sign);
};
BigInteger.prototype.abs = function () {
return new BigInteger(this.value, sign.positive);
};
BigInteger.prototype.add = function (n) {
if(isSmall(n)) return fastAdd(this, +n);
n = parseInput(n);
if (this.sign !== n.sign) {
if (this.sign === sign.positive) return this.abs().subtract(n.abs());
return n.abs().subtract(this.abs());
}
var a = this.value, b = n.value;
var result = [],
carry = 0,
length = Math.max(a.length, b.length);
for (var i = 0; i < length || carry > 0; i++) {
var sum = (a[i] || 0) + (b[i] || 0) + carry;
carry = sum >= base ? 1 : 0;
result.push(sum % base);
}
return new BigInteger(trim(result), this.sign);
};
BigInteger.prototype.plus = function (n) {
return this.add(n);
};
BigInteger.prototype.subtract = function (n) {
if (isSmall(n)) return fastSubtract(this, +n);
n = parseInput(n);
if (this.sign !== n.sign) return this.add(n.negate());
if (this.sign === sign.negative) return n.negate().subtract(this.negate());
if (this.compare(n) < 0) return n.subtract(this).negate();
var a = this.value, b = n.value;
var result = [],
borrow = 0,
length = Math.max(a.length, b.length);
for (var i = 0; i < length; i++) {
var ai = a[i] || 0, bi = b[i] || 0;
var tmp = ai - borrow;
borrow = tmp < bi ? 1 : 0;
result.push((borrow * base) + tmp - bi);
}
return new BigInteger(trim(result), sign.positive);
};
BigInteger.prototype.minus = function (n) {
return this.subtract(n);
};
BigInteger.prototype.multiply = function (n) {
if (isSmall(n)) return fastMultiply(this, +n);
n = parseInput(n);
var sign = this.sign !== n.sign;
var a = this.value, b = n.value;
var length = Math.max(a.length, b.length);
var resultSum = [];
for (var i = 0; i < length; i++) {
resultSum[i] = [];
var j = i;
while (j--) {
resultSum[i].push(0);
}
}
var carry = 0;
for (var i = 0; i < a.length; i++) {
var x = a[i];
for (var j = 0; j < b.length || carry > 0; j++) {
var y = b[j];
var product = y ? (x * y) + carry : carry;
carry = Math.floor(product / base);
resultSum[i].push(product % base);
}
}
var max = -1;
for (var i = 0; i < resultSum.length; i++) {
var len = resultSum[i].length;
if (len > max) max = len;
}
var result = [], carry = 0;
for (var i = 0; i < max || carry > 0; i++) {
var sum = carry;
for (var j = 0; j < resultSum.length; j++) {
sum += resultSum[j][i] || 0;
}
carry = sum > base ? Math.floor(sum / base) : 0;
sum -= carry * base;
result.push(sum);
}
return new BigInteger(trim(result), sign);
};
BigInteger.prototype.times = function (n) {
return this.multiply(n);
};
BigInteger.prototype.divmod = function (n) {
if (isSmall(n)) return fastDivMod(this, +n);
n = parseInput(n);
var quotientSign = this.sign !== n.sign;
if (this.equals(0)) return {
quotient: new BigInteger([0], sign.positive),
remainder: new BigInteger([0], sign.positive)
};
if (n.equals(0)) throw new Error("Cannot divide by zero");
var a = this.value, b = n.value;
var result = [], remainder = [];
for (var i = a.length - 1; i >= 0; i--) {
var m = [a[i]].concat(remainder);
var quotient = goesInto(b, m);
result.push(quotient.result);
remainder = quotient.remainder;
}
result.reverse();
return {
quotient: new BigInteger(trim(result), quotientSign),
remainder: new BigInteger(trim(remainder), this.sign)
};
};
BigInteger.prototype.divide = function (n) {
return this.divmod(n).quotient;
};
BigInteger.prototype.over = function (n) {
return this.divide(n);
};
BigInteger.prototype.mod = function (n) {
return this.divmod(n).remainder;
};
BigInteger.prototype.remainder = function (n) {
return this.mod(n);
};
BigInteger.prototype.pow = function (n) {
n = parseInput(n);
var a = this, b = n, r = ONE;
if (b.equals(ZERO)) return r;
if (a.equals(ZERO) || b.lesser(ZERO)) return ZERO;
while (true) {
if (b.isOdd()) {
r = r.times(a);
}
b = b.divide(2);
if (b.equals(ZERO)) break;
a = a.times(a);
}
return r;
};
BigInteger.prototype.modPow = function (exp, mod) {
exp = parseInput(exp);
mod = parseInput(mod);
if (mod.equals(ZERO)) throw new Error("Cannot take modPow with modulus 0");
var r = ONE,
base = this.mod(mod);
if (base.equals(ZERO)) return ZERO;
while (exp.greater(0)) {
if (exp.isOdd()) r = r.multiply(base).mod(mod);
exp = exp.divide(2);
base = base.square().mod(mod);
}
return r;
};
BigInteger.prototype.square = function () {
return this.multiply(this);
};
function gcd(a, b) {
a = parseInput(a).abs();
b = parseInput(b).abs();
if (a.equals(b)) return a;
if (a.equals(ZERO)) return b;
if (b.equals(ZERO)) return a;
if (a.isEven()) {
if (b.isOdd()) {
return gcd(a.divide(2), b);
}
return gcd(a.divide(2), b.divide(2)).multiply(2);
}
if (b.isEven()) {
return gcd(a, b.divide(2));
}
if (a.greater(b)) {
return gcd(a.subtract(b).divide(2), b);
}
return gcd(b.subtract(a).divide(2), a);
}
function lcm(a, b) {
a = parseInput(a).abs();
b = parseInput(b).abs();
return a.multiply(b).divide(gcd(a, b));
}
BigInteger.prototype.next = function () {
return fastAdd(this, 1);
};
BigInteger.prototype.prev = function () {
return fastSubtract(this, 1);
};
BigInteger.prototype.compare = function (n) {
var first = this, second = parseInput(n);
if (first.value.length === 1 && second.value.length === 1 && first.value[0] === 0 && second.value[0] === 0) return 0;
if (second.sign !== first.sign) return first.sign === sign.positive ? 1 : -1;
var multiplier = first.sign === sign.positive ? 1 : -1;
var a = first.value, b = second.value,
length = Math.max(a.length, b.length) - 1;
for (var i = length; i >= 0; i--) {
var ai = (a[i] || 0), bi = (b[i] || 0);
if (ai > bi) return 1 * multiplier;
if (bi > ai) return -1 * multiplier;
}
return 0;
};
BigInteger.prototype.compareTo = function (n) {
return this.compare(n);
};
BigInteger.prototype.compareAbs = function (n) {
return this.abs().compare(n.abs());
};
BigInteger.prototype.equals = function (n) {
return this.compare(n) === 0;
};
BigInteger.prototype.notEquals = function (n) {
return !this.equals(n);
};
BigInteger.prototype.lesser = function (n) {
return this.compare(n) < 0;
};
BigInteger.prototype.greater = function (n) {
return this.compare(n) > 0;
};
BigInteger.prototype.greaterOrEquals = function (n) {
return this.compare(n) >= 0;
};
BigInteger.prototype.lesserOrEquals = function (n) {
return this.compare(n) <= 0;
};
BigInteger.prototype.lt = BigInteger.prototype.lesser;
BigInteger.prototype.leq = BigInteger.prototype.lesserOrEquals;
BigInteger.prototype.gt = BigInteger.prototype.greater;
BigInteger.prototype.geq = BigInteger.prototype.greaterOrEquals;
BigInteger.prototype.eq = BigInteger.prototype.equals;
BigInteger.prototype.neq = BigInteger.prototype.notEquals;
function max (a, b) {
a = parseInput(a);
b = parseInput(b);
return a.greater(b) ? a : b;
}
function min (a, b) {
a = parseInput(a);
b = parseInput(b);
return a.lesser(b) ? a : b;
}
BigInteger.prototype.isPositive = function () {
return this.sign === sign.positive;
};
BigInteger.prototype.isNegative = function () {
return this.sign === sign.negative;
};
BigInteger.prototype.isEven = function () {
return this.value[0] % 2 === 0;
};
BigInteger.prototype.isOdd = function () {
return this.value[0] % 2 === 1;
};
BigInteger.prototype.isUnit = function () {
return this.value.length === 1 && this.value[0] === 1;
};
BigInteger.prototype.isDivisibleBy = function (n) {
return this.mod(n).equals(ZERO);
};
BigInteger.prototype.isPrime = function () {
var n = this.abs(),
nPrev = n.prev();
if (n.isUnit()) return false;
if (n.equals(2) || n.equals(3) || n.equals(5)) return true;
if (n.isEven() || n.isDivisibleBy(3) || n.isDivisibleBy(5)) return false;
if (n.lesser(25)) return true;
var a = [2, 3, 5, 7, 11, 13, 17, 19],
b = nPrev,
d, t, i, x;
while (b.isEven()) b = b.divide(2);
for (i = 0; i < a.length; i++) {
x = bigInt(a[i]).modPow(b, n);
if (x.equals(ONE) || x.equals(nPrev)) continue;
for (t = true, d = b; t && d.lesser(nPrev); d = d.multiply(2)) {
x = x.square().mod(n);
if (x.equals(nPrev)) t = false;
}
if (t) return false;
}
return true;
};
function randBetween (a, b) {
a = parseInput(a);
b = parseInput(b);
var low = min(a, b), high = max(a, b);
var range = high.subtract(low);
var length = range.value.length - 1;
var result = [], restricted = true;
for (var i = length; i >= 0; i--) {
var top = restricted ? range.value[i] : base;
var digit = Math.floor(Math.random() * top);
result.unshift(digit);
if (digit < top) restricted = false;
}
return low.add(new BigInteger(result, false));
}
var powersOfTwo = [1];
while (powersOfTwo[powersOfTwo.length - 1] <= base) powersOfTwo.push(2 * powersOfTwo[powersOfTwo.length - 1]);
var powers2Length = powersOfTwo.length, highestPower2 = powersOfTwo[powers2Length - 1];
BigInteger.prototype.shiftLeft = function (n) {
if (!isSmall(n)) {
if (n.isNegative()) return this.shiftRight(n.abs());
return this.times(bigInt(2).pow(n));
}
n = +n;
if (n < 0) return this.shiftRight(-n);
var result = this;
while (n >= powers2Length) {
result = fastMultiply(result, highestPower2);
n -= powers2Length - 1;
}
return fastMultiply(result, powersOfTwo[n]);
};
BigInteger.prototype.shiftRight = function (n) {
if (!isSmall(n)) {
if (n.isNegative()) return this.shiftLeft(n.abs());
return this.over(bigInt(2).pow(n));
}
n = +n;
if (n < 0) return this.shiftLeft(-n);
var result = this;
while (n >= powers2Length) {
if (result.equals(ZERO)) return result;
result = fastDivMod(result, highestPower2).quotient;
n -= powers2Length - 1;
}
return fastDivMod(result, powersOfTwo[n]).quotient;
};
// Reference: http://en.wikipedia.org/wiki/Bitwise_operation#Mathematical_equivalents
function bitwise(x, y, fn) {
var sum = ZERO;
var limit = max(x.abs(), y.abs());
var n = 0, _2n = ONE;
while (_2n.lesserOrEquals(limit)) {
var xMod, yMod;
xMod = x.over(_2n).isEven() ? 0 : 1;
yMod = y.over(_2n).isEven() ? 0 : 1;
sum = sum.add(_2n.times(fn(xMod, yMod)));
_2n = fastMultiply(_2n, 2);
}
return sum;
}
BigInteger.prototype.not = function () {
var body = bitwise(this, this, function (xMod) { return (xMod + 1) % 2; });
return !this.sign ? body.negate() : body;
};
BigInteger.prototype.and = function (n) {
n = parseInput(n);
var body = bitwise(this, n, function (xMod, yMod) { return xMod * yMod; });
return this.sign && n.sign ? body.negate() : body;
};
BigInteger.prototype.or = function (n) {
n = parseInput(n);
var body = bitwise(this, n, function (xMod, yMod) { return (xMod + yMod + xMod * yMod) % 2 });
return this.sign || n.sign ? body.negate() : body;
};
BigInteger.prototype.xor = function (n) {
n = parseInput(n);
var body = bitwise(this, n, function (xMod, yMod) { return (xMod + yMod) % 2; });
return this.sign ^ n.sign ? body.negate() : body;
};
BigInteger.prototype.toString = function (radix) {
if (radix === undefined) {
radix = 10;
}
if (radix !== 10) return toBase(this, radix);
var first = this;
var str = "", len = first.value.length;
if (len === 0) {
return "0";
}
while (len--) {
if (first.value[len].toString().length === 8) str += first.value[len];
else str += (base.toString() + first.value[len]).slice(-logBase);
}
while (str[0] === "0") {
str = str.slice(1);
}
if (!str.length) str = "0";
if (str === "0") return str;
var s = first.sign === sign.positive ? "" : "-";
return s + str;
};
BigInteger.prototype.toJSNumber = function () {
return this.valueOf();
};
BigInteger.prototype.valueOf = function () {
if (this.value.length === 1) return this.sign ? -this.value[0] : this.value[0];
return +this.toString();
};
var goesInto = function (a, b) {
var a = new BigInteger(a, sign.positive), b = new BigInteger(b, sign.positive);
if (a.equals(0)) throw new Error("Cannot divide by 0");
var n = 0;
do {
var inc = 1;
var c = a, t = c.times(10);
while (t.lesser(b)) {
c = t;
inc *= 10;
t = t.times(10);
}
while (c.lesserOrEquals(b)) {
b = b.minus(c);
n += inc;
}
} while (a.lesserOrEquals(b));
return {
remainder: b.value,
result: n
};
};
var ZERO = new BigInteger([0], sign.positive);
var ONE = new BigInteger([1], sign.positive);
var MINUS_ONE = new BigInteger([1], sign.negative);
function parseInput(text) {
if (text instanceof BigInteger) return text;
if (Math.abs(+text) < base && +text === (+text | 0)) {
var value = +text;
return new BigInteger([Math.abs(value)], (value < 0 || (1 / value) === -Infinity));
}
text += "";
var s = sign.positive, value = [];
if (text[0] === "-") {
s = sign.negative;
text = text.slice(1);
}
var text = text.split(/e/i);
if (text.length > 2) throw new Error("Invalid integer: " + text.join("e"));
if (text[1]) {
var exp = text[1];
if (exp[0] === "+") exp = exp.slice(1);
exp = parseInput(exp);
if (exp.lesser(0)) throw new Error("Cannot include negative exponent part for integers");
while (exp.notEquals(0)) {
text[0] += "0";
exp = exp.prev();
}
}
text = text[0];
if (text === "-0") text = "0";
var isValid = /^([0-9][0-9]*)$/.test(text);
if (!isValid) throw new Error("Invalid integer: " + text);
while (text.length) {
var divider = text.length > logBase ? text.length - logBase : 0;
value.push(+text.slice(divider));
text = text.slice(0, divider);
}
return new BigInteger(value, s);
}
var parseBase = function (text, base) {
base = parseInput(base);
var val = ZERO;
var digits = [];
var i;
var isNegative = false;
function parseToken(text) {
var c = text[i].toLowerCase();
if (i === 0 && text[i] === "-") {
isNegative = true;
return;
}
if (/[0-9]/.test(c)) digits.push(parseInput(c));
else if (/[a-z]/.test(c)) digits.push(parseInput(c.charCodeAt(0) - 87));
else if (c === "<") {
var start = i;
do { i++; } while (text[i] !== ">");
digits.push(parseInput(text.slice(start + 1, i)));
}
else throw new Error(c + " is not a valid character");
}
for (i = 0; i < text.length; i++) {
parseToken(text);
}
digits.reverse();
for (i = 0; i < digits.length; i++) {
val = val.add(digits[i].times(base.pow(i)));
}
return isNegative ? val.negate() : val;
};
function stringify(digit) {
var v = digit.value;
if (v.length === 1 && v[0] <= 36) {
return "0123456789abcdefghijklmnopqrstuvwxyz".charAt(v[0]);
}
return "<" + v + ">";
}
function toBase(n, base) {
base = bigInt(base);
if (base.equals(0)) {
if (n.equals(0)) return "0";
throw new Error("Cannot convert nonzero numbers to base 0.");
}
if (base.equals(-1)) {
if (n.equals(0)) return "0";
if (n.lesser(0)) return Array(1 - n).join("10");
return "1" + Array(+n).join("01");
}
var minusSign = "";
if (n.isNegative() && base.isPositive()) {
minusSign = "-";
n = n.abs();
}
if (base.equals(1)) {
if (n.equals(0)) return "0";
return minusSign + Array(+n + 1).join(1);
}
var out = [];
var left = n, divmod;
while (left.lesser(0) || left.compareAbs(base) >= 0) {
divmod = left.divmod(base);
left = divmod.quotient;
var digit = divmod.remainder;
if (digit.lesser(0)) {
digit = base.minus(digit).abs();
left = left.next();
}
out.push(stringify(digit));
}
out.push(stringify(left));
return minusSign + out.reverse().join("");
}
var fnReturn = function (a, b) {
if (typeof a === "undefined") return ZERO;
if (typeof b !== "undefined") return parseBase(a, b);
return parseInput(a);
};
fnReturn.zero = ZERO;
fnReturn.one = ONE;
fnReturn.minusOne = MINUS_ONE;
fnReturn.randBetween = randBetween;
fnReturn.min = min;
fnReturn.max = max;
fnReturn.gcd = gcd;
fnReturn.lcm = lcm;
return fnReturn;
})();
if (typeof module !== "undefined") {
module.exports = bigInt;
}