UNPKG

bignum

Version:

Arbitrary-precision integer arithmetic using OpenSSL

409 lines (344 loc) 9.17 kB
var bin = require('bindings')('bignum') var Buffer = require('safe-buffer').Buffer var BigNum = bin.BigNum module.exports = BigNum BigNum.conditionArgs = function (num, base) { if (typeof num !== 'string') num = num.toString(base || 10) if (num.match(/e\+/)) { // positive exponent if (!Number(num).toString().match(/e+/)) { return { num: Math.floor(Number(num)).toString(), base: 10 } } else { var pow = Math.ceil(Math.log(num) / Math.log(2)) var n = (num / Math.pow(2, pow)).toString(2) .replace(/^0/, '') var i = n.length - n.indexOf('.') n = n.replace(/\./, '') for (; i <= pow; i++) n += '0' return { num: n, base: 2 } } } else if (num.match(/e-/)) { // negative exponent return { num: Math.floor(Number(num)).toString(), base: base || 10 } } else { return { num: num, base: base || 10 } } } bin.setJSConditioner(BigNum.conditionArgs) BigNum.isBigNum = function (num) { if (!num) { return false } for (var key in BigNum.prototype) { if (!num[key]) { return false } } return true } BigNum.prototype.inspect = function () { return '<BigNum ' + this.toString(10) + '>' } BigNum.prototype.toString = function (base) { var value if (base) { value = this.tostring(base) } else { value = this.tostring() } if (base > 10 && typeof value === 'string') { value = value.toLowerCase() } return value } BigNum.prototype.toNumber = function () { return parseInt(this.toString(), 10) } ;['add', 'sub', 'mul', 'div', 'mod'].forEach(function (op) { BigNum.prototype[op] = function (num) { var x if (BigNum.isBigNum(num)) { return this['b' + op](num) } else if (typeof num === 'number') { if (num >= 0) { return this['u' + op](num) } else if (op === 'add') { return this.usub(-num) } else if (op === 'sub') { return this.uadd(-num) } else { x = BigNum(num) return this['b' + op](x) } } else if (typeof num === 'string') { x = BigNum(num) return this['b' + op](x) } else { throw new TypeError('Unspecified operation for type ' + (typeof num) + ' for ' + op) } } }) BigNum.prototype.abs = function () { return this.babs() } BigNum.prototype.neg = function () { return this.bneg() } BigNum.prototype.powm = function (num, mod) { var m if ((typeof mod) === 'number' || (typeof mod) === 'string') { m = BigNum(mod) } else if (BigNum.isBigNum(mod)) { m = mod } if ((typeof num) === 'number') { return this.upowm(num, m) } else if ((typeof num) === 'string') { var n = BigNum(num) return this.bpowm(n, m) } else if (BigNum.isBigNum(num)) { return this.bpowm(num, m) } } BigNum.prototype.mod = function (num, mod) { var m if ((typeof mod) === 'number' || (typeof mod) === 'string') { m = BigNum(mod) } else if (BigNum.isBigNum(mod)) { m = mod } if ((typeof num) === 'number') { return this.umod(num, m) } else if ((typeof num) === 'string') { var n = BigNum(num) return this.bmod(n, m) } else if (BigNum.isBigNum(num)) { return this.bmod(num, m) } } BigNum.prototype.pow = function (num) { if (typeof num === 'number') { if (num >= 0) { return this.upow(num) } else { return BigNum.prototype.powm.call(this, num, this) } } else { var x = parseInt(num.toString(), 10) return BigNum.prototype.pow.call(this, x) } } BigNum.prototype.shiftLeft = function (num) { if (typeof num === 'number') { if (num >= 0) { return this.umul2exp(num) } else { return this.shiftRight(-num) } } else { var x = parseInt(num.toString(), 10) return BigNum.prototype.shiftLeft.call(this, x) } } BigNum.prototype.shiftRight = function (num) { if (typeof num === 'number') { if (num >= 0) { return this.udiv2exp(num) } else { return this.shiftLeft(-num) } } else { var x = parseInt(num.toString(), 10) return BigNum.prototype.shiftRight.call(this, x) } } BigNum.prototype.cmp = function (num) { if (BigNum.isBigNum(num)) { return this.bcompare(num) } else if (typeof num === 'number') { if (num < 0) { return this.scompare(num) } else { return this.ucompare(num) } } else { var x = BigNum(num) return this.bcompare(x) } } BigNum.prototype.gt = function (num) { return this.cmp(num) > 0 } BigNum.prototype.ge = function (num) { return this.cmp(num) >= 0 } BigNum.prototype.eq = function (num) { return this.cmp(num) === 0 } BigNum.prototype.ne = function (num) { return this.cmp(num) !== 0 } BigNum.prototype.lt = function (num) { return this.cmp(num) < 0 } BigNum.prototype.le = function (num) { return this.cmp(num) <= 0 } 'and or xor'.split(' ').forEach(function (name) { BigNum.prototype[name] = function (num) { if (BigNum.isBigNum(num)) { return this['b' + name](num) } else { var x = BigNum(num) return this['b' + name](x) } } }) BigNum.prototype.sqrt = function () { return this.bsqrt() } BigNum.prototype.root = function (num) { if (BigNum.isBigNum(num)) { return this.broot(num) } else { return this.broot(num) } } BigNum.prototype.rand = function (to) { if (to === undefined) { if (this.toString() === '1') { return BigNum(0) } else { return this.brand0() } } else { var x = BigNum.isBigNum(to) ? to.sub(this) : BigNum(to).sub(this) return x.brand0().add(this) } } BigNum.prototype.invertm = function (mod) { if (BigNum.isBigNum(mod)) { return this.binvertm(mod) } else { var x = BigNum(mod) return this.binvertm(x) } } BigNum.prime = function (bits, safe) { if (typeof safe === 'undefined') { safe = true } // Force uint32 bits >>>= 0 return BigNum.uprime0(bits, !!safe) } BigNum.prototype.probPrime = function (reps) { var n = this.probprime(reps || 10) return { 1: true, 0: false }[n] } BigNum.prototype.nextPrime = function () { var num = this do { num = num.add(1) } while (!num.probPrime()) return num } BigNum.prototype.isBitSet = function (n) { return this.isbitset(n) === 1 } BigNum.fromBuffer = function (buf, opts) { if (!opts) opts = {} var endian = { 1: 'big', '-1': 'little' }[opts.endian] || opts.endian || 'big' var size = opts.size === 'auto' ? Math.ceil(buf.length) : (opts.size || 1) if (buf.length % size !== 0) { throw new RangeError('Buffer length (' + buf.length + ')' + ' must be a multiple of size (' + size + ')' ) } var hex = [] for (var i = 0; i < buf.length; i += size) { var chunk = [] for (var j = 0; j < size; j++) { chunk.push(buf[i + (endian === 'big' ? j : (size - j - 1))]) } hex.push(chunk .map(function (c) { return (c < 16 ? '0' : '') + c.toString(16) }) .join('') ) } return BigNum(hex.join(''), 16) } BigNum.prototype.toBuffer = function (opts) { if (typeof opts === 'string') { if (opts !== 'mpint') return 'Unsupported Buffer representation' var abs = this.abs() var buf = abs.toBuffer({ size: 1, endian: 'big' }) var len = buf.length === 1 && buf[0] === 0 ? 0 : buf.length if (buf[0] & 0x80) len++ var ret = Buffer.alloc(4 + len) if (len > 0) buf.copy(ret, 4 + (buf[0] & 0x80 ? 1 : 0)) if (buf[0] & 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: var isNeg = this.lt(0) if (isNeg) { for (var 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 = {} var endian = { 1: 'big', '-1': 'little' }[opts.endian] || opts.endian || 'big' var hex = this.toString(16) if (hex.charAt(0) === '-') { throw new Error('converting negative numbers to Buffers not supported yet') } var size = opts.size === 'auto' ? Math.ceil(hex.length / 2) : (opts.size || 1) len = Math.ceil(hex.length / (2 * size)) * size buf = Buffer.alloc(len) // zero-pad the hex string so the chunks are all `size` long while (hex.length < 2 * len) hex = '0' + hex var hx = hex .split(new RegExp('(.{' + (2 * size) + '})')) .filter(function (s) { return s.length > 0 }) hx.forEach(function (chunk, i) { for (var j = 0; j < size; j++) { var ix = i * size + (endian === 'big' ? j : size - j - 1) buf[ix] = parseInt(chunk.slice(j * 2, j * 2 + 2), 16) } }) return buf } Object.keys(BigNum.prototype).forEach(function (name) { if (name === 'inspect' || name === 'toString') return BigNum[name] = function (num) { var args = [].slice.call(arguments, 1) if (BigNum.isBigNum(num)) { return num[name].apply(num, args) } else { var bigi = BigNum(num) return bigi[name].apply(bigi, args) } } })