mpzjs
Version:
Arbitrary-precision integer arithmetic using libgmp
917 lines (792 loc) • 24.2 kB
JavaScript
const inspect = require('util').inspect;
const {MPZInternal} = require('node-gyp-build')(__dirname + '/..');
function MPZ(value, base) {
if (!new.target) {
return new MPZ(value, base);
}
if (value == null) {
this._value = new MPZInternal();
return;
}
if (value instanceof MPZInternal) {
this._value = value;
return;
}
if (typeof value === 'number') {
this._value = new MPZInternal(value);
return;
}
const args = prepareArgs(value, base || 10);
this._value = new MPZInternal(args.num, args.base);
}
const exponentRE = /e[-+]?\d/i;
const parseRE = /(.*)e(.*)/i;
function prepareArgs(num, base) {
if (typeof num !== 'string') {
num = num.toString(base);
}
if (base === 10 && exponentRE.test(num)) {
const parsedNum = parseRE.exec(num);
const coefficient = parsedNum[1];
let pow = Number(parsedNum[2]);
const dotPos = coefficient.indexOf('.');
if (dotPos !== -1) {
if (pow < 0) {
pow -= coefficient.length - dotPos;
} else {
pow -= coefficient.length - dotPos - 1;
}
}
if (pow < 0) {
return {
num: coefficient.substring(0, coefficient.length + pow).replace('.', ''),
base: 10,
};
}
return {
num: coefficient.replace('.', '') + ''.padEnd(pow, '0'),
base: 10,
}
}
return {
num,
base,
};
}
module.exports = MPZ;
MPZ.prototype.toString = function (base) {
if (base == null || base === 10) {
return this._value.toString();
}
base = Number(base);
if (base < 2 || base > 62) {
throw RangeError('base must be : 2<= base <= 62');
}
return this._value.toString(base);
};
MPZ.prototype[inspect.custom] = function () {
return `<MPZ ${this.toString()}>`;
};
MPZ.prototype.toNumber = function () {
return this._value.toNumber();
};
MPZ.prototype.toBigInt = function () {
return BigInt(this._value.toString());
};
MPZ.prototype.toJSON = function () {
return this.toString();
};
MPZ.prototype.valueOf = function () {
return this.toBigInt();
};
MPZ.prototype.set = function (value) {
if (value instanceof MPZ) {
this._value.set(value._value);
}
if (typeof value === 'number') {
this._value.set(value);
}
this._value.set(new MPZ(value)._value);
return this;
};
MPZ.set = function (result, value) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (value == null) {
throw new TypeError('Second operand is missed');
}
result.set(value);
};
MPZ.prototype.add = function (value) {
if (value instanceof MPZ) {
return new MPZ(this._value.add(value._value));
}
if (typeof value === 'number') {
return new MPZ(this._value.add(value));
}
return new MPZ(this._value.add(new MPZ(value)._value));
};
MPZ.add = function (result, value1, value2) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (value1 == null) {
throw new TypeError('Second operand is missed');
}
if (value2 == null) {
throw new TypeError('Third operand is missed');
}
const num1 = value1 instanceof MPZ
? value1._value
: new MPZ(value1)._value;
const num2 = value2 instanceof MPZ
? value2._value
: typeof value2 === 'number'
? value2
: new MPZ(value2)._value;
result._value.assignAdd(num1, num2);
};
MPZ.prototype.sub = function (value) {
if (value instanceof MPZ) {
return new MPZ(this._value.sub(value._value));
}
if (typeof value === 'number') {
return new MPZ(this._value.sub(value));
}
return new MPZ(this._value.sub(new MPZ(value)._value));
};
MPZ.sub = function (result, value1, value2) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (value1 == null) {
throw new TypeError('Second operand is missed');
}
if (value2 == null) {
throw new TypeError('Third operand is missed');
}
const num1 = value1 instanceof MPZ
? value1._value
: new MPZ(value1)._value;
const num2 = value2 instanceof MPZ
? value2._value
: typeof value2 === 'number'
? value2
: new MPZ(value2)._value;
result._value.assignSub(num1, num2);
};
MPZ.prototype.mul = function (value) {
if (value instanceof MPZ) {
return new MPZ(this._value.mul(value._value));
}
if (typeof value === 'number' && value >= 0) {
return new MPZ(this._value.mul(value));
}
return new MPZ(this._value.mul(new MPZ(value)._value));
};
MPZ.mul = function (result, value1, value2) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (value1 == null) {
throw new TypeError('Second operand is missed');
}
if (value2 == null) {
throw new TypeError('Third operand is missed');
}
const num1 = value1 instanceof MPZ
? value1._value
: new MPZ(value1)._value;
const num2 = value2 instanceof MPZ
? value2._value
: typeof value2 === 'number' && value2 >= 0
? value2
: new MPZ(value2)._value;
result._value.assignMul(num1, num2);
};
MPZ.prototype.div = function (value) {
if (value instanceof MPZ) {
return new MPZ(this._value.div(value._value));
}
if (typeof value === 'number' && value >= 0) {
return new MPZ(this._value.div(value));
}
return new MPZ(this._value.div(new MPZ(value)._value));
};
MPZ.div = function (result, value1, value2) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (value1 == null) {
throw new TypeError('Second operand is missed');
}
if (value2 == null) {
throw new TypeError('Third operand is missed');
}
const num1 = value1 instanceof MPZ
? value1._value
: new MPZ(value1)._value;
const num2 = value2 instanceof MPZ
? value2._value
: typeof value2 === 'number' && value2 >= 0
? value2
: new MPZ(value2)._value;
result._value.assignDiv(num1, num2);
};
MPZ.prototype.mod = function (value) {
if (value instanceof MPZ) {
return new MPZ(this._value.mod(value._value));
}
if (typeof value === 'number' && value >= 0) {
return new MPZ(this._value.mod(value));
}
return new MPZ(this._value.mod(new MPZ(value)._value));
};
MPZ.mod = function (result, value1, value2) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (value1 == null) {
throw new TypeError('Second operand is missed');
}
if (value2 == null) {
throw new TypeError('Third operand is missed');
}
const num1 = value1 instanceof MPZ
? value1._value
: new MPZ(value1)._value;
const num2 = value2 instanceof MPZ
? value2._value
: typeof value2 === 'number' && value2 >= 0
? value2
: new MPZ(value2)._value;
result._value.assignMod(num1, num2);
};
MPZ.addMul = function (result, value1, value2) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (value1 == null) {
throw new TypeError('Second operand is missed');
}
if (value2 == null) {
throw new TypeError('Third operand is missed');
}
const num1 = value1 instanceof MPZ
? value1._value
: new MPZ(value1)._value;
const num2 = value2 instanceof MPZ
? value2._value
: typeof value2 === 'number' && value2 >= 0
? value2
: new MPZ(value2)._value;
result._value.assignAddMul(num1, num2);
};
MPZ.subMul = function (result, value1, value2) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (value1 == null) {
throw new TypeError('Second operand is missed');
}
if (value2 == null) {
throw new TypeError('Third operand is missed');
}
const num1 = value1 instanceof MPZ
? value1._value
: new MPZ(value1)._value;
const num2 = value2 instanceof MPZ
? value2._value
: typeof value2 === 'number' && value2 >= 0
? value2
: new MPZ(value2)._value;
result._value.assignSubMul(num1, num2);
};
MPZ.prototype.and = function (value) {
if (value instanceof MPZ) {
return new MPZ(this._value.and(value._value));
}
return new MPZ(this._value.and(new MPZ(value)._value));
};
MPZ.and = function (result, value1, value2) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (value1 == null) {
throw new TypeError('Second operand is missed');
}
if (value2 == null) {
throw new TypeError('Third operand is missed');
}
const num1 = value1 instanceof MPZ
? value1._value
: new MPZ(value1)._value;
const num2 = value2 instanceof MPZ
? value2._value
: new MPZ(value2)._value;
result._value.assignAnd(num1, num2);
};
MPZ.prototype.or = function (value) {
if (value instanceof MPZ) {
return new MPZ(this._value.or(value._value));
}
return new MPZ(this._value.or(new MPZ(value)._value));
};
MPZ.or = function (result, value1, value2) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (value1 == null) {
throw new TypeError('Second operand is missed');
}
if (value2 == null) {
throw new TypeError('Third operand is missed');
}
const num1 = value1 instanceof MPZ
? value1._value
: new MPZ(value1)._value;
const num2 = value2 instanceof MPZ
? value2._value
: new MPZ(value2)._value;
result._value.assignOr(num1, num2);
};
MPZ.prototype.xor = function (value) {
if (value instanceof MPZ) {
return new MPZ(this._value.xor(value._value));
}
return new MPZ(this._value.xor(new MPZ(value)._value));
};
MPZ.xor = function (result, value1, value2) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (value1 == null) {
throw new TypeError('Second operand is missed');
}
if (value2 == null) {
throw new TypeError('Third operand is missed');
}
const num1 = value1 instanceof MPZ
? value1._value
: new MPZ(value1)._value;
const num2 = value2 instanceof MPZ
? value2._value
: new MPZ(value2)._value;
result._value.assignXor(num1, num2);
};
MPZ.prototype.not = function () {
return new MPZ(this._value.not());
};
MPZ.not = function (result, value) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (value == null) {
throw new TypeError('Second operand is missed');
}
const num = value instanceof MPZ
? value._value
: new MPZ(value)._value;
result._value.assignNot(num);
};
MPZ.prototype.shiftLeft = function (num) {
if (typeof num !== 'number') {
num = parseInt(num.toString(), 10);
}
if (num < 0) {
throw RangeError('Shift must be positive');
}
return new MPZ(this._value.shiftLeft(num));
};
MPZ.shiftLeft = function (result, value, shift) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (value == null) {
throw new TypeError('Second operand is missed');
}
if (shift == null) {
throw new TypeError('Third operand is missed');
}
const num1 = value instanceof MPZ
? value._value
: new MPZ(value)._value;
const num2 = typeof shift === 'number'
? shift
: parseInt(shift.toString(), 10);
if (num2 < 0) {
throw RangeError('Shift must be positive');
}
result._value.assignShiftLeft(num1, num2);
};
MPZ.prototype.shiftRight = function (num) {
if (typeof num !== 'number') {
num = parseInt(num.toString(), 10);
}
if (num < 0) {
throw RangeError('Shift must be positive');
}
return new MPZ(this._value.shiftRight(num));
};
MPZ.shiftRight = function (result, value, shift) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (value == null) {
throw new TypeError('Second operand is missed');
}
if (shift == null) {
throw new TypeError('Third operand is missed');
}
const num1 = value instanceof MPZ
? value._value
: new MPZ(value)._value;
const num2 = typeof shift === 'number'
? shift
: parseInt(shift.toString(), 10);
if (num2 < 0) {
throw RangeError('Shift must be positive');
}
result._value.assignShiftRight(num1, num2);
};
MPZ.prototype.abs = function () {
return new MPZ(this._value.abs());
};
MPZ.abs = function (result, value) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (value == null) {
throw new TypeError('Second operand is missed');
}
const num = value instanceof MPZ
? value._value
: new MPZ(value)._value;
result._value.assignAbs(num);
};
MPZ.prototype.neg = function () {
return new MPZ(this._value.neg());
};
MPZ.neg = function (result, value) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (value == null) {
throw new TypeError('Second operand is missed');
}
const num = value instanceof MPZ
? value._value
: new MPZ(value)._value;
result._value.assignNeg(num);
};
MPZ.prototype.sqrt = function() {
return new MPZ(this._value.sqrt());
};
MPZ.sqrt = function (result, value) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (value == null) {
throw new TypeError('Second operand is missed');
}
const num = value instanceof MPZ
? value._value
: new MPZ(value)._value;
result._value.assignSqrt(num);
};
MPZ.prototype.root = function(num) {
if (typeof num !== 'number') {
num = parseInt(num.toString(), 10);
}
if (num < 0) {
throw RangeError('Root degree must be positive');
}
return new MPZ(this._value.root(num));
};
MPZ.root = function (result, value, num) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (value == null) {
throw new TypeError('Second operand is missed');
}
if (num == null) {
throw new TypeError('Third operand is missed');
}
const num1 = value instanceof MPZ
? value._value
: new MPZ(value)._value;
const num2 = typeof num === 'number'
? num
: parseInt(num.toString(), 10);
if (num2 < 0) {
throw RangeError('Root degree must be positive');
}
result._value.assignRoot(num1, num2);
};
MPZ.prototype.powm = function (exp, mod) {
const normMod = mod instanceof MPZ
? mod._value
: new MPZ(mod)._value;
if (exp instanceof MPZ) {
return new MPZ(this._value.powm(exp._value, normMod));
}
if (typeof exp === 'number' && exp >= 0) {
return new MPZ(this._value.powm(exp, normMod));
}
return new MPZ(this._value.powm(new MPZ(exp)._value, normMod));
};
MPZ.powm = function (result, base, exp, mod) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (base == null) {
throw new TypeError('base operand is missed');
}
if (exp == null) {
throw new TypeError('exp operand is missed');
}
if (mod == null) {
throw new TypeError('mod operand is missed');
}
const normBase = base instanceof MPZ
? base._value
: new MPZ(base)._value;
const normExp = exp instanceof MPZ
? exp._value
: typeof exp === 'number' && exp >= 0
? exp
: new MPZ(exp)._value;
const normMod = mod instanceof MPZ
? mod._value
: new MPZ(mod)._value;
result._value.assignPowm(normBase, normExp, normMod);
};
MPZ.prototype.pow = function (exp) {
if (typeof exp !== 'number') {
exp = parseInt(exp.toString(), 10);
}
if (exp < 0) {
throw RangeError('Exponent must be positive');
}
return new MPZ(this._value.pow(exp));
};
MPZ.pow = function (result, base, exp) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (base == null) {
throw new TypeError('base operand is missed');
}
if (exp == null) {
throw new TypeError('exp operand is missed');
}
const normBase = base instanceof MPZ
? base._value
: new MPZ(base)._value;
const normExp = typeof exp === 'number'
? exp
: parseInt(exp.toString(), 10);
if (normExp < 0) {
throw RangeError('Exponent must be positive');
}
result._value.assignPow(normBase, normExp);
};
MPZ.prototype.cmp = function (num) {
if (num instanceof MPZ) {
return this._value.compare(num._value);
}
if (typeof num === 'number') {
return this._value.compare(num);
}
return this._value.compare(new MPZ(num)._value);
};
MPZ.prototype.gt = function (num) {
return this.cmp(num) > 0;
};
MPZ.prototype.ge = function (num) {
return this.cmp(num) >= 0;
};
MPZ.prototype.eq = function (num) {
return this.cmp(num) === 0;
};
MPZ.prototype.ne = function (num) {
return this.cmp(num) !== 0;
};
MPZ.prototype.lt = function (num) {
return this.cmp(num) < 0;
};
MPZ.prototype.le = function (num) {
return this.cmp(num) <= 0;
};
MPZ.prototype.rand = function (to) {
if (to == null) {
if (this.toNumber() === 1) {
return new MPZ(0);
}
return new MPZ(this._value.rand());
}
const n = to instanceof MPZ
? to.sub(this)
: new MPZ(to).sub(this);
return new MPZ(n._value.rand()).add(this);
};
MPZ.rand = function (result, from, to) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (from == null) {
throw new TypeError('Second operand is missed');
}
if (to == null) {
const normTo = from instanceof MPZ
? from._value
: new MPZ(from)._value;
result._value.assignRand(normTo);
return;
}
const normFrom = from instanceof MPZ
? from._value
: new MPZ(from)._value;
const normTo = to instanceof MPZ
? to._value
: new MPZ(to)._value;
result._value.assignRand(normTo.sub(normFrom));
result._value.assignAdd(result._value, normFrom);
};
MPZ.prototype.probPrime = function (reps) {
const n = this._value.probPrime(reps || 10);
return { 0: false, 1: 'maybe', 2: true }[n];
};
MPZ.prototype.nextPrime = function () {
return new MPZ(this._value.nextPrime());
};
MPZ.nextPrime = function (result, num) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (num == null) {
throw new TypeError('Second operand is missed');
}
const normNum = num instanceof MPZ
? num._value
: new MPZ(num)._value;
result._value.assignNextPrime(normNum);
};
MPZ.prototype.invert = function (mod) {
const normNum = mod instanceof MPZ
? mod._value
: new MPZ(mod)._value;
return new MPZ(this._value.invert(normNum));
};
MPZ.invert = function (result, num, mod) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (num == null) {
throw new TypeError('Second operand is missed');
}
if (mod == null) {
throw new TypeError('Third operand is missed');
}
const normNum = num instanceof MPZ
? num._value
: new MPZ(num)._value;
const normMod = mod instanceof MPZ
? mod._value
: new MPZ(mod)._value;
result._value.assignInvert(normNum, normMod);
};
MPZ.prototype.gcd = function (num) {
const normNum = num instanceof MPZ
? num._value
: new MPZ(num)._value;
return new MPZ(this._value.gcd(normNum));
};
MPZ.gcd = function (result, num1, num2) {
if (!(result instanceof MPZ)) {
throw new TypeError('First operand must be MPZ');
}
if (num1 == null) {
throw new TypeError('Second operand is missed');
}
if (num2 == null) {
throw new TypeError('Third operand is missed');
}
const normNum1 = num1 instanceof MPZ
? num1._value
: new MPZ(num1)._value;
const normNum2 = num2 instanceof MPZ
? num2._value
: new MPZ(num2)._value;
result._value.assignGcd(normNum1, normNum2);
};
MPZ.prototype.bitLength = function () {
return this._value.bitLength();
};
MPZ.fromBuffer = function (buf, opts) {
if (!opts) {
opts = {};
}
const endian = { 1: 'big', '-1': 'little' }[opts.endian]
|| opts.endian || 'big';
const size = opts.size || 1;
if (buf.length % size !== 0) {
throw new RangeError(`Buffer length (${buf.length}) must be a multiple of size (${size})`);
}
const hex = [];
for (let i = 0; i < buf.length; i += size) {
const chunk = [];
for (let j = 0; j < size; j++) {
chunk.push(buf[i + (endian === 'big' ? j : (size - j - 1))]);
}
hex.push(chunk
.map(c => (c < 16 ? '0' : '') + c.toString(16))
.join('')
);
}
return new MPZ(hex.join(''), 16);
};
MPZ.prototype.toBuffer = function (opts) {
if (typeof opts === 'string') {
if (opts !== 'mpint') {
throw new TypeError('Unsupported Buffer representation');
}
const abs = this.abs();
const buf = abs.toBuffer({ size: 1, endian: 'big' });
let len = buf.length === 1 && buf[0] === 0 ? 0 : buf.length;
if ((buf[0] & 0x80) === 0x80) {
len ++;
}
const ret = Buffer.alloc(4 + len);
if (len > 0) {
buf.copy(ret, 4 + ((buf[0] & 0x80) === 0x80 ? 1 : 0));
}
if ((buf[0] & 0x80) === 0x80) {
ret[4] = 0;
}
ret[0] = len & (0xff << 24);
ret[1] = len & (0xff << 16);
ret[2] = len & (0xff << 8);
ret[3] = len & (0xff << 0);
// two's compliment for negative integers:
const isNeg = this.lt(0);
if (isNeg) {
for (let i = 4; i < ret.length; i++) {
ret[i] = 0xff - ret[i];
}
}
ret[4] = (ret[4] & 0x7f) | (isNeg ? 0x80 : 0);
if (isNeg) {
ret[ret.length - 1]++;
}
return ret;
}
if (!opts) {
opts = {};
}
const endian = { 1: 'big', '-1': 'little' }[opts.endian]
|| opts.endian || 'big';
const size = opts.size || 1;
let hex = this.toString(16);
if (hex.charAt(0) === '-') {
throw new Error('converting negative numbers to Buffers not supported yet');
}
const len = Math.ceil(hex.length / (2 * size)) * size;
const buf = Buffer.alloc(len);
// zero-pad the hex string so the chunks are all `size` long
while (hex.length < 2 * len) {
hex = '0' + hex;
}
const hx = hex
.split(new RegExp('(.{' + (2 * size) + '})'))
.filter(s => s.length > 0);
hx.forEach((chunk, i) => {
for (let j = 0; j < size; j++) {
const ix = i * size + (endian === 'big' ? j : size - j - 1);
buf[ix] = parseInt(chunk.slice(j * 2, j * 2 + 2), 16);
}
});
return buf;
};