UNPKG

mathjs

Version:

Math.js is an extensive math library for JavaScript and Node.js. It features a flexible expression parser and offers an integrated solution to work with numbers, big numbers, complex numbers, units, and matrices.

1,409 lines (1,224 loc) 37.2 kB
'use strict'; var BigNumber = require('../type/BigNumber'); var isNumber = require('./number').isNumber; var digits = require('./number').digits; var memoize = require('./function').memoize; /** * Test whether value is a BigNumber * @param {*} value * @return {Boolean} isBigNumber */ exports.isBigNumber = function (value) { return (value instanceof BigNumber); }; /************************************* * Constants * *************************************/ /** * Calculate BigNumber e * @param {Number} precision * @returns {BigNumber} Returns e */ exports.e = memoize(function (precision) { var Big = BigNumber.constructor({precision: precision}); return new Big(1).exp(); }); /** * Calculate BigNumber golden ratio, phi = (1+sqrt(5))/2 * @param {Number} precision * @returns {BigNumber} Returns phi */ exports.phi = memoize(function (precision) { var Big = BigNumber.constructor({precision: precision}); return new Big(1).plus(new Big(5).sqrt()).div(2); }); /** * Calculate BigNumber pi. * * Uses Machin's formula: pi / 4 = 4 * arctan(1 / 5) - arctan(1 / 239) * http://milan.milanovic.org/math/english/pi/machin.html * @param {Number} precision * @returns {BigNumber} Returns pi */ exports.pi = memoize(function (precision) { // we calculate pi with a few decimal places extra to prevent round off issues var Big = BigNumber.constructor({precision: precision + 4}); var pi4th = new Big(4).times(arctan_taylor(new Big(1).div(5))) .minus(arctan_taylor(new Big(1).div(239))); Big.config({precision: precision}); // the final pi has the requested number of decimals return new Big(4).times(pi4th); }); /** * Calculate BigNumber tau, tau = 2 * pi * @param {Number} precision * @returns {BigNumber} Returns tau */ exports.tau = memoize(function (precision) { // we calculate pi at a slightly higher precision than configured to prevent round off errors // when multiplying by two in the end var pi = exports.pi(precision + 2); var Big = BigNumber.constructor({precision: precision}); return new Big(2).times(pi); }); /************************************* * Bitwise functions * *************************************/ /* * Special Cases: * N & n = N * n & 0 = 0 * n & -1 = n * n & n = n * I & I = I * -I & -I = -I * I & -I = 0 * I & n = n * I & -n = I * -I & n = 0 * -I & -n = -I * * @param {BigNumber} value * @param {BigNumber} value * @return {BigNumber} Result of `x` & `y`, is fully precise * */ exports.and = function(x, y) { if ((x.isFinite() && !x.isInteger()) || (y.isFinite() && !y.isInteger())) { throw new Error('Parameters in function bitAnd must be integer numbers'); } var BigNumber = x.constructor; if (x.isNaN() || y.isNaN()) { return new BigNumber(NaN); } if (x.isZero() || y.eq(-1) || x.eq(y)) { return x; } if (y.isZero() || x.eq(-1)) { return y; } if (!x.isFinite() || !y.isFinite()) { if (!x.isFinite() && !y.isFinite()) { if (x.isNegative() == y.isNegtive()) { return x; } return new BigNumber(0); } if (!x.isFinite()) { if (y.isNegative()) { return x; } if (x.isNegative()) { return new BigNumber(0); } return y; } if (!y.isFinite()) { if (x.isNegative()) { return y; } if (y.isNegative()) { return new BigNumber(0); } return x; } } return bitwise(x, y, function (a, b) { return a & b }); }; /* * Special Cases: * n << -n = N * n << N = N * N << n = N * n << 0 = n * 0 << n = 0 * I << I = N * I << n = I * n << I = I * * @param {BigNumber} value * @param {BigNumber} value * @return {BigNumber} Result of `x` << `y` * */ exports.leftShift = function (x, y) { if ((x.isFinite() && !x.isInteger()) || (y.isFinite() && !y.isInteger())) { throw new Error('Parameters in function leftShift must be integer numbers'); } var BigNumber = x.constructor; if (x.isNaN() || y.isNaN() || (y.isNegative() && !y.isZero())) { return new BigNumber(NaN); } if (x.isZero() || y.isZero()) { return x; } if (!x.isFinite() && !y.isFinite()) { return new BigNumber(NaN); } // Math.pow(2, y) is fully precise for y < 55, and fast if (y.lt(55)) { return x.times(Math.pow(2, y.toNumber()) + ''); } return x.times(new BigNumber(2).pow(y)); }; /* * @param {BigNumber} value * @return {BigNumber} Result of ~`x`, fully precise * */ exports.not = function (x) { if (x.isFinite() && !x.isInteger()) { throw new Error('Parameter in function bitNot must be integer numbers'); } var BigNumber = x.constructor; var prevPrec = BigNumber.precision; BigNumber.config({precision: 1E9}); var x = x.plus(BigNumber.ONE); x.s = -x.s || null; BigNumber.config({precision: prevPrec}); return x; }; /* * Special Cases: * N | n = N * n | 0 = n * n | -1 = -1 * n | n = n * I | I = I * -I | -I = -I * I | -n = -1 * I | -I = -1 * I | n = I * -I | n = -I * -I | -n = -n * * @param {BigNumber} value * @param {BigNumber} value * @return {BigNumber} Result of `x` | `y`, fully precise * */ exports.or = function (x, y) { if ((x.isFinite() && !x.isInteger()) || (y.isFinite() && !y.isInteger())) { throw new Error('Parameters in function bitOr must be integer numbers'); } var BigNumber = x.constructor; if (x.isNaN() || y.isNaN()) { return new BigNumber(NaN); } var negOne = new BigNumber(-1); if (x.isZero() || y.eq(negOne) || x.eq(y)) { return y; } if (y.isZero() || x.eq(negOne)) { return x; } if (!x.isFinite() || !y.isFinite()) { if ((!x.isFinite() && !x.isNegative() && y.isNegative()) || (x.isNegative() && !y.isNegative() && !y.isFinite())) { return negOne; } if (x.isNegative() && y.isNegative()) { return x.isFinite() ? x : y; } return x.isFinite() ? y : x; } return bitwise(x, y, function (a, b) { return a | b }); }; /* * Special Cases: * n >> -n = N * n >> N = N * N >> n = N * I >> I = N * n >> 0 = n * I >> n = I * -I >> n = -I * -I >> I = -I * n >> I = I * -n >> I = -1 * 0 >> n = 0 * * @param {BigNumber} value * @param {BigNumber} value * @return {BigNumber} Result of `x` >> `y` * */ exports.rightShift = function (x, y) { if ((x.isFinite() && !x.isInteger()) || (y.isFinite() && !y.isInteger())) { throw new Error('Parameters in function rightArithShift must be integer numbers'); } var BigNumber = x.constructor; if (x.isNaN() || y.isNaN() || (y.isNegative() && !y.isZero())) { return new BigNumber(NaN); } if (x.isZero() || y.isZero()) { return x; } if (!y.isFinite()) { if (x.isNegative()) { return new BigNumber(-1); } if (!x.isFinite()) { return new BigNumber(NaN); } return new BigNumber(0); } // Math.pow(2, y) is fully precise for y < 55, and fast if (y.lt(55)) { return x.div(Math.pow(2, y.toNumber()) + '').floor(); } return x.div(new BigNumber(2).pow(y)).floor(); }; /* * Special Cases: * N ^ n = N * n ^ 0 = n * n ^ n = 0 * n ^ -1 = ~n * I ^ n = I * I ^ -n = -I * I ^ -I = -1 * -I ^ n = -I * -I ^ -n = I * * @param {BigNumber} value * @param {BigNumber} value * @return {BigNumber} Result of `x` ^ `y`, fully precise * */ exports.xor = function (x, y) { if ((x.isFinite() && !x.isInteger()) || (y.isFinite() && !y.isInteger())) { throw new Error('Parameters in function bitXor must be integer numbers'); } var BigNumber = x.constructor; if (x.isNaN() || y.isNaN()) { return new BigNumber(NaN); } if (x.isZero()) { return y; } if (y.isZero()) { return x; } if (x.eq(y)) { return new BigNumber(0); } var negOne = new BigNumber(-1); if (x.eq(negOne)) { return exports.not(y); } if (y.eq(negOne)) { return exports.not(x); } if (!x.isFinite() || !y.isFinite()) { if (!x.isFinite() && !y.isFinite()) { return negOne; } return new BigNumber(x.isNegative() == y.isNegative() ? Infinity : -Infinity); } return bitwise(x, y, function (a, b) { return a ^ b }); }; /* Applies bitwise function to numbers. */ function bitwise(x, y, func) { var BigNumber = x.constructor; var xBits, yBits; var xSign = +(x.s < 0); var ySign = +(y.s < 0); if (xSign) { xBits = decCoefficientToBinaryString(exports.not(x)); for (var i = 0; i < xBits.length; ++i) { xBits[i] ^= 1; } } else { xBits = decCoefficientToBinaryString(x); } if (ySign) { yBits = decCoefficientToBinaryString(exports.not(y)); for (var i = 0; i < yBits.length; ++i) { yBits[i] ^= 1; } } else { yBits = decCoefficientToBinaryString(y); } var minBits, maxBits, minSign; if (xBits.length <= yBits.length) { minBits = xBits; maxBits = yBits; minSign = xSign; } else { minBits = yBits; maxBits = xBits; minSign = ySign; } var shortLen = minBits.length; var longLen = maxBits.length; var expFuncVal = func(xSign, ySign) ^ 1; var outVal = new BigNumber(expFuncVal ^ 1); var twoPower = BigNumber.ONE; var two = new BigNumber(2); var prevPrec = BigNumber.precision; BigNumber.config({precision: 1E9}); while (shortLen > 0) { if (func(minBits[--shortLen], maxBits[--longLen]) == expFuncVal) { outVal = outVal.plus(twoPower); } twoPower = twoPower.times(two); } while (longLen > 0) { if (func(minSign, maxBits[--longLen]) == expFuncVal) { outVal = outVal.plus(twoPower); } twoPower = twoPower.times(two); } BigNumber.config({precision: prevPrec}); if (expFuncVal == 0) { outVal.s = -outVal.s; } return outVal; } /* Extracted from decimal.js, and edited to specialize. */ function decCoefficientToBinaryString(x) { // Convert to string var a = x.c; var r = a[0] + ''; for (var i = 1; i < a.length; ++i) { var s = a[i] + ''; for (var z = 7 - s.length; z--; ) { s = '0' + s; } r += s; } var j; for (j = r.length - 1; r.charAt(j) == '0'; --j); var xe = x.e; var str = r.slice(0, j + 1 || 1); var strL = str.length; if (xe > 0) { if (++xe > strL) { // Append zeros. for (xe -= strL; xe--; str += '0'); } else if (xe < strL) { str = str.slice(0, xe) + '.' + str.slice(xe); } } // Convert from base 10 (decimal) to base 2 var arr = [0]; for (var i = 0; i < str.length; ) { for (var arrL = arr.length; arrL--; arr[arrL] *= 10); arr[0] += str.charAt(i++) << 0; // convert to int for (var j = 0; j < arr.length; ++j) { if (arr[j] > 1) { if (arr[j + 1] == null) { arr[j + 1] = 0; } arr[j + 1] += arr[j] >> 1; arr[j] &= 1; } } } return arr.reverse(); } /************************************* * Trigonometric functions * *************************************/ /** * Calculate the arccosine or arcsecant of x * * acos(x) = 2*atan(sqrt(1-x^2)/(1+x)) * * asec(x) = acos(1/x) * * @param {BigNumber} x * @param {DecimalFactory} Big current BigNumber constructor * @param {Boolean} reciprocal is sec * @returns {BigNumber} arccosine or arcsecant of x */ exports.arccos_arcsec = function (x, Big, reciprocal) { var precision = Big.precision; if (reciprocal) { if (x.abs().lt(Big.ONE)) { throw new Error('asec() only has non-complex values for |x| >= 1.'); } } else if (x.abs().gt(Big.ONE)) { throw new Error('acos() only has non-complex values for |x| <= 1.'); } if (x.eq(-1)) { return exports.pi(precision); } Big.config({precision: precision + 4}); if (reciprocal) { x = Big.ONE.div(x); } var acos = exports.arctan_arccot(Big.ONE.minus(x.times(x)).sqrt() .div(x.plus(Big.ONE)), Big).times(2); Big.config({precision: precision}); return acos.toDP(precision - 1); }; /** * Calculate the arcsine or arccosecant of x * * @param {BigNumber} x * @param {DecimalFactory} Big current BigNumber constructor * @param {Boolean} reciprocal is csc * @returns {BigNumber} arcsine or arccosecant of x */ exports.arcsin_arccsc = function (x, Big, reciprocal) { if (x.isNaN()) { return new Big(NaN); } var precision = Big.precision; var absX = x.abs(); if (reciprocal) { if (absX.lt(Big.ONE)) { throw new Error('acsc() only has non-complex values for |x| >= 1.'); } Big.config({precision: precision + 2}); x = Big.ONE.div(x); Big.config({precision: precision}); absX = x.abs(); } else if (absX.gt(Big.ONE)) { throw new Error('asin() only has non-complex values for |x| <= 1.'); } // Get x below 0.58 if (absX.gt(0.8)) { Big.config({precision: precision + 4}); // arcsin(x) = sign(x)*(Pi/2 - arcsin(sqrt(1 - x^2))) var sign = x.s; var halfPi = exports.pi(precision + 4).div(2); x = halfPi.minus(exports.arcsin_arccsc(Big.ONE.minus(x.times(x)).sqrt(), Big)); x.s = sign; x.constructor = Big; Big.config({precision: precision}); return x.toDP(precision - 1); } var wasReduced = absX.gt(0.58); if (wasReduced) { Big.config({precision: precision + 8}); // arcsin(x) = 2*arcsin(x / (sqrt(2)*sqrt(sqrt(1 - x^2) + 1))) x = x.div(new Big(2).sqrt().times(Big.ONE.minus(x.times(x)).sqrt() .plus(Big.ONE).sqrt())); Big.config({precision: precision}); } // Avoid overhead of Newton's Method if feasible var ret = (precision <= 60 || ((x.dp() <= Math.log(precision)) && x.lt(0.05))) ? arcsin_taylor(x, precision) : arcsin_newton(x, Big); if (wasReduced) { return ret.times(2); } return ret; }; /** * Calculate the arctangent or arccotangent of x * * @param {BigNumber} x * @param {DecimalFactory} Big current BigNumber constructor * @param {Boolean} reciprocal is cot * @returns {BigNumber} arctangent or arccotangent of x */ exports.arctan_arccot = function (x, Big, reciprocal) { if (x.isNaN()) { return new Big(NaN); } if ((!reciprocal && x.isZero()) || (reciprocal && !x.isFinite())) { return new Big(0); } var precision = Big.precision; if ((!reciprocal && !x.isFinite()) || (reciprocal && x.isZero())) { var halfPi = exports.pi(precision + 2).div(2).toDP(precision - 1); halfPi.constructor = Big; halfPi.s = x.s; return halfPi; } Big.config({precision: precision + 4}); if (reciprocal) { x = Big.ONE.div(x); } var absX = x.abs(); if (absX.lte(0.875)) { var ret = arctan_taylor(x); ret.constructor = Big; Big.config({precision: precision}); return ret.toDP(Big.precision - 1); } if (absX.gte(1.143)) { // arctan(x) = sign(x)*((PI / 2) - arctan(1 / |x|)) var halfPi = exports.pi(precision + 4).div(2); var ret = halfPi.minus(arctan_taylor(Big.ONE.div(absX))); ret.s = x.s; ret.constructor = Big; Big.config({precision: precision}); return ret.toDP(Big.precision - 1); } // arctan(x) = arcsin(x / [sqrt(1 + x^2)]) x = x.div(x.times(x).plus(1).sqrt()); Big.config({precision: precision}); return exports.arcsin_arccsc(x, Big); }; /** * Calculate the arctangent of y, x * * @param {BigNumber} y * @param {BigNumber} x * @param {DecimalFactory} Big current BigNumber constructor * @returns {BigNumber} arctangent of y, x */ exports.arctan2 = function (y, x, Big) { var precision = Big.precision; if (x.isZero()) { if (y.isZero()) { return new Big(NaN); } var halfPi = exports.pi(precision + 2).div(2).toDP(precision - 1); halfPi.constructor = Big; halfPi.s = y.s; return halfPi; } Big.config({precision: precision + 2}); var ret = exports.arctan_arccot(y.div(x), Big, false); if (x.isNegative()) { var pi = exports.pi(precision + 2); ret = y.isNegative() ? ret.minus(pi) : ret.plus(pi); } ret.constructor = Big; Big.config({precision: precision}); return ret.toDP(precision - 1); }; /** * Calculate the hyperbolic arccosine, arcsine, arcsecant, or arccosecant of x * * acosh(x) = ln(x + sqrt(x^2 - 1)) * * asinh(x) = ln(x + sqrt(x^2 + 1)) * * asech(x) = acosh(1 / x) * * acsch(x) = asinh(1 / x) * * @param {BigNumber} x * @param {DecimalFactory} Big current BigNumber constructor * @param {Boolean} mode sine function if true, cosine function if false * @param {Boolean} reciprocal is sec or csc * @returns {BigNumber} hyperbolic arccosine, arcsine, arcsecant, or arccosecant of x */ exports.acosh_asinh_asech_acsch = function (x, Big, mode, reciprocal) { if (x.isNaN()) { return new Big(NaN); } if (reciprocal && x.isZero()) { return new Big(Infinity); } if (!mode) { if (reciprocal) { if (x.isNegative() || x.gt(Big.ONE)) { throw new Error('asech() only has non-complex values for 0 <= x <= 1.'); } } else if (x.lt(Big.ONE)) { throw new Error('acosh() only has non-complex values for x >= 1.'); } } var precision = Big.precision; Big.config({precision: precision + 4}); var y = new Big(x); y.constructor = Big; if (reciprocal) { y = Big.ONE.div(y); } var x2PlusOrMinus = (mode) ? y.times(y).plus(Big.ONE) : y.times(y).minus(Big.ONE); var ret = y.plus(x2PlusOrMinus.sqrt()).ln(); Big.config({precision: precision}); return new Big(ret.toPrecision(precision)); }; /** * Calculate the hyperbolic arctangent or arccotangent of x * * atanh(x) = ln((1 + x)/(1 - x)) / 2 * * acoth(x) = atanh(1 / x) * * @param {BigNumber} x * @param {DecimalFactory} Big current BigNumber constructor * @param {Boolean} reciprocal is sec or csc * @returns {BigNumber} hyperbolic arctangent or arccotangent of x */ exports.atanh_acoth = function (x, Big, reciprocal) { if (x.isNaN()) { return new Big(NaN); } var absX = x.abs(); if (absX.eq(Big.ONE)) { return new Big(x.isNegative() ? -Infinity : Infinity); } if (absX.gt(Big.ONE)) { if (!reciprocal) { throw new Error('atanh() only has non-complex values for |x| <= 1.'); } } else if (reciprocal) { throw new Error('acoth() has complex values for |x| < 1.'); } if (x.isZero()) { return new Big(0); } var precision = Big.precision; Big.config({precision: precision + 4}); var y = new Big(x); y.constructor = Big; if (reciprocal) { y = Big.ONE.div(y); } var ret = Big.ONE.plus(y).div(Big.ONE.minus(y)).ln().div(2); Big.config({precision: precision}); return new Big(ret.toPrecision(precision)); }; /** * Calculate the cosine/sine of x using the multiple angle identity: * * cos(4x) = 8[cos(x)^4 - cos(x)^2] + 1 * * sin(5x) = 16sin(x)^5 - 20sin(x)^3 + 5sin(x) * http://www.tc.umn.edu/~ringx004/sidebar.html * * @param {BigNumber} x * @param {DecimalFactory} Big current BigNumber constructor * @param {Number} mode cosine function if 0, sine function if 1 * @param {Boolean} reciprocal is sec or csc * @returns {BigNumber} cosine, sine, secant, or cosecant of x */ exports.cos_sin_sec_csc = function (x, Big, mode, reciprocal) { if (x.isNaN() || !x.isFinite()) { return new Big(NaN); } var precision = Big.precision; // Avoid changing the original value var y = new Big(x); // sin(-x) == -sin(x), cos(-x) == cos(x) var isNeg = y.isNegative(); if (isNeg) { y.s = -y.s; } // Apply ~log(precision) guard bits var precPlusGuardDigits = precision + (Math.log(precision) | 0) + 3; Big.config({precision: precPlusGuardDigits}); y = reduceToPeriod(y, precPlusGuardDigits, mode); // Make this destructive y[0].constructor = Big; if (y[1]) { y = y[0]; if (reciprocal && y.isZero()) { y = new Big(Infinity); } Big.config({precision: precision}); return y; } var ret; y = y[0]; if (mode) { ret = cos_sin_taylor(y.div(3125), mode); Big.config({precision: Math.min(precPlusGuardDigits, precision + 15)}); var five = new Big(5); var sixteen = new Big(16); var twenty = new Big(20); for (var i = 0; i < 5; ++i) { var ret2 = ret.times(ret); var ret3 = ret2.times(ret); var ret5 = ret3.times(ret2); ret = sixteen.times(ret5).minus( twenty.times(ret3)).plus( five.times(ret)); } if (isNeg) { ret.s = -ret.s; } } else { var div_factor, loops; if (y.abs().lt(Big.ONE)) { div_factor = 64; loops = 3; } else { div_factor = 256; loops = 4; } ret = cos_sin_taylor(y.div(div_factor), mode); Big.config({precision: Math.min(precPlusGuardDigits, precision + 8)}); var eight = new Big(8); for (; loops > 0; --loops) { var ret2 = ret.times(ret); var ret4 = ret2.times(ret2); ret = eight.times(ret4.minus(ret2)).plus(Big.ONE); } } if (reciprocal) { ret = (ret.e <= -precision) ? new Big(Infinity) : Big.ONE.div(ret); } Big.config({precision: precision}); return ret.toDP(precision - 1); }; /** * Calculate the tangent of x * * tan(x) = sin(x) / cos(x) * * cot(x) = cos(x) / sin(x) * * @param {BigNumber} x * @param {DecimalFactory} Big current BigNumber constructor * @param {Boolean} reciprocal is cot * @returns {BigNumber} tangent or cotangent of x */ exports.tan_cot = function (x, Big, reciprocal) { if (x.isNaN()) { return new Big(NaN); } var precision = Big.precision; var pi = exports.pi(precision + 2); var halfPi = pi.div(2).toDP(precision - 1); pi = pi.toDP(precision - 1); var y = reduceToPeriod(x, precision, 1)[0]; if (y.abs().eq(pi)) { return new Big(Infinity); } Big.config({precision: precision + 4}); var sin = exports.cos_sin_sec_csc(y, Big, 1, false); var cos = sinToCos(sin); sin = sin.toDP(precision); cos = cos.toDP(precision); // Make sure sign for cosine is correct if (y.eq(x)) { if (y.gt(halfPi)) { cos.s = -cos.s; } } else if (pi.minus(y.abs()).gt(halfPi)) { cos.s = -cos.s; } var tan = (reciprocal) ? cos.div(sin) : sin.div(cos); Big.config({precision: precision}); return new Big(tan.toPrecision(precision)); }; /** * Calculate the hyperbolic sine, cosine, secant, or cosecant of x * * cosh(x) = (exp(x) + exp(-x)) / 2 * = (e^x + 1/e^x) / 2 * * sinh(x) = (exp(x) - exp(-x)) / 2 * = (e^x - 1/e^x) / 2 * * sech(x) = 2 / (exp(x) + exp(-x)) * = 2 / (e^x + 1/e^x) * * csch(x) = 2 / (exp(x) - exp(-x)) * = 2 / (e^x - 1/e^x) * * @param {BigNumber} x * @param {DecimalFactory} Big current BigNumber constructor * @param {Boolean} mode sinh function if true, cosh function if false * @param {Boolean} reciprocal is sech or csch * @returns {BigNumber} hyperbolic cosine, sine, secant. or cosecant of x */ exports.cosh_sinh_csch_sech = function (x, Big, mode, reciprocal) { if (x.isNaN()) { return new Big(NaN); } if (!x.isFinite()) { if (reciprocal) { return new Big(0); } return new Big((mode) ? x : Infinity); } var precision = Big.precision; Big.config({precision: precision + 4}); var y = new Big(x); y.constructor = Big; y = y.exp(); y = (mode) ? y.minus(Big.ONE.div(y)) : y.plus(Big.ONE.div(y)); y = (reciprocal) ? new Big(2).div(y) : y.div(2); Big.config({precision: precision}); return new Big(y.toPrecision(precision)); }; /** * Calculate the hyperbolic tangent of x * * tanh(x) = (exp(x) + exp(-x)) / (exp(x) - exp(-x)) * = (exp(2x) - 1) / (exp(2x) + 1) * = (e^x - 1/e^x) / (e^x + 1/e^x) * * coth(x) = (exp(x) - exp(-x)) / (exp(x) + exp(-x)) * = (exp(2x) + 1) / (exp(2x) - 1) * = (e^x + 1/e^x) / (e^x - 1/e^x) * * @param {BigNumber} x * @param {DecimalFactory} Big current BigNumber constructor * @param {Boolean} reciprocal is coth * @returns {BigNumber} hyperbolic tangent or cotangent of x */ exports.tanh_coth = function (x, Big, reciprocal) { if (x.isNaN()) { return new Big(NaN); } if (!x.isFinite()) { return new Big(x.s); } var precision = Big.precision; Big.config({precision: precision + 4}); var y = new Big(x); y.constructor = Big; var posExp = y.exp(); var negExp = Big.ONE.div(posExp); var ret = posExp.minus(negExp); ret = (reciprocal) ? posExp.plus(negExp).div(ret) : ret.div(posExp.plus(negExp)); Big.config({precision: precision}); return ret.toDP(precision - 1); }; /** * Calculate the arc sine of x using Newton's method * * f(x) = sin(x) = N => f(x) = sin(x) - N * f'(x) = cos(x) * * Thus we solve each step as follows: * x_(i+1) = x_i - (sin(x_i) - N)/cos(x_i) * * @param {BigNumber} x * @param {DecimalFactory} Big current BigNumber constructor * @returns {BigNumber} arc sine of x */ function arcsin_newton(x, Big) { var oldPrecision = Big.precision; // Calibration variables, adjusted from MAPM var tolerance = -(oldPrecision + 4); var maxp = oldPrecision + 8 - x.e; var localPrecision = 25 - x.e; var maxIter = Math.max(Math.log(oldPrecision + 2) * 1.442695 | 0 + 5, 5); Big.config({precision: localPrecision}); var i = 0; var curr = new Big(Math.asin(x.toNumber()) + ''); do { var tmp0 = exports.cos_sin_sec_csc(curr, Big, 1, false); var tmp1 = sinToCos(tmp0); if (!tmp0.isZero()) { tmp0.s = curr.s; } var tmp2 = tmp0.minus(x).div(tmp1); curr = curr.minus(tmp2); localPrecision = Math.min(2*localPrecision, maxp); Big.config({precision: localPrecision}); } while ((2*tmp2.e >= tolerance) && !tmp2.isZero() && (++i <= maxIter)) if (i == maxIter) { throw new Error('asin() failed to converge to the requested accuracy.' + 'Try with a higher precision.'); } Big.config({precision: oldPrecision}); return curr.toDP(oldPrecision - 1); } /** * Calculate the arc sine of x * * arcsin(x) = x + (1/2)*x^3/3 + (3/8)*x^5/5 + (15/48)*x^7/7 ... * = x + (1/2)*x^2*x^1/3 + [(1*3)/(2*4)]*x^2*x^3/5 + [(1*3*5)/(2*4*6)]*x^2*x^5/7 ... * * @param {BigNumber} x * @param {Number} precision * @returns {BigNumber} arc sine of x */ function arcsin_taylor(x, precision) { var Big = x.constructor; Big.config({precision: precision + Math.log(precision) | 0 + 4}); var one = new Big(1); var y = x; var yPrev = NaN; var x2 = x.times(x); var polyNum = x; var constNum = new Big(one); var constDen = new Big(one); var bigK = new Big(one); for (var k = 3; !y.equals(yPrev); k += 2) { polyNum = polyNum.times(x2); constNum = constNum.times(bigK); constDen = constDen.times(bigK.plus(one)); yPrev = y; bigK = new Big(k); y = y.plus(polyNum.times(constNum).div(bigK.times(constDen))); } Big.config({precision: precision}); return y.toDP(precision - 1); } /** * Calculate the arc tangent of x using a Taylor expansion * * arctan(x) = x - x^3/3 + x^5/5 - x^7/7 + x^9/9 - ... * = x - x^2*x^1/3 + x^2*x^3/5 - x^2*x^5/7 + x^2*x^7/9 - ... * * @param {BigNumber} x * @returns {BigNumber} arc tangent of x */ function arctan_taylor(x) { var y = x; var yPrev = NaN; var x2 = x.times(x); var num = x; var add = true; for (var k = 3; !y.equals(yPrev); k += 2) { num = num.times(x2); yPrev = y; add = !add; y = (add) ? y.plus(num.div(k)) : y.minus(num.div(k)); } return y; } /** * Calculate the cosine or sine of x using Taylor Series. * * cos(x) = 1 - x^2/2! + x^4/4! - x^6/6! + x^8/8! - ... * = 1 - 1*x^2/2! + x^2*x^2/4! - x^2*x^4/6! + x^2*x^6/8! - ... * * sin(x) = x - x^3/3! + x^5/5! - x^7/7! + x^9/9! - ... * = x - x^2*x^1/3! + x^2*x^3/5! - x^2*x^5/7! + x^2*x^7/9! - ... * * @param {BigNumber} x reduced argument * @param {Number} mode sine function if 1, cosine function if 0 * @returns {BigNumber} sine or cosine of x */ function cos_sin_taylor(x, mode) { var one = x.constructor.ONE; var y = x; var yPrev = NaN; var x2 = x.times(x); var num = (mode) ? y : y = one; var den = one; var add = true; for (var k = mode; !y.equals(yPrev); k += 2) { num = num.times(x2); den = den.times(k+1).times(k+2); yPrev = y; add = !add; y = (add) ? y.plus(num.div(den)) : y.minus(num.div(den)); } return y; } /** * Reduce x within a period of pi (0, pi] with guard digits. * * @param {BigNumber} x * @param {Number} precision * @param {Number} mode * @returns {Array} [Reduced x, is tau multiple?] */ function reduceToPeriod(x, precision, mode) { var pi = exports.pi(precision + 2); var tau = exports.tau(precision); if (x.abs().lte(pi.toDP(x.dp()))) { return [x, false]; } var Big = x.constructor; // Catch if input is tau multiple using pi's precision if (x.div(pi.toDP(x.dp())).toNumber() % 2 == 0) { return [new Big(mode ^ 1), true]; } var y = x.mod(tau); // Catch if tau multiple with tau's precision if (y.toDP(x.dp(), 1).isZero()) { return [new Big(mode ^ 1), true]; } if (y.gt(pi)) { if (mode) { // sin(x + pi) = -sin(x) y = y.minus(pi); y.s = -y.s; } else { // cos(x) = cos(tau - x) y = tau.minus(y); } } y.constructor = Big; return [y, false]; } /** * Convert from sine to cosine * * |cos(x)| = sqrt(1 - sin(x)^2) * * @param {BigNumber} sine of x * @returns {BigNumber} sine as cosine */ function sinToCos(sinVal) { var Big = sinVal.constructor; var precision = Big.precision; Big.config({precision: precision + 2}); var ret = Big.ONE.minus(sinVal.times(sinVal)).sqrt(); Big.config({precision: precision}); return ret.toDP(precision - 1); } /************************************ * Format functions * ************************************/ /** * Convert a number to a formatted string representation. * * Syntax: * * format(value) * format(value, options) * format(value, precision) * format(value, fn) * * Where: * * {Number} value The value to be formatted * {Object} options An object with formatting options. Available options: * {String} notation * Number notation. Choose from: * 'fixed' Always use regular number notation. * For example '123.40' and '14000000' * 'exponential' Always use exponential notation. * For example '1.234e+2' and '1.4e+7' * 'auto' (default) Regular number notation for numbers * having an absolute value between * `lower` and `upper` bounds, and uses * exponential notation elsewhere. * Lower bound is included, upper bound * is excluded. * For example '123.4' and '1.4e7'. * {Number} precision A number between 0 and 16 to round * the digits of the number. * In case of notations 'exponential' and * 'auto', `precision` defines the total * number of significant digits returned * and is undefined by default. * In case of notation 'fixed', * `precision` defines the number of * significant digits after the decimal * point, and is 0 by default. * {Object} exponential An object containing two parameters, * {Number} lower and {Number} upper, * used by notation 'auto' to determine * when to return exponential notation. * Default values are `lower=1e-3` and * `upper=1e5`. * Only applicable for notation `auto`. * {Function} fn A custom formatting function. Can be used to override the * built-in notations. Function `fn` is called with `value` as * parameter and must return a string. Is useful for example to * format all values inside a matrix in a particular way. * * Examples: * * format(6.4); // '6.4' * format(1240000); // '1.24e6' * format(1/3); // '0.3333333333333333' * format(1/3, 3); // '0.333' * format(21385, 2); // '21000' * format(12.071, {notation: 'fixed'}); // '12' * format(2.3, {notation: 'fixed', precision: 2}); // '2.30' * format(52.8, {notation: 'exponential'}); // '5.28e+1' * * @param {BigNumber} value * @param {Object | Function | Number} [options] * @return {String} str The formatted value */ exports.format = function(value, options) { if (typeof options === 'function') { // handle format(value, fn) return options(value); } // handle special cases if (!value.isFinite()) { return value.isNaN() ? 'NaN' : (value.gt(0) ? 'Infinity' : '-Infinity'); } // default values for options var notation = 'auto'; var precision = undefined; if (options !== undefined) { // determine notation from options if (options.notation) { notation = options.notation; } // determine precision from options if (isNumber(options)) { precision = options; } else if (options.precision) { precision = options.precision; } } // handle the various notations switch (notation) { case 'fixed': return exports.toFixed(value, precision); case 'exponential': return exports.toExponential(value, precision); case 'auto': // determine lower and upper bound for exponential notation. // TODO: implement support for upper and lower to be BigNumbers themselves var lower = 1e-3; var upper = 1e5; if (options && options.exponential) { if (options.exponential.lower !== undefined) { lower = options.exponential.lower; } if (options.exponential.upper !== undefined) { upper = options.exponential.upper; } } // adjust the configuration of the BigNumber constructor (yeah, this is quite tricky...) var oldConfig = { toExpNeg: value.constructor.toExpNeg, toExpPos: value.constructor.toExpPos }; value.constructor.config({ toExpNeg: Math.round(Math.log(lower) / Math.LN10), toExpPos: Math.round(Math.log(upper) / Math.LN10) }); // handle special case zero if (value.isZero()) return '0'; // determine whether or not to output exponential notation var str; var abs = value.abs(); if (abs.gte(lower) && abs.lt(upper)) { // normal number notation str = value.toSignificantDigits(precision).toFixed(); } else { // exponential notation str = exports.toExponential(value, precision); } // remove trailing zeros after the decimal point return str.replace(/((\.\d*?)(0+))($|e)/, function () { var digits = arguments[2]; var e = arguments[4]; return (digits !== '.') ? digits + e : e; }); default: throw new Error('Unknown notation "' + notation + '". ' + 'Choose "auto", "exponential", or "fixed".'); } }; /** * Format a number in exponential notation. Like '1.23e+5', '2.3e+0', '3.500e-3' * @param {BigNumber} value * @param {Number} [precision] Number of digits in formatted output. * If not provided, the maximum available digits * is used. * @returns {string} str */ exports.toExponential = function(value, precision) { if (precision !== undefined) { return value.toExponential(precision - 1); // Note the offset of one } else { return value.toExponential(); } }; /** * Format a number with fixed notation. * @param {BigNumber} value * @param {Number} [precision=0] Optional number of decimals after the * decimal point. Zero by default. */ exports.toFixed = function(value, precision) { return value.toFixed(precision || 0); // Note: the (precision || 0) is needed as the toFixed of BigNumber has an // undefined default precision instead of 0. };