diff --git a/lib/function/probability/factorial.js b/lib/function/probability/factorial.js index 26f05fc75..b56bda806 100644 --- a/lib/function/probability/factorial.js +++ b/lib/function/probability/factorial.js @@ -34,7 +34,7 @@ module.exports = function (math) { * @return {Number | BigNumber | Array | Matrix} The factorial of `n` */ math.factorial = function factorial (n) { - var value, res; + var value, res, preciseFacs; if (arguments.length != 1) { throw new math.error.ArgumentsError('factorial', arguments.length, 1); @@ -53,23 +53,33 @@ module.exports = function (math) { return new BigNumber(n); } - n = n.toNumber(); - if (n < fac.length) { - return (n < 21) - ? new BigNumber(fac[n]) - : fac[n]; + n = n.toNumber(); // Should definitely be below Number.MAX_VALUE + if (n < smallBigFacs.length) { + return BigNumber.convert(smallBigFacs[n]).toSD(math.precision); + } + + // adjust n do align with the precision specific tables + n -= smallBigFacs.length; + if (preciseFacs = bigBigFacs[math.precision]) { + if (preciseFacs[n]) { + return preciseFacs[n]; + } + res = preciseFacs[preciseFacs.length-1]; + } else { + preciseFacs = []; + res = BigNumber.convert(smallBigFacs[smallBigFacs.length-1]) + .toSD(math.precision); } var one = new BigNumber(1); - value = new BigNumber(fac.length); - res = fac[fac.length - 1]; - for (var i = fac.length; i < n; ++i) { + value = new BigNumber(preciseFacs.length + smallBigFacs.length); + for (var i = preciseFacs.length; i < n; ++i) { res = res.times(value); value = value.plus(one); - fac[i] = res; + preciseFacs[i] = res; } - return fac[n] = res.times(value); + return preciseFacs[n] = res.times(value); } if (isBoolean(n) || n === null) { @@ -92,8 +102,11 @@ module.exports = function (math) { return n.isInteger() && (!n.isNegative() || n.isZero()); }; - // 0-21! values - var fac = [ + // 21! >= values for each precision + var bigBigFacs = []; + + // 0-20! values + var smallBigFacs = [ 1, 1, 2, @@ -114,7 +127,6 @@ module.exports = function (math) { 355687428096000, 6402373705728000, 121645100408832000, - 2432902008176640000, - new BigNumber('51090942171709440000') + 2432902008176640000 ] }; diff --git a/lib/util/bignumber.js b/lib/util/bignumber.js index b54fb67ce..a26a72592 100644 --- a/lib/util/bignumber.js +++ b/lib/util/bignumber.js @@ -104,7 +104,7 @@ exports.and = function(x, y) { throw new Error('Parameters in function bitAnd must be integer numbers'); } - var BigNumber = x['constructor']; + var BigNumber = x.constructor; if (x.isNaN() || y.isNaN()) { return new BigNumber(NaN); } @@ -193,7 +193,7 @@ exports.leftShift = function (x, y) { throw new Error('Parameters in function leftShift must be integer numbers'); } - var BigNumber = x['constructor']; + var BigNumber = x.constructor; if (x.isNaN() || y.isNaN() || (y.isNegative() && !y.isZero())) { return new BigNumber(NaN); } @@ -221,14 +221,14 @@ exports.not = function (x) { throw new Error('Parameter in function bitNot must be integer numbers'); } - var BigNumber = x['constructor']; - var prevPrec = BigNumber['precision']; - BigNumber['precision'] = 1E9; + var BigNumber = x.constructor; + var prevPrec = BigNumber.precision; + BigNumber.config({precision: 1E9}); - var x = x.plus(BigNumber['ONE']); - x['s'] = -x['s'] || null; + var x = x.plus(BigNumber.ONE); + x.s = -x.s || null; - BigNumber['precision'] = prevPrec; + BigNumber.config({precision: prevPrec}); return x; }; @@ -256,7 +256,7 @@ exports.or = function (x, y) { throw new Error('Parameters in function bitOr must be integer numbers'); } - var BigNumber = x['constructor']; + var BigNumber = x.constructor; if (x.isNaN() || y.isNaN()) { return new BigNumber(NaN); } @@ -294,21 +294,22 @@ exports.or = function (x, y) { * @returns {BigNumber} sine of x */ exports.sin = function (x) { - var BigNumber = x['constructor']; - var precision = BigNumber['precision']; + var BigNumber = x.constructor; + var precision = BigNumber.precision; if (x.isNaN() || !x.isFinite()) { return new BigNumber(NaN); } // sin(-x) == -sin(x) - var isNeg; - if (isNeg = x.isNegative()) { - x['s'] = -x['s']; + var isNeg = x.isNegative(); + if (isNeg) { + x.s = -x.s; } // Get offset within the period of sin (-pi, pi] w/ gaurd digits - var pi = exports.pi(precision + ~~(3*Math.log(precision)) + 1); - var tau = pi.times(2); + var prec_plus_guard_digits = precision + (3*Math.log(precision) | 0) + 1; + var pi = exports.pi(prec_plus_guard_digits + 2); + var tau = exports.tau(prec_plus_guard_digits); // Catch if tau multiple using pi's precision if (x.div(pi.toDP(x.dp(), 1)).toNumber() % 2 == 0) { @@ -327,7 +328,7 @@ exports.sin = function (x) { var yPrev = NaN; var y2 = y.times(y); var num = y; - var den = BigNumber['ONE']; + var den = BigNumber.ONE; var add = true; for (var k = 1; !y.equals(yPrev); k += 2) { num = num.times(y2); @@ -339,9 +340,9 @@ exports.sin = function (x) { } if (isNeg) { - y['s'] = -y['s']; + y.s = -y.s; } - y['constructor']['precision'] = precision; + y.constructor.config({precision: precision}); return y.toDP(precision - 1); }; @@ -369,7 +370,7 @@ exports.rightShift = function (x, y) { throw new Error('Parameters in function rightArithShift must be integer numbers'); } - var BigNumber = x['constructor']; + var BigNumber = x.constructor; if (x.isNaN() || y.isNaN() || (y.isNegative() && !y.isZero())) { return new BigNumber(NaN); } @@ -415,7 +416,7 @@ exports.xor = function (x, y) { throw new Error('Parameters in function bitXor must be integer numbers'); } - var BigNumber = x['constructor']; + var BigNumber = x.constructor; if (x.isNaN() || y.isNaN()) { return new BigNumber(NaN); } @@ -639,11 +640,11 @@ exports.toFixed = function(value, precision) { function bitwise(x, y, func) { - var BigNumber = x['constructor']; + var BigNumber = x.constructor; var xBits, yBits; - var xSign = +(x['s'] < 0); - var ySign = +(y['s'] < 0); + var xSign = +(x.s < 0); + var ySign = +(y.s < 0); if (xSign) { xBits = decToBinary(coefficientToString(exports.not(x))); for (var i = 0; i < xBits.length; ++i) { @@ -676,11 +677,11 @@ function bitwise(x, y, func) { var longLen = maxBits.length; var expFuncVal = func(xSign, ySign) ^ 1; var outVal = new BigNumber(expFuncVal ^ 1); - var twoPower = BigNumber['ONE']; + var twoPower = BigNumber.ONE; var two = new BigNumber(2); - var prevPrec = BigNumber['precision']; - BigNumber['precision'] = 1E9; + var prevPrec = BigNumber.precision; + BigNumber.config({precision: 1E9}); while (shortLen > 0) { if (func(minBits[--shortLen], maxBits[--longLen]) == expFuncVal) { @@ -695,10 +696,10 @@ function bitwise(x, y, func) { twoPower = twoPower.times(two); } - BigNumber['precision'] = prevPrec; + BigNumber.config({precision: prevPrec}); if (expFuncVal == 0) { - outVal['s'] = -outVal['s']; + outVal.s = -outVal.s; } return outVal; } @@ -708,7 +709,7 @@ function bitwise(x, y, func) { function coefficientToString(x) { - var a = x['c']; + var a = x.c; var r = a[0] + ''; for (var i = 1; i < a.length; ++i) { @@ -723,7 +724,7 @@ function coefficientToString(x) { var j; for (j = r.length - 1; r.charAt(j) == '0'; --j); - var xe = x['e']; + var xe = x.e; var str = r.slice(0, j + 1 || 1); var strL = str.length; if (xe > 0) { diff --git a/test/function/bitwise/leftShift.test.js b/test/function/bitwise/leftShift.test.js index 88f6a2290..c516154dc 100644 --- a/test/function/bitwise/leftShift.test.js +++ b/test/function/bitwise/leftShift.test.js @@ -42,9 +42,9 @@ describe('leftShift', function () { assert.deepEqual(leftShift(bignumber(2), bignumber(3)), bignumber(16)); assert.deepEqual(leftShift(bignumber(500), bignumber(100)), bignumber('633825300114114700748351602688000')); assert.deepEqual(leftShift(bignumber(-1), bignumber(2)), bignumber(-4)); - assert.deepEqual(leftShift(bignumber(0), bignumber(-2)).toString(), 'NaN'); + assert.equal(leftShift(bignumber(0), bignumber(-2)).isNaN(), true); assert.deepEqual(leftShift(bignumber(Infinity), bignumber(2)), bignumber(Infinity)); - assert.deepEqual(leftShift(bignumber(Infinity), bignumber(Infinity)).toString(), 'NaN'); + assert.equal(leftShift(bignumber(Infinity), bignumber(Infinity)).isNaN(), true); }); it('should left shift mixed numbers and bignumbers', function () { @@ -53,8 +53,8 @@ describe('leftShift', function () { assert.deepEqual(leftShift(2, bignumber(3)), bignumber(16)); assert.deepEqual(leftShift(-1, bignumber(2)), bignumber(-4)); assert.deepEqual(leftShift(bignumber(-1), 2), bignumber(-4)); - assert.deepEqual(leftShift(bignumber(0), -2).toString(), 'NaN'); - assert.deepEqual(leftShift(bignumber(Infinity), Infinity).toString(), 'NaN'); + assert.equal(leftShift(bignumber(0), -2).isNaN(), true); + assert.equal(leftShift(bignumber(Infinity), Infinity).isNaN(), true); }); it('should left shift mixed booleans and bignumbers', function () { diff --git a/test/function/bitwise/rightArithShift.test.js b/test/function/bitwise/rightArithShift.test.js index 4c9d418d4..e2acb2f87 100644 --- a/test/function/bitwise/rightArithShift.test.js +++ b/test/function/bitwise/rightArithShift.test.js @@ -43,21 +43,21 @@ describe('rightArithShift', function () { assert.deepEqual(rightArithShift(bignumber(17), bignumber(3)), bignumber(2)); assert.deepEqual(rightArithShift(bignumber('633825300114114700748351602688000'), bignumber(100)), bignumber(500)); assert.deepEqual(rightArithShift(bignumber(-17), bignumber(3)), bignumber(-3)); - assert.deepEqual(rightArithShift(bignumber(-17), bignumber(-3)).toString(), 'NaN'); - assert.deepEqual(rightArithShift(bignumber(Infinity), bignumber(Infinity)).toString(), 'NaN'); + assert.equal(rightArithShift(bignumber(-17), bignumber(-3)).isNaN(), true); + assert.equal(rightArithShift(bignumber(Infinity), bignumber(Infinity)).isNaN(), true); assert.deepEqual(rightArithShift(bignumber(-Infinity), bignumber(Infinity)), bignumber(-1)); }); it('should right arithmetically shift mixed numbers and bignumbers', function () { assert.deepEqual(rightArithShift(bignumber(17), 3), bignumber(2)); assert.deepEqual(rightArithShift(bignumber('-633825300114114700748351602688000'), 100), bignumber(-500)); - assert.deepEqual(rightArithShift(bignumber(-17), -3).toString(), 'NaN'); + assert.equal(rightArithShift(bignumber(-17), -3).isNaN(), true); assert.deepEqual(rightArithShift(17, bignumber(3)), bignumber(2)); assert.deepEqual(rightArithShift(-17, bignumber(3)), bignumber(-3)); - assert.deepEqual(rightArithShift(-3, bignumber(-17)).toString(), 'NaN'); + assert.equal(rightArithShift(-3, bignumber(-17)).isNaN(), true); assert.deepEqual(rightArithShift(bignumber(-Infinity), Infinity), bignumber(-1)); - assert.deepEqual(rightArithShift(bignumber(Infinity), Infinity).toString(), 'NaN'); - assert.deepEqual(rightArithShift(Infinity, bignumber(Infinity)).toString(), 'NaN'); + assert.equal(rightArithShift(bignumber(Infinity), Infinity).isNaN(), true); + assert.equal(rightArithShift(Infinity, bignumber(Infinity)).isNaN(), true); }); it('should right arithmetically shift mixed booleans and bignumbers', function () { diff --git a/test/function/probability/factorial.test.js b/test/function/probability/factorial.test.js index e9e89e1fc..e98a70e1b 100644 --- a/test/function/probability/factorial.test.js +++ b/test/function/probability/factorial.test.js @@ -16,17 +16,27 @@ describe('factorial', function() { }); it('should calculate the factorial of a bignumber', function() { - assert.deepEqual(factorial(math.bignumber(0)), math.bignumber(1)); - assert.deepEqual(factorial(math.bignumber(1)), math.bignumber(1)); - assert.deepEqual(factorial(math.bignumber(2)), math.bignumber(2)); - assert.deepEqual(factorial(math.bignumber(3)), math.bignumber(6)); - assert.deepEqual(factorial(math.bignumber(4)), math.bignumber(24)); - assert.deepEqual(factorial(math.bignumber(5)), math.bignumber(120)); - assert.deepEqual(factorial(math.bignumber(20)), math.bignumber('2432902008176640000')); - assert.deepEqual(factorial(math.bignumber(21)), math.bignumber('51090942171709440000')); - assert.deepEqual(factorial(math.bignumber(25)), math.bignumber('1.5511210043330985984e+25')); - assert.deepEqual(factorial(math.bignumber(24)), math.bignumber('6.2044840173323943936e+23')); - assert.deepEqual(factorial(math.bignumber(22)), math.bignumber('1124000727777607680000')); + var bigmath = math.create({precision: 5}); + var bigfactorial = bigmath.factorial; + var bignumber = bigmath.bignumber; + + assert.deepEqual(bigfactorial(bignumber(11)), bignumber(39917000)); + assert.deepEqual(bigfactorial(bignumber(22)), bignumber(1.124e+21)); + + bigmath.config({precision: 20}); + assert.deepEqual(bigfactorial(bignumber(5)), bignumber(120)); + assert.deepEqual(bigfactorial(bignumber(19)), bignumber(121645100408832000)); + assert.deepEqual(bigfactorial(bignumber(20)), bignumber(2432902008176640000)); + assert.deepEqual(bigfactorial(bignumber(21)), bignumber('51090942171709440000')); + assert.deepEqual(bigfactorial(bignumber(25)), bignumber('1.5511210043330985984e+25')); + assert.deepEqual(bigfactorial(bignumber(24)), bignumber('6.2044840173323943936e+23')); + assert.deepEqual(bigfactorial(bignumber(22)), bignumber('1124000727777607680000')); + + bigmath.config({precision: 5}); + assert.deepEqual(bigfactorial(bignumber(11)), bignumber(39917000)); + assert.deepEqual(bigfactorial(bignumber(22)), bignumber(1.124e+21)); + assert.deepEqual(bigfactorial(bignumber(24)), bignumber(6.2045e+23)); + assert.deepEqual(bigfactorial(bignumber(21)), bignumber(5.1091e+19)); }); it('should calculate the factorial of a boolean', function() { diff --git a/test/function/trigonometry/sin.test.js b/test/function/trigonometry/sin.test.js index 1d17335ee..5949c1026 100644 --- a/test/function/trigonometry/sin.test.js +++ b/test/function/trigonometry/sin.test.js @@ -36,28 +36,32 @@ describe('sin', function() { assert.deepEqual(bigmath.sin(bigmath.bignumber(0)), bigmath.bignumber(0)); // 103.64 % tau = 3.109... <- pretty close to the pi boundary - assert.deepEqual(bigmath.sin(bigmath.bignumber(103.64)).toString(), '0.032551816956616158442731315994267213051204459121689332893471030' + - '714804383298805501395839512341888732261080924779366105855493575' + - '835362891900420559398509489530577719840860106717522689249606121' + - '2602629134186583352145117086874446046421403346033616'); - assert.deepEqual(bigmath.sin(bigmath.bignumber(-103.64)).toString(), '-0.0325518169566161584427313159942672130512044591216893328934710' + - '3071480438329880550139583951234188873226108092477936610585549' + - '3575835362891900420559398509489530577719840860106717522689249' + - '6061212602629134186583352145117086874446046421403346033616'); + var result_val = bigmath.sin(bigmath.bignumber(103.64)); + assert.equal(result_val.constructor.precision, 242); + assert.deepEqual(result_val.toString(), '0.032551816956616158442731315994267213051204459121689332893471030' + + '714804383298805501395839512341888732261080924779366105855493575' + + '835362891900420559398509489530577719840860106717522689249606121' + + '2602629134186583352145117086874446046421403346033616'); + result_val = bigmath.sin(bigmath.bignumber(-103.64)); + assert.equal(result_val.constructor.precision, 242); + assert.deepEqual(result_val.toString(), '-0.0325518169566161584427313159942672130512044591216893328934710' + + '3071480438329880550139583951234188873226108092477936610585549' + + '3575835362891900420559398509489530577719840860106717522689249' + + '6061212602629134186583352145117086874446046421403346033616'); bigmath.config({precision: 15}); var bigPi = bigmath.pi; - var result = bigmath.SQRT2.div(2).toString(); - assert.deepEqual(bigmath.sin(bigPi.div(4)).toString(), result); + result_val = bigmath.SQRT2.div(2).toString(); + assert.deepEqual(bigmath.sin(bigPi.div(4)).toString(), result_val); assert.deepEqual(bigmath.sin(bigPi.div(2)).toString(), '1'); - assert.deepEqual(bigmath.sin(bigPi.times(3).div(4)).toString(), result); - assert.deepEqual(bigmath.sin(bigPi).toString(), '0'); - assert.deepEqual(bigmath.sin(bigPi.times(5).div(4)).toString(), '-'+result); + assert.deepEqual(bigmath.sin(bigPi.times(3).div(4)).toString(), result_val); + assert.equal(bigmath.sin(bigPi).isZero(), true); + assert.deepEqual(bigmath.sin(bigPi.times(5).div(4)).toString(), '-'+result_val); assert.deepEqual(bigmath.sin(bigPi.times(3).div(2)).toString(), '-1'); - assert.deepEqual(bigmath.sin(bigPi.times(7).div(4)).toString(), '-'+result); - assert.deepEqual(bigmath.sin(bigPi.times(2)).toString(), '0'); - assert.deepEqual(bigmath.sin(bigmath.tau).toString(), '0'); - assert.deepEqual(bigmath.sin(bigmath.tau.times(2)).toString(), '0'); + assert.deepEqual(bigmath.sin(bigPi.times(7).div(4)).toString(), '-'+result_val); + assert.equal(bigmath.sin(bigPi.times(2)).isZero(), true); + assert.equal(bigmath.sin(bigmath.tau).isZero(), true); + assert.equal(bigmath.sin(bigmath.tau.times(2)).isZero(), true); }); it('should return the sine of a complex number', function() {