UNPKG

nerdamer-ts

Version:

javascript light-weight symbolic math expression evaluator

402 lines 18.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.primeFactors = exports.pow = void 0; const Symbol_1 = require("../../../Types/Symbol"); const Groups_1 = require("../../../Types/Groups"); const Settings_1 = require("../../../Settings"); const Utils_1 = require("../../../Core/Utils"); const Errors_1 = require("../../../Core/Errors"); const add_1 = require("./add"); const decimal_js_1 = __importDefault(require("decimal.js")); const Frac_1 = require("../../../Types/Frac"); const log_1 = require("../math/log"); const expand_1 = require("../math/expand"); const abs_1 = require("../math/abs"); const Matrix_1 = require("../../../Types/Matrix"); const Math_consts_1 = require("../../Math.consts"); const index_1 = require("../index"); const Trig_1 = require("../../Trig"); const Parser_1 = require("../../../Parser/Parser"); /** * Gets called when the parser finds the ^ operator. See this.add * @param {Symbol} a * @param {Symbol} b * @returns {Symbol} */ function pow(a, b) { var aIsSymbol = (0, Utils_1.isSymbol)(a), bIsSymbol = (0, Utils_1.isSymbol)(b); if (aIsSymbol && bIsSymbol) { //it has units then it's the Unit module's problem if (a.unit || b.unit) { return deps.Unit.pow(a, b); } // Handle abs if (a.group === Groups_1.Groups.FN && a.fname === Settings_1.Settings.ABS && (0, Utils_1.even)(b)) { var m = a.multiplier.clone(); var raised = pow(a.args[0], b); raised.multiplier = m; return raised; } // Handle infinity if (a.isInfinity || b.isInfinity) { if (a.isInfinity && b.isInfinity) throw new Errors_1.UndefinedError('(' + a + ')^(' + b + ') is undefined!'); if (a.isConstant() && b.isInfinity) { if (a.equals(0)) { if (b.lessThan(0)) throw new Errors_1.UndefinedError('0^Infinity is undefined!'); return new Symbol_1.Symbol(0); } if (a.equals(1)) throw new Errors_1.UndefinedError('1^' + b.toString() + ' is undefined!'); //a^-oo if (b.lessThan(0)) return new Symbol_1.Symbol(0); //a^oo if (!a.lessThan(0)) return Symbol_1.Symbol.infinity(); } if (a.isInfinity && b.isConstant()) { if (b.equals(0)) throw new Errors_1.UndefinedError(a + '^0 is undefined!'); if (b.lessThan(0)) return new Symbol_1.Symbol(0); return (0, index_1.multiply)(Symbol_1.Symbol.infinity(), pow(new Symbol_1.Symbol(a.sign()), b.clone())); } } var aIsZero = a.equals(0); var bIsZero = b.equals(0); if (aIsZero && bIsZero) throw new Errors_1.UndefinedError('0^0 is undefined!'); // Return 0 right away if possible if (aIsZero && b.isConstant() && b.multiplier.greaterThan(0)) return new Symbol_1.Symbol(0); if (bIsZero) return new Symbol_1.Symbol(1); var bIsConstant = b.isConstant(), aIsConstant = a.isConstant(), bIsInt = b.isInteger(), m = a.multiplier, result = a.clone(); // 0^0, 1/0, etc. Complain. if (aIsConstant && bIsConstant && a.equals(0) && b.lessThan(0)) throw new Errors_1.UndefinedError('Division by zero is not allowed!'); // Compute imaginary numbers right away if (Settings_1.Settings.PARSE2NUMBER && aIsConstant && bIsConstant && a.sign() < 0 && (0, Utils_1.evenFraction)(b)) { var k, re, im; k = Math.PI * b; re = new Symbol_1.Symbol(Math.cos(k)); im = (0, index_1.multiply)(Symbol_1.Symbol.imaginary(), new Symbol_1.Symbol(Math.sin(k))); return (0, add_1.add)(re, im); } // Imaginary number under negative nthroot or to the n if (Settings_1.Settings.PARSE2NUMBER && a.isImaginary() && bIsConstant && (0, Utils_1.isInt)(b) && !b.lessThan(0)) { var re, im, r, theta, nre, nim, phi; re = a.realpart(); im = a.imagpart(); if (re.isConstant('all') && im.isConstant('all')) { phi = Settings_1.Settings.USE_BIG ? (0, Symbol_1.Symbol)(decimal_js_1.default.atan2(i.multiplier.toDecimal(), r.multiplier.toDecimal()).times(b.toString())) : Math.atan2(im, re) * b; theta = new Symbol_1.Symbol(phi); r = pow(Symbol_1.Symbol.hyp(re, im), b); nre = (0, index_1.multiply)(r.clone(), Trig_1.Trig.cos(theta.clone())); nim = (0, index_1.multiply)(r, Trig_1.Trig.sin(theta)); return (0, add_1.add)(nre, (0, index_1.multiply)(Symbol_1.Symbol.imaginary(), nim)); } } // Take care of the symbolic part result.toUnitMultiplier(); //simpifly sqrt if (result.group === Groups_1.Groups.FN && result.fname === Settings_1.Settings.SQRT && !bIsConstant) { var s = result.args[0]; s.multiplyPower(new Symbol_1.Symbol(0.5)); s.multiplier.multiply(result.multiplier); s.multiplyPower(b); result = s; } else { var sign = m.sign(); //handle cases such as (-a^3)^(1/4) if ((0, Utils_1.evenFraction)(b) && sign < 0) { // Swaperoo // First put the sign back on the symbol result.negate(); // Wrap it in brackets result = (0, Symbol_1.symfunction)(Settings_1.Settings.PARENTHESIS, [result]); // Move the sign back the exterior and let nerdamer handle the rest result.negate(); } result.multiplyPower(b); } if (aIsConstant && bIsConstant && Settings_1.Settings.PARSE2NUMBER) { var c; //remove the sign if (sign < 0) { a.negate(); if (b.multiplier.den.equals(2)) //we know that the numerator has to be odd and therefore it's i c = new Symbol_1.Symbol(Settings_1.Settings.IMAGINARY); else if ((0, Utils_1.isInt)(b.multiplier)) { if ((0, Utils_1.even)(b.multiplier)) c = new Symbol_1.Symbol(1); else c = new Symbol_1.Symbol(-1); } else if (!(0, Utils_1.even)(b.multiplier.den)) { c = new Symbol_1.Symbol(Math.pow(sign, b.multiplier.num)); } else { c = pow((0, Symbol_1.symfunction)(Settings_1.Settings.PARENTHESIS, [new Symbol_1.Symbol(sign)]), b.clone()); } } result = new Symbol_1.Symbol(Math.pow(a.multiplier.toDecimal(), b.multiplier.toDecimal())); //result = new Symbol(Math2.bigpow(a.multiplier, b.multiplier)); //put the back sign if (c) result = (0, index_1.multiply)(result, c); } else if (bIsInt && !m.equals(1)) { var abs_b = b.abs(); // Provide fall back to JS until big number implementation is improved if (abs_b.gt(Settings_1.Settings.MAX_EXP)) { if (b.sign() < 0) return new Symbol_1.Symbol(0); return Symbol_1.Symbol.infinity(); } else { var p = b.multiplier.toDecimal(); var sgn = Math.sign(p); p = Math.abs(p); var multiplier = new Frac_1.Frac(1); multiplier.num = m.num.pow(p); multiplier.den = m.den.pow(p); if (sgn < 0) multiplier.invert(); //multiplying is justified since after mulltiplyPower if it was of group Groups.P it will now be of group Groups.N result.multiplier = result.multiplier.multiply(multiplier); } } else { var sign = a.sign(); if (b.isConstant() && a.isConstant() && !b.multiplier.den.equals(1) && sign < 0) { //we know the sign is negative so if the denominator for b == 2 then it's i if (b.multiplier.den.equals(2)) { var i = new Symbol_1.Symbol(Settings_1.Settings.IMAGINARY); a.negate(); //remove the sign //if the power is negative then i is negative if (b.lessThan(0)) { i.negate(); b.negate(); //remove the sign from the power } //pull the power normally and put back the imaginary result = (0, index_1.multiply)(pow(a, b), i); } else { var aa = a.clone(); aa.multiplier.negate(); result = pow((0, Symbol_1.symfunction)(Settings_1.Settings.PARENTHESIS, [new Symbol_1.Symbol(sign)]), b.clone()); var _a = pow(new Symbol_1.Symbol(aa.multiplier.num), b.clone()); var _b = pow(new Symbol_1.Symbol(aa.multiplier.den), b.clone()); var r = (0, index_1.divide)(_a, _b); result = (0, index_1.multiply)(result, r); } } else if (Settings_1.Settings.PARSE2NUMBER && b.isImaginary()) { //4^(i + 2) = e^(- (2 - 4 i) π n + (2 + i) log(4)) var re = b.realpart(); var im = b.imagpart(); /* if (b.group === CP && false) { var ex = pow(a.clone(), re); var xi = multiply(multiply(ex.clone(), trig.sin(im.clone())), Symbol.imaginary()); var xa = multiply(trig.cos(im), ex); result = add(xi, xa); } else { */ var aa = a.clone().toLinear(); var a1 = pow(aa.clone(), re); var log_a = (0, log_1.log)(aa.clone()); var b1 = Trig_1.Trig.cos((0, index_1.multiply)(im.clone(), log_a)); var c1 = (0, index_1.multiply)(Trig_1.Trig.sin((0, index_1.multiply)(im, (0, log_1.log)(aa))), Symbol_1.Symbol.imaginary()); result = (0, index_1.multiply)(a1, (0, add_1.add)(b1, c1)); result = (0, expand_1.expand)((0, Parser_1.parse)(result)); /* } */ } else { //b is a symbol var neg_num = a.group === Groups_1.Groups.N && sign < 0, num = testSQRT(new Symbol_1.Symbol(neg_num ? m.num : Math.abs(m.num)).setPower(b.clone())), den = testSQRT(new Symbol_1.Symbol(m.den).setPower(b.clone()).invert()); //eliminate imaginary if possible if (a.imaginary) { if (bIsInt) { var s, p, n; s = Math.sign(b); p = (0, abs_1.abs)(b); n = p % 4; result = new Symbol_1.Symbol((0, Utils_1.even)(n) ? -1 : Settings_1.Settings.IMAGINARY); if (n === 0 || s < 0 && (n === 1) || s > 0 && (n === 3)) { result.negate(); } } else { //assume i = sqrt(-1) -> (-1)^(1/2) var nr = b.multiplier.multiply(Frac_1.Frac.quick(1, 2)), //the denominator denotes the power so raise to it. It will turn positive it round tn = Math.pow(-1, nr.num); result = (0, Utils_1.even)(nr.den) ? new Symbol_1.Symbol(-1).setPower(nr, true) : new Symbol_1.Symbol(tn); } } //ensure that the sign is carried by the symbol and not the multiplier //this enables us to check down the line if the multiplier can indeed be transferred if (sign < 0 && !neg_num) result.negate(); //retain the absolute value if (bIsConstant && a.group !== Groups_1.Groups.EX) { var evenr = (0, Utils_1.even)(b.multiplier.den), evenp = (0, Utils_1.even)(a.power), n = result.power.toDecimal(), evennp = (0, Utils_1.even)(n); if (evenr && evenp && !evennp) { if (n === 1) result = (0, Symbol_1.symfunction)(Settings_1.Settings.ABS, [result]); else if (!(0, Utils_1.isInt)(n)) { var p = result.power; result = (0, Symbol_1.symfunction)(Settings_1.Settings.ABS, [result.toLinear()]).setPower(p); } else { result = (0, index_1.multiply)((0, Symbol_1.symfunction)(Settings_1.Settings.ABS, [result.clone().toLinear()]), result.clone().setPower(new Frac_1.Frac(n - 1))); } //quick workaround. Revisit if (Settings_1.Settings.POSITIVE_MULTIPLIERS && result.fname === Settings_1.Settings.ABS) result = result.args[0]; } } //multiply out sqrt if (b.equals(2) && result.group === Groups_1.Groups.CB) { var _result = new Symbol_1.Symbol(1); result.each(function (sym) { _result = (0, index_1.multiply)(_result, pow(sym, b)); }); result = _result; } } } result = testSQRT(result); // Don't multiply until we've tested the remaining symbol if (num && den) { result = (0, index_1.multiply)(result, testPow((0, index_1.multiply)(num, den))); } // Reduce square root if (result.fname === Settings_1.Settings.SQRT) { var isEX = result.group === Groups_1.Groups.EX; var t = isEX ? result.power.multiplier.toString() : result.power.toString(); if ((0, Utils_1.even)(t)) { var pt = isEX ? (0, index_1.divide)(result.power, new Symbol_1.Symbol(2)) : new Symbol_1.Symbol(result.power.divide(new Frac_1.Frac(2))), m = result.multiplier; result = pow(result.args[0], pt); result.multiplier = result.multiplier.multiply(m); } } // Detect Euler's identity else if (!Settings_1.Settings.IGNORE_E && result.isE() && result.group === Groups_1.Groups.EX && result.power.contains('pi') && result.power.contains(Settings_1.Settings.IMAGINARY) && b.group === Groups_1.Groups.CB) { var theta = b.stripVar(Settings_1.Settings.IMAGINARY); result = (0, add_1.add)(Trig_1.Trig.cos(theta), (0, index_1.multiply)(Symbol_1.Symbol.imaginary(), Trig_1.Trig.sin(theta))); } return result; } else { if ((0, Utils_1.isVector)(a) && bIsSymbol) { a = a.map(function (x) { return pow(x, b.clone()); }); } else if ((0, Utils_1.isMatrix)(a) && bIsSymbol) { var M = new Matrix_1.Matrix(); a.eachElement(function (x, i, j) { M.set(i, j, pow(x, b.clone())); }); a = M; } else if (aIsSymbol && (0, Utils_1.isMatrix)(b)) { var M = new Matrix_1.Matrix(); b.eachElement(function (x, i, j) { M.set(i, j, pow(a.clone(), x)); }); a = M; } return a; } } exports.pow = pow; function testSQRT(symbol) { //wrap the symbol in sqrt. This eliminates one more check down the line. if (!(0, Utils_1.isSymbol)(symbol.power) && symbol.power.absEquals(0.5)) { var sign = symbol.power.sign(); //don't devide the power directly. Notice the use of toString. This makes it possible //to use a bigNumber library in the future var retval = (0, index_1.sqrt)(symbol.group === Groups_1.Groups.P ? new Symbol_1.Symbol(symbol.value) : symbol.toLinear()); //place back the sign of the power if (sign < 0) retval.invert(); return retval; } return symbol; } //try to reduce a symbol by pulling its power function testPow(symbol) { if (symbol.group === Groups_1.Groups.P) { var v = symbol.value; var fct = primeFactors(v)[0]; //safety if (!fct) { (0, Utils_1.warn)('Unable to compute prime factors. This should not happen. Please review and report.'); return symbol; } var n = new Frac_1.Frac(Math.log(v) / Math.log(fct)), p = n.multiply(symbol.power); //we don't want a more complex number than before if (p.den > symbol.power.den) return symbol; if ((0, Utils_1.isInt)(p)) symbol = new Symbol_1.Symbol(Math.pow(fct, p)); else symbol = new Symbol_1.Symbol(fct).setPower(p); } return symbol; } /** * Calculates prime factors for a number. It first checks if the number * is a prime number. If it's not then it will calculate all the primes * for that number. * @param {int} num * @returns {Array} */ function primeFactors(num) { if ((0, Utils_1.isPrime)(num)) { return [num]; } var l = num, i = 1, factors = [], epsilon = 2.2204460492503130808472633361816E-16; while (i < l) { var quotient = num / i; var whole = Math.floor(quotient); var remainder = quotient - whole; if (remainder <= epsilon && i > 1) { // If the prime wasn't found but calculated then save it and // add it as a factor. if ((0, Utils_1.isPrime)(i)) { if (Math_consts_1.PRIMES.indexOf(i) === -1) { Math_consts_1.PRIMES.push(i); } factors.push(i); } // Check if the remainder is a prime if ((0, Utils_1.isPrime)(whole)) { factors.push(whole); break; } l = whole; } i++; } return factors.sort(function (a, b) { return a - b; }); } exports.primeFactors = primeFactors; //# sourceMappingURL=pow.js.map