Merge pull request #266 from BigFav/develop

Cleanup and factorial fix.
This commit is contained in:
Jos de Jong 2015-01-12 22:18:17 +01:00
commit bc2c81f9cd
6 changed files with 111 additions and 84 deletions

View File

@ -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
]
};

View File

@ -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) {

View File

@ -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 () {

View File

@ -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 () {

View File

@ -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() {

View File

@ -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() {