nerdamer-ts
Version:
javascript light-weight symbolic math expression evaluator
402 lines • 18.9 kB
JavaScript
"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