2015-03-04 13:53:32 -05:00

194 lines
5.1 KiB
JavaScript

'use strict';
module.exports = function (math) {
var util = require('../../util/index');
var BigNumber = math.type.BigNumber;
var collection = math.collection;
var isNumber = util.number.isNumber;
var isBoolean = util['boolean'].isBoolean;
var isCollection = collection.isCollection;
/**
* Calculate the nth root of a value.
* The principal nth root of a positive real number A, is the positive real
* solution of the equation
*
* x^root = A
*
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.nthRoot(a, root)
*
* Examples:
*
* math.nthRoot(9, 2); // returns 3, as 3^2 == 9
* math.sqrt(9); // returns 3, as 3^2 == 9
* math.nthRoot(64, 3); // returns 4, as 4^3 == 64
*
* See also:
*
* sqrt, pow
*
* @param {Number | BigNumber | Boolean | Array | Matrix | null} a
* Value for which to calculate the nth root
* @param {Number | BigNumber | Boolean | null} [root=2] The root.
* @return {Number | Complex | Array | Matrix} Returns the nth root of `a`
*/
math.nthRoot = function nthRoot (a, root) {
if (arguments.length != 1 && arguments.length != 2) {
throw new math.error.ArgumentsError('nthRoot', arguments.length, 1, 2);
}
switch(arguments.length) {
case 1:
if (isNumber(a)) {
return _nthRoot(a);
}
else if (a instanceof BigNumber) {
return _nthRootBig(a);
}
else if (isCollection(a)) {
return collection.deepMap(x, nthRoot);
}
if (isBoolean(a) || a === null) {
return nthRoot(+a);
}
break;
case 2:
if (isNumber(a)) {
if (isNumber(root)) {
return _nthRoot(a, root);
}
else if (root instanceof BigNumber) {
// try to convert to bignumber
a = BigNumber.convert(a);
if (a instanceof BigNumber) {
return _nthRootBig(a, root);
}
else {
// downgrade to number
return _nthRoot(a, root.toNumber());
}
}
}
else if (a instanceof BigNumber) {
// try to convert to bignumber
if (isNumber(root)) {
root = BigNumber.convert(root);
}
if (root instanceof BigNumber) {
return _nthRootBig(a, root);
}
else {
// downgrade to number
return _nthRoot(a.toNumber(), root);
}
}
else if (isCollection(a) && !isCollection(root)) {
return collection.deepMap2(a, root, nthRoot);
}
if (isBoolean(a) || a === null) {
return nthRoot(+a, root);
}
if (isBoolean(root) || root === null) {
return nthRoot(a, +root);
}
break;
default:
throw new math.error.ArgumentsError('nthRoot', arguments.length, 1, 2);
}
if (isBoolean(x) || x === null) {
return arguments.length == 2 ? nthRoot(+x, n) : nthRoot(+x);
}
throw new math.error.UnsupportedTypeError('nthRoot', math['typeof'](a), math['typeof'](root));
};
/**
* Calculate the nth root of a, solve x^root == a
* http://rosettacode.org/wiki/Nth_root#JavaScript
* @param {number} a
* @param {number} [root=2]
* @private
*/
function _nthRoot(a, root) {
var _root = (root != undefined) ? root : 2;
var inv = _root < 0;
if (inv) _root = -_root;
if (_root == 0) throw new Error('Root must be non-zero');
if (a < 0 && (Math.abs(_root) % 2 != 1)) throw new Error('Root must be odd when a is negative.');
// edge cases zero and infinity
if (a == 0) return 0;
if (!Number.isFinite(a)) {
return inv ? 0 : a;
}
var epsilon = 1e-16;
var x = 1; // Initial guess
var i = 0;
var iMax = 100;
do {
var delta = (a / Math.pow(x, _root - 1) - x) / _root;
x = x + delta;
i++;
}
while (Math.abs(delta) > epsilon && i < iMax);
return inv ? 1 / x : x;
}
/**
* Calculate the nth root of a for BigNumbers, solve x^root == a
* http://rosettacode.org/wiki/Nth_root#JavaScript
* @param {BigNumber} a
* @param {BigNumber} [root=2]
* @private
*/
function _nthRootBig(a, root) {
var _root = (root != undefined) ? root : new BigNumber(2);
var zero = new BigNumber(0);
var one = new BigNumber(1);
var inv = _root.isNegative();
if (inv) _root = _root.negated();
if (_root.isZero()) throw new Error('Root must be non-zero');
if (a.isNegative() && !_root.abs().mod(2).equals(1)) throw new Error('Root must be odd when a is negative.');
// edge cases zero and infinity
if (a.isZero()) return zero;
if (!a.isFinite())
{
return inv ? zero : a;
}
var x = one; // Initial guess
var i = 0;
var iMax = 100;
do {
var xPrev = x;
var delta = a.div(x.pow(_root.minus(1))).minus(x).div(_root);
x = x.plus(delta);
i++;
}
while (!x.equals(xPrev) && i < iMax);
return inv ? one.div(x) : x;
}
};