UNPKG

nerdamer-ts

Version:

javascript light-weight symbolic math expression evaluator

334 lines 16.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.multiply = void 0; const Symbol_1 = require("../../../Types/Symbol"); const Collection_1 = require("../../../Parser/Collection"); const Errors_1 = require("../../../Core/Errors"); const Settings_1 = require("../../../Settings"); const decimal_js_1 = __importDefault(require("decimal.js")); const Groups_1 = require("../../../Types/Groups"); const Frac_1 = require("../../../Types/Frac"); const divide_1 = require("./divide"); const Utils_1 = require("../../../Core/Utils"); const subtract_1 = require("./subtract"); const add_1 = require("./add"); const pow_1 = require("./pow"); const Text_1 = require("../../../Core/Text"); const Matrix_1 = require("../../../Types/Matrix"); const index_1 = require("../index"); 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 multiply(a, b) { var aIsSymbol = (0, Utils_1.isSymbol)(a), bIsSymbol = (0, Utils_1.isSymbol)(b); //we're dealing with function assignment here if (aIsSymbol && b instanceof Collection_1.Collection) { b.elements.push(a); return b; } if (aIsSymbol && bIsSymbol) { //if it has a unit then add it and return it right away. if (b.isUnit) { var result = a.clone(); a.unit = b; return result; } //if it has units then just forward that problem to the unit module if (a.unit || b.unit) { return deps.Unit.multiply(a, b); } //handle Infinty if (a.isInfinity || b.isInfinity) { if (a.equals(0) || b.equals(0)) throw new Errors_1.UndefinedError(a + '*' + b + ' is undefined!'); //x/infinity if (b.power.lessThan(0)) { if (!a.isInfinity) { return new Symbol_1.Symbol(0); } else { throw new Errors_1.UndefinedError('Infinity/Infinity is not defined!'); } } var sign = a.multiplier.multiply(b.multiplier).sign(), inf = Symbol_1.Symbol.infinity(); if (a.isConstant() || b.isConstant() || (a.isInfinity && b.isInfinity)) { if (sign < 0) inf.negate(); return inf; } } //the quickies if (a.isConstant() && b.isConstant() && Settings_1.Settings.PARSE2NUMBER) { var t = new decimal_js_1.default(a.multiplier.toDecimal()).times(new decimal_js_1.default(b.multiplier.toDecimal())).toFixed(); var retval = new Symbol_1.Symbol(t); return retval; } //don't waste time if (a.isOne()) { return b.clone(); } if (b.isOne()) { return a.clone(); } if (a.multiplier.equals(0) || b.multiplier.equals(0)) return new Symbol_1.Symbol(0); if (b.group > a.group && !(b.group === Groups_1.Groups.CP)) return multiply(b, a); //correction for PL/CB dilemma if (a.group === Groups_1.Groups.CB && b.group === Groups_1.Groups.PL && a.value === b.value) { var t = a; a = b; b = t; //swap } var g1 = a.group, g2 = b.group, bnum = b.multiplier.num, bden = b.multiplier.den; if (g1 === Groups_1.Groups.FN && a.fname === Settings_1.Settings.SQRT && !b.isConstant() && a.args[0].value === b.value && !a.args[0].multiplier.lessThan(0)) { //unwrap sqrt var a_pow = a.power; var a_multiplier = (0, Parser_1.parse)(a.multiplier); a = multiply(a_multiplier, a.args[0].clone()); a.setPower(new Frac_1.Frac(0.5).multiply(a_pow)); g1 = a.group; } //simplify n/sqrt(n). Being very specific else if (g1 === Groups_1.Groups.FN && a.fname === Settings_1.Settings.SQRT && a.multiplier.equals(1) && a.power.equals(-1) && b.isConstant() && a.args[0].equals(b)) { a = (0, Symbol_1.symfunction)(Settings_1.Settings.SQRT, [b.clone()]); b = new Symbol_1.Symbol(1); } ; var v1 = a.value, v2 = b.value, sign = new Frac_1.Frac(a.sign()), //since Groups.P is just a morphed version of Groups.N we need to see if they relate ONN = (g1 === Groups_1.Groups.P && g2 === Groups_1.Groups.N && b.multiplier.equals(a.value)), //don't multiply the multiplier of b since that's equal to the value of a m = ONN ? new Frac_1.Frac(1).multiply(a.multiplier).abs() : a.multiplier.multiply(b.multiplier).abs(), result = a.clone().toUnitMultiplier(); b = b.clone().toUnitMultiplier(true); //further simplification of sqrt if (g1 === Groups_1.Groups.FN && g2 === Groups_1.Groups.FN) { var u = a.args[0].clone(); var v = b.args[0].clone(); if (a.fname === Settings_1.Settings.SQRT && b.fname === Settings_1.Settings.SQRT && a.isLinear() && b.isLinear()) { var q = (0, divide_1.divide)(u, v).invert(); if (q.gt(1) && (0, Utils_1.isInt)(q)) { //b contains a factor a which can be moved to a result = multiply(a.args[0].clone(), (0, index_1.sqrt)(q.clone())); b = new Symbol_1.Symbol(1); } } //simplify factorial but only if //1 - It's division so b will have a negative power //2 - We're not dealing with factorials of numbers else if (a.fname === Settings_1.Settings.FACTORIAL && b.fname === Settings_1.Settings.FACTORIAL && !u.isConstant() && !v.isConstant() && b.power < 0) { //assume that n = positive var d = (0, subtract_1.subtract)(u.clone(), v.clone()); //if it's not numeric then we don't know if we can simplify so just return if (d.isConstant()) { //there will never be a case where d == 0 since this will already have //been handled at the beginning of this function t = new Symbol_1.Symbol(1); if (d < 0) { //If d is negative then the numerator is larger so expand that for (var i = 0, n = Math.abs(d); i <= n; i++) { var s = (0, add_1.add)(u.clone(), new Symbol_1.Symbol(i)); t = multiply(t, s); } result = multiply((0, pow_1.pow)(u, new Symbol_1.Symbol(a.power)), (0, pow_1.pow)(t, new Symbol_1.Symbol(b.power))); b = new Symbol_1.Symbol(1); } else { //Otherwise the denominator is larger so expand that for (var i = 0, n = Math.abs(d); i <= n; i++) { var s = (0, add_1.add)(v.clone(), new Symbol_1.Symbol(i)); t = multiply(t, s); } result = multiply((0, pow_1.pow)(t, new Symbol_1.Symbol(a.power)), (0, pow_1.pow)(v, new Symbol_1.Symbol(b.power))); b = new Symbol_1.Symbol(1); } } } } //if both are Groups.PL then their hashes have to match if (v1 === v2 && g1 === Groups_1.Groups.PL && g1 === g2) { v1 = a.text('hash'); v2 = b.text('hash'); } //same issue with (x^2+1)^x*(x^2+1) //Groups.EX needs an exception when multiplying because it needs to recognize //that (x+x^2)^x has the same hash as (x+x^2). The latter is kept as x if (g2 === Groups_1.Groups.EX && b.previousGroup === Groups_1.Groups.PL && g1 === Groups_1.Groups.PL) { v1 = (0, Text_1.text)(a, 'hash', Groups_1.Groups.EX); } if ((v1 === v2 || ONN) && !(g1 === Groups_1.Groups.PL && (g2 === Groups_1.Groups.S || g2 === Groups_1.Groups.P || g2 === Groups_1.Groups.FN)) && !(g1 === Groups_1.Groups.PL && g2 === Groups_1.Groups.CB)) { var p1 = a.power, p2 = b.power, isSymbolP1 = (0, Utils_1.isSymbol)(p1), isSymbolP2 = (0, Utils_1.isSymbol)(p2), toEX = (isSymbolP1 || isSymbolP2); //TODO: this needs cleaning up if (g1 === Groups_1.Groups.PL && g2 !== Groups_1.Groups.PL && b.previousGroup !== Groups_1.Groups.PL && p1.equals(1)) { result = new Symbol_1.Symbol(0); a.each(function (x) { result = (0, add_1.add)(result, multiply(x, b.clone())); }, true); } else { //add the powers result.power = toEX ? (0, add_1.add)(!((0, Utils_1.isSymbol)(p1)) ? new Symbol_1.Symbol(p1) : p1, !((0, Utils_1.isSymbol)(p2)) ? new Symbol_1.Symbol(p2) : p2) : (g1 === Groups_1.Groups.N /*don't add powers for Groups.N*/ ? p1 : p1.add(p2)); //eliminate zero power values and convert them to numbers if (result.power.equals(0)) result = result.convert(Groups_1.Groups.N); //properly convert to Groups.EX if (toEX) result.convert(Groups_1.Groups.EX); //take care of imaginaries if (a.imaginary && b.imaginary) { var isEven = (0, Utils_1.even)(result.power % 2); if (isEven) { result = new Symbol_1.Symbol(1); m.negate(); } } //cleanup: this causes the LaTeX generator to get confused as to how to render the symbol if (result.group !== Groups_1.Groups.EX && result.previousGroup) result.previousGroup = undefined; //the sign for b is floating around. Remember we are assuming that the odd variable will carry //the sign but this isn't true if they're equals symbols result.multiplier = result.multiplier.multiply(b.multiplier); } } else if (g1 === Groups_1.Groups.CB && a.isLinear()) { if (g2 === Groups_1.Groups.CB) b.distributeExponent(); if (g2 === Groups_1.Groups.CB && b.isLinear()) { for (var s in b.symbols) { var x = b.symbols[s]; result = result.combine(x); } result.multiplier = result.multiplier.multiply(b.multiplier); } else { result.combine(b); } } else { //the multiplier was already handled so nothing left to do if (g1 !== Groups_1.Groups.N) { if (g1 === Groups_1.Groups.CB) { result.distributeExponent(); result.combine(b); } else if (!b.isOne()) { var bm = b.multiplier.clone(); b.toUnitMultiplier(); result = Symbol_1.Symbol.shell(Groups_1.Groups.CB).combine([result, b]); //transfer the multiplier to the outside result.multiplier = result.multiplier.multiply(bm); } } else { result = b.clone().toUnitMultiplier(); } } if (result.group === Groups_1.Groups.P) { var logV = Math.log(result.value), n1 = Math.log(bnum) / logV, n2 = Math.log(bden) / logV, ndiv = m.num / bnum, ddiv = m.den / bden; //we don't want to divide by zero no do we? Strange things happen. if (n1 !== 0 && (0, Utils_1.isInt)(n1) && (0, Utils_1.isInt)(ndiv)) { result.power = result.power.add(new Frac_1.Frac(n1)); m.num /= bnum; //BigInt? Keep that in mind for the future. } if (n2 !== 0 && (0, Utils_1.isInt)(n2) && (0, Utils_1.isInt)(ddiv)) { result.power = result.power.subtract(new Frac_1.Frac(n2)); m.den /= bden; //BigInt? Keep that in mind for the future. } } //unpack Groups.CB if length is only one if (result.length === 1) { var t = result.multiplier; //transfer the multiplier result = (0, Utils_1.firstObject)(result.symbols); result.multiplier = result.multiplier.multiply(t); } //reduce square root var ps = result.power.toString(); if ((0, Utils_1.even)(ps) && result.fname === Settings_1.Settings.SQRT) { //grab the sign of the symbol sign = sign * result.sign(); var p = result.power; result = result.args[0]; result = multiply(new Symbol_1.Symbol(m), (0, pow_1.pow)(result, new Symbol_1.Symbol(p.divide(new Frac_1.Frac(2))))); //flip it back to the correct sign if (sign < 0) result.negate(); } else { result.multiplier = result.multiplier.multiply(m).multiply(sign); if (result.group === Groups_1.Groups.CP && result.isImaginary()) result.distributeMultiplier(); } //back convert group Groups.P to a simpler group Groups.N if possible if (result.group === Groups_1.Groups.P && (0, Utils_1.isInt)(result.power.toDecimal())) result = result.convert(Groups_1.Groups.N); return result; } else { //****** Matrices & Vector *****// if (bIsSymbol && !aIsSymbol) { //keep symbols to the right t = a; a = b; b = t; //swap t = bIsSymbol; bIsSymbol = aIsSymbol; aIsSymbol = t; } var isMatrixB = (0, Utils_1.isMatrix)(b), isMatrixA = (0, Utils_1.isMatrix)(a); if (aIsSymbol && isMatrixB) { var M = new Matrix_1.Matrix(); b.eachElement(function (e, i, j) { M.set(i, j, multiply(a.clone(), e)); }); b = M; } else { if (isMatrixA && isMatrixB) { b = a.multiply(b); } else if (aIsSymbol && (0, Utils_1.isVector)(b)) { b.each(function (x, i) { i--; b.elements[i] = multiply(a.clone(), b.elements[i]); }); } else { if ((0, Utils_1.isVector)(a) && (0, Utils_1.isVector)(b)) { b.each(function (x, i) { i--; b.elements[i] = multiply(a.elements[i], b.elements[i]); }); } else if ((0, Utils_1.isVector)(a) && (0, Utils_1.isMatrix)(b)) { //try to convert a to a matrix return multiply(b, a); } else if ((0, Utils_1.isMatrix)(a) && (0, Utils_1.isVector)(b)) { if (b.elements.length === a.rows()) { var M = new Matrix_1.Matrix(), l = a.cols(); b.each(function (e, i) { var row = []; for (var j = 0; j < l; j++) { row.push(multiply(a.elements[i - 1][j].clone(), e.clone())); } M.elements.push(row); }); return M; } else (0, Errors_1.err)('Dimensions must match!'); } } } return b; } } exports.multiply = multiply; //# sourceMappingURL=multiply.js.map