UNPKG

nerdamer

Version:

javascript light-weight symbolic math expression evaluator

1,122 lines (1,052 loc) 151 kB
/* global module */ /* * Author : Martin Donk * Website : http://www.nerdamer.com * Email : martin.r.donk@gmail.com * Source : https://github.com/jiggzson/nerdamer */ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { var nerdamer = require('./nerdamer.core.js'); require('./Algebra.js'); } (function () { "use strict"; var core = nerdamer.getCore(), _ = core.PARSER, Frac = core.Frac, Settings = core.Settings, isSymbol = core.Utils.isSymbol, FN = core.groups.FN, Symbol = core.Symbol, text = core.Utils.text, inBrackets = core.Utils.inBrackets, isInt = core.Utils.isInt, format = core.Utils.format, even = core.Utils.even, evaluate = core.Utils.evaluate, N = core.groups.N, S = core.groups.S, FN = core.groups.FN, PL = core.groups.PL, CP = core.groups.CP, CB = core.groups.CB, EX = core.groups.EX, P = core.groups.P, LOG = Settings.LOG, EXP = 'exp', ABS = 'abs', SQRT = 'sqrt', SIN = 'sin', COS = 'cos', TAN = 'tan', SEC = 'sec', CSC = 'csc', COT = 'cot', ASIN = 'asin', ACOS = 'acos', ATAN = 'atan', ASEC = 'asec', ACSC = 'acsc', ACOT = 'acot', SINH = 'sinh', COSH = 'cosh', TANH = 'tanh', CSCH = 'csch', SECH = 'sech', COTH = 'coth', ASECH = 'asech', ACSCH = 'acsch', ACOTH = 'acoth'; //custom errors function NoIntegralFound(msg) { this.message = msg || ""; } NoIntegralFound.prototype = new Error(); //Preparations Symbol.prototype.hasIntegral = function () { return this.containsFunction('integrate'); }; //transforms a function Symbol.prototype.fnTransform = function () { if(this.group !== FN) return this; var retval, a = this.args[0]; var m = new Symbol(this.multiplier); var sym = this.clone().toUnitMultiplier(); if(this.isLinear()) { switch(this.fname) { case SINH: retval = _.parse(format('(e^({0})-e^(-({0})))/2', a)); break; case COSH: retval = _.parse(format('(e^({0})+e^(-({0})))/2', a)); break; case TANH: retval = _.parse(format('(e^({0})-e^(-({0})))/(e^({0})+e^(-({0})))', a)); break; case TAN: retval = _.parse(format('sin({0})/cos({0})', a)); break; case CSC: retval = _.parse(format('1/sin({0})', a)); break; case SEC: retval = _.parse(format('1/cos({0})', a)); break; default: retval = sym; } } else if(this.power.equals(2)) { switch(this.fname) { case SIN: retval = _.parse(format('1/2-cos(2*({0}))/2', a)); break; case COS: retval = _.parse(format('1/2+cos(2*({0}))/2', a)); break; case TAN: //retval = _.parse(format('(1-cos(2*({0})))/(1+cos(2*({0})))', a)); retval = _.parse(format('sin({0})^2/cos({0})^2', a)); break; case COSH: retval = _.parse(format('1/2+cosh(2*({0}))/2', a)); break; case SINH: retval = _.parse(format('-1/2+cosh(2*({0}))/2', a)); break; case TANH: retval = _.parse(format('(1+cosh(2*({0})))/(-1+cosh(2*({0})))', a)); break; case SEC: retval = _.parse(format('(1-cos(2*({0})))/(1+cos(2*({0})))+1', a)); break; default: retval = sym; } } else if(this.fname === SEC) { retval = _.parse(format('1/cos({0})^({1})', this.args[0], this.power)); } else if(this.fname === CSC) { retval = _.parse(format('1/sin({0})^({1})', this.args[0], this.power)); } else if(this.fname === TAN) { if(this.power.lessThan(0)) { retval = _.parse(format('cos({0})^(-({1}))/sin({0})^({1})', this.args[0], this.power.negate())); } else { retval = _.parse(format('sin({0})^({1})/cos({0})^({1})', this.args[0], this.power)); } } else if(this.fname === SIN && this.power.lessThan(0)) { retval = _.parse(format('csc({0})^(-({1}))', this.args[0], this.power.negate())); } else if(this.fname === COS && this.power.lessThan(0)) { retval = _.parse(format('sec({0})^(-({1}))', this.args[0], this.power.negate())); } else if(this.fname === SIN && this.power.equals(3)) { retval = _.parse(format('(3*sin({0})-sin(3*({0})))/4', this.args[0])); } else if(this.fname === COS && this.power.equals(3)) { retval = _.parse(format('(cos(3*({0}))+3*cos({0}))/4', this.args[0])); } //cos(a*x)^(2*n) or sin(a*x)^(2*n) else if((this.fname === COS || this.fname === SIN) && even(this.power)) { var n = this.power / 2; //convert to a double angle var double_angle = _.pow(this.clone().toLinear(), _.parse(2)).fnTransform(); //raise to the n and expand var transformed = _.expand(_.pow(double_angle, _.parse(n))); retval = new Symbol(0); transformed.each(function (s) { var t = s.fnTransform(); retval = _.add(retval, t); }, true); } else retval = sym; return _.multiply(retval, m); }; Symbol.prototype.hasTrig = function () { if(this.isConstant(true) || this.group === S) return false; if(this.fname && (core.Utils.in_trig(this.fname) || core.Utils.in_inverse_trig(this.fname))) return true; if(this.symbols) { for(var x in this.symbols) if(this.symbols[x].hasTrig()) return true; } return false; }; core.Expression.prototype.hasIntegral = function () { return this.symbol.hasIntegral(); }; /** * Attempts to rewrite a symbol under one common denominator * @param {Symbol} symbol */ core.Utils.toCommonDenominator = function (symbol) { //transform x/a+x -> (ax+x)/a if(symbol.isComposite() && symbol.isLinear()) { var m = new Symbol(symbol.multiplier); var denominator = new Symbol(1); var numerator = new Symbol(0); symbol.each(function (x) { denominator = _.multiply(denominator, x.getDenom()); }, true); //remove the denomitor in each term symbol.each(function (x) { var num = x.getNum(); var den = x.getDenom(); var factor = _.multiply(num, _.divide(denominator.clone(), den)); numerator = _.add(numerator, factor); }); var retval = _.multiply(m, core.Algebra.divide(_.expand(numerator), _.expand(denominator))); return retval; } return symbol; }; //A function to check if a function name is an inverse trig function core.Utils.in_inverse_trig = function (x) { var inv_trig_fns = [ASIN, ACOS, ATAN, ACSC, ASEC, ACOT]; return inv_trig_fns.indexOf(x) !== -1; }; //A function to check if a function name is a trig function core.Utils.in_trig = function (x) { var trig_fns = [COS, SIN, TAN, SEC, CSC, COT]; return trig_fns.indexOf(x) !== -1; }; core.Utils.in_htrig = function (x) { var trig_fns = [SINH, COSH, TANH, ACSCH, ASECH, ACOTH]; return trig_fns.indexOf(x) !== -1; }; // Matrix functions core.Matrix.jacobian = function (eqns, vars) { var jacobian = new core.Matrix(); //get the variables if not supplied if(!vars) { vars = core.Utils.arrayGetVariables(eqns); } vars.forEach(function (v, i) { eqns.forEach(function (eq, j) { var e = core.Calculus.diff(eq.clone(), v); jacobian.set(j, i, e); }); }); return jacobian; }; core.Matrix.prototype.max = function () { var max = new Symbol(0); this.each(function (x) { var e = x.abs(); if(e.gt(max)) max = e; }); return max; }; core.Matrix.cMatrix = function (value, vars) { var m = new core.Matrix(); //make an initial guess vars.forEach(function (v, i) { m.set(i, 0, _.parse(value)); }); return m; }; var all_functions = core.Utils.all_functions = function (arr) { for(var i = 0, l = arr.length; i < l; i++) if(arr[i].group !== FN) return false; return true; }, cosAsinBtransform = core.Utils.cosAsinBtranform = function (symbol1, symbol2) { var a, b; a = symbol1.args[0]; b = symbol2.args[0]; return _.parse(format('(sin(({0})+({1}))-sin(({0})-({1})))/2', a, b)); }, cosAsinAtransform = core.Utils.cosAsinAtranform = function (symbol1, symbol2) { //TODO: temporary fix for integrate(e^x*sin(x)*cos(x)^2). //we technically know how to do this transform but more is needed for correct output if(Number(symbol2.power) !== 1) return _.multiply(symbol1, symbol2); var a; a = symbol1.args[0]; return _.parse(format('(sin(2*({0})))/2', a)); }, sinAsinBtransform = core.Utils.cosAsinBtranform = function (symbol1, symbol2) { var a, b; a = symbol1.args[0]; b = symbol2.args[0]; return _.parse(format('(cos(({0})+({1}))-cos(({0})-({1})))/2', a, b)); }, trigTransform = core.Utils.trigTransform = function (arr) { var map = {}, symbol, t, retval = new Symbol(1); for(var i = 0, l = arr.length; i < l; i++) { symbol = arr[i]; if(symbol.group === FN) { var fname = symbol.fname; if(fname === COS && map[SIN]) { if(map[SIN].args[0].toString() !== symbol.args[0].toString()) { t = cosAsinBtransform(symbol, map[SIN]); } else { t = cosAsinAtransform(symbol, map[SIN]); } delete map[SIN]; retval = _.multiply(retval, t); } else if(fname === SIN && map[COS]) { if(map[COS].args[0].toString() !== symbol.args[0].toString()) { t = cosAsinBtransform(symbol, map[COS]); } else { t = cosAsinAtransform(symbol, map[COS]); } delete map[COS]; retval = _.multiply(retval, t); } else if(fname === SIN && map[SIN]) { if(map[SIN].args[0].toString() !== symbol.args[0].toString()) { t = sinAsinBtransform(symbol, map[SIN]); delete map[SIN]; } else { //This should actually be redundant code but let's put just in case t = _.multiply(symbol, map[SIN]); delete map[SIN]; } retval = t; } else { map[fname] = symbol; } } else retval = _.multiply(retval, symbol); } //put back the remaining functions for(var x in map) retval = _.multiply(retval, map[x]); return retval; }; core.Settings.integration_depth = 10; core.Settings.max_lim_depth = 10; var __ = core.Calculus = { version: '1.4.6', sum: function (fn, index, start, end) { if(!(index.group === core.groups.S)) throw new core.exceptions.NerdamerTypeError('Index must be symbol. ' + text(index) + ' provided'); index = index.value; var retval; if(core.Utils.isNumericSymbol(start) && core.Utils.isNumericSymbol(end)) { var modifier = end - start < 200 ? '' : 'PARSE2NUMBER'; start = Number(start); end = Number(end); retval = core.Utils.block(modifier, function () { var f = fn.text(), subs = {'~': true}, //lock subs. Is this even being used? retval = new core.Symbol(0); for(var i = start; i <= end; i++) { subs[index] = new Symbol(i); var ans = _.parse(f, subs); retval = _.add(retval, ans); } return retval; }); } else { retval = _.symfunction('sum', arguments); } return retval; }, product: function (fn, index, start, end) { if(!(index.group === core.groups.S)) throw new core.exceptions.NerdamerTypeError('Index must be symbol. ' + text(index) + ' provided'); index = index.value; var retval; if(core.Utils.isNumericSymbol(start) && core.Utils.isNumericSymbol(end)) { var modifier = end - start < 200 ? '' : 'PARSE2NUMBER'; retval = core.Utils.block(modifier, function () { start = Number(start); end = Number(end.multiplier); var f = fn.text(), subs = {}, retval = new core.Symbol(1); for(var i = start; i <= end; i++) { subs[index] = new Symbol(i); retval = _.multiply(retval, _.parse(f, subs)); } return retval; }); } else { retval = _.symfunction('product', arguments); } return retval; }, diff: function (symbol, wrt, nth) { if(core.Utils.isVector(symbol)) { var vector = new core.Vector([]); symbol.each(function (x) { vector.elements.push(__.diff(x, wrt, nth)); }); return vector; } else if(core.Utils.isMatrix(symbol)) { var matrix = new core.Matrix(); symbol.each(function (x, i, j) { matrix.set(i, j, __.diff(x, wrt, nth)); }); return matrix; } var d = isSymbol(wrt) ? wrt.text() : wrt; //the nth derivative nth = isSymbol(nth) ? nth.multiplier : nth || 1; if(d === undefined) d = core.Utils.variables(symbol)[0]; //unwrap sqrt if(symbol.group === FN && symbol.fname === SQRT) { var s = symbol.args[0], sp = symbol.power.clone(); //these groups go to zero anyway so why waste time? if(s.group !== N || s.group !== P) { s.power = isSymbol(s.power) ? _.multiply(s.power, _.multiply(new Symbol(1 / 2)), sp) : s.power.multiply(new Frac(0.5)).multiply(sp); s.multiplier = s.multiplier.multiply(symbol.multiplier); } symbol = s; } if(symbol.group === FN && !isSymbol(symbol.power)) { var a = derive(_.parse(symbol)); var b = __.diff(symbol.args[0].clone(), d); symbol = _.multiply(a, b);//chain rule } else { symbol = derive(symbol); } if(nth > 1) { nth--; symbol = __.diff(symbol, wrt, nth); } return symbol; // Equivalent to "derivative of the outside". function polydiff(symbol) { if(symbol.value === d || symbol.contains(d, true)) { symbol.multiplier = symbol.multiplier.multiply(symbol.power); symbol.power = symbol.power.subtract(new Frac(1)); if(symbol.power.equals(0)) { symbol = Symbol(symbol.multiplier); } } return symbol; } function derive(symbol) { var g = symbol.group, a, b, cp; if(g === N || g === S && symbol.value !== d || g === P) { symbol = Symbol(0); } else if(g === S) { symbol = polydiff(symbol); } else if(g === CB) { var m = symbol.multiplier.clone(); symbol.toUnitMultiplier(); var retval = _.multiply(product_rule(symbol), polydiff(symbol)); retval.multiplier = retval.multiplier.multiply(m); return retval; } else if(g === FN && symbol.power.equals(1)) { // Table of known derivatives switch(symbol.fname) { case LOG: cp = symbol.clone(); symbol = symbol.args[0].clone();//get the arguments symbol.power = symbol.power.negate(); symbol.multiplier = cp.multiplier.divide(symbol.multiplier); break; case COS: //cos -> -sin symbol.fname = SIN; symbol.multiplier.negate(); break; case SIN: //sin -> cos symbol.fname = COS; break; case TAN: //tan -> sec^2 symbol.fname = SEC; symbol.power = new Frac(2); break; case SEC: // Use a clone if this gives errors symbol = qdiff(symbol, TAN); break; case CSC: symbol = qdiff(symbol, '-cot'); break; case COT: symbol.fname = CSC; symbol.multiplier.negate(); symbol.power = new Frac(2); break; case ASIN: symbol = _.parse('(sqrt(1-(' + text(symbol.args[0]) + ')^2))^(-1)'); break; case ACOS: symbol = _.parse('-(sqrt(1-(' + text(symbol.args[0]) + ')^2))^(-1)'); break; case ATAN: symbol = _.parse('(1+(' + text(symbol.args[0]) + ')^2)^(-1)'); break; case ABS: m = symbol.multiplier.clone(); symbol.toUnitMultiplier(); //depending on the complexity of the symbol it's easier to just parse it into a new symbol //this should really be readdressed soon b = symbol.args[0].clone(); b.toUnitMultiplier(); symbol = _.parse(inBrackets(text(symbol.args[0])) + '/abs' + inBrackets(text(b))); symbol.multiplier = m; break; case 'parens': //see product rule: f'.g goes to zero since f' will return zero. This way we only get back //1*g' symbol = Symbol(1); break; case 'cosh': //cosh -> -sinh symbol.fname = 'sinh'; break; case 'sinh': //sinh -> cosh symbol.fname = 'cosh'; break; case TANH: //tanh -> sech^2 symbol.fname = SECH; symbol.power = new Frac(2); break; case SECH: // Use a clone if this gives errors symbol = qdiff(symbol, '-tanh'); break; case CSCH: var arg = String(symbol.args[0]); return _.parse('-coth(' + arg + ')*csch(' + arg + ')'); break; case COTH: var arg = String(symbol.args[0]); return _.parse('-csch(' + arg + ')^2'); break; case 'asinh': symbol = _.parse('(sqrt(1+(' + text(symbol.args[0]) + ')^2))^(-1)'); break; case 'acosh': symbol = _.parse('(sqrt(-1+(' + text(symbol.args[0]) + ')^2))^(-1)'); break; case 'atanh': symbol = _.parse('(1-(' + text(symbol.args[0]) + ')^2)^(-1)'); break; case ASECH: var arg = String(symbol.args[0]); symbol = _.parse('-1/(sqrt(1/(' + arg + ')^2-1)*(' + arg + ')^2)'); break; case ACOTH: symbol = _.parse('-1/((' + symbol.args[0] + ')^2-1)'); break; case ACSCH: var arg = String(symbol.args[0]); symbol = _.parse('-1/(sqrt(1/(' + arg + ')^2+1)*(' + arg + ')^2)'); break; case ASEC: var arg = String(symbol.args[0]); symbol = _.parse('1/(sqrt(1-1/(' + arg + ')^2)*(' + arg + ')^2)'); break; case ACSC: var arg = String(symbol.args[0]); symbol = _.parse('-1/(sqrt(1-1/(' + arg + ')^2)*(' + arg + ')^2)'); break; case ACOT: symbol = _.parse('-1/((' + symbol.args[0] + ')^2+1)'); break; case 'S': var arg = String(symbol.args[0]); symbol = _.parse('sin((pi*(' + arg + ')^2)/2)'); break; case 'C': var arg = String(symbol.args[0]); symbol = _.parse('cos((pi*(' + arg + ')^2)/2)'); break; case 'Si': var arg = symbol.args[0]; symbol = _.parse('sin(' + arg + ')/(' + arg + ')'); break; case 'Shi': var arg = symbol.args[0]; symbol = _.parse('sinh(' + arg + ')/(' + arg + ')'); break; case 'Ci': var arg = symbol.args[0]; symbol = _.parse('cos(' + arg + ')/(' + arg + ')'); break; case 'Chi': var arg = symbol.args[0]; symbol = _.parse('cosh(' + arg + ')/(' + arg + ')'); break; case 'Ei': var arg = symbol.args[0]; symbol = _.parse('e^(' + arg + ')/(' + arg + ')'); break; case 'Li': var arg = symbol.args[0]; symbol = _.parse('1/' + Settings.LOG + '(' + arg + ')'); break; case 'erf': symbol = _.parse('(2*e^(-(' + symbol.args[0] + ')^2))/sqrt(pi)'); break; case 'atan2': var x_ = String(symbol.args[0]), y_ = String(symbol.args[1]); symbol = _.parse('(' + y_ + ')/((' + y_ + ')^2+(' + x_ + ')^2)'); break; case 'sign': symbol = new Symbol(0); break; case 'sinc': symbol = _.parse(format('(({0})*cos({0})-sin({0}))*({0})^(-2)', symbol.args[0])); break; case Settings.LOG10: symbol = _.parse('1/((' + symbol.args[0] + ')*' + Settings.LOG + '(10))'); break; default: symbol = _.symfunction('diff', [symbol, wrt]); } } else if(g === EX || g === FN && isSymbol(symbol.power)) { var value; if(g === EX) { value = symbol.value; } else if(g === FN && symbol.contains(d)) { value = symbol.fname + inBrackets(text(symbol.args[0])); } else { value = symbol.value + inBrackets(text(symbol.args[0])); } a = _.multiply(_.parse(LOG + inBrackets(value)), symbol.power.clone()); b = __.diff(_.multiply(_.parse(LOG + inBrackets(value)), symbol.power.clone()), d); symbol = _.multiply(symbol, b); } else if(g === FN && !symbol.power.equals(1)) { b = symbol.clone(); b.toLinear(); b.toUnitMultiplier(); symbol = _.multiply(polydiff(symbol.clone()), derive(b)); } else if(g === CP || g === PL) { // Note: Do not use `parse` since this puts back the sqrt and causes a bug as in #610. Use clone. var c = symbol.clone(); var result = new Symbol(0); for(var x in symbol.symbols) { result = _.add(result, __.diff(symbol.symbols[x].clone(), d)); } symbol = _.multiply(polydiff(c), result); } symbol.updateHash(); return symbol; } ; function qdiff(symbol, val, altVal) { return _.multiply(symbol, _.parse(val + inBrackets(altVal || text(symbol.args[0])))); } ; function product_rule(symbol) { //grab all the symbols within the CB symbol var symbols = symbol.collectSymbols(), result = new Symbol(0), l = symbols.length; //loop over all the symbols for(var i = 0; i < l; i++) { var df = __.diff(symbols[i].clone(), d); for(var j = 0; j < l; j++) { //skip the symbol of which we just pulled the derivative if(i !== j) { //multiply out the remaining symbols df = _.multiply(df, symbols[j].clone()); } } //add the derivative to the result result = _.add(result, df); } return result; //done } ; }, integration: { u_substitution: function (symbols, dx) { function try_combo(a, b, f) { var d = __.diff(b, dx); var q = f ? f(a, b) : _.divide(a.clone(), d); if(!q.contains(dx, true)) return q; return null; } function do_fn_sub(fname, arg) { var subbed = __.integrate(_.symfunction(fname, [new Symbol(u)]), u, 0); subbed = subbed.sub(new Symbol(u), arg); subbed.updateHash(); return subbed; } var a = symbols[0].clone(), b = symbols[1].clone(), g1 = a.group, g2 = b.group, //may cause problems if person is using this already. Will need //to find algorithm for detecting conflict u = '__u__', Q; if(g1 === FN && g2 !== FN) { //e.g. 2*x*cos(x^2) var arg = a.args[0]; Q = try_combo(b, arg.clone()); if(Q) return _.multiply(Q, do_fn_sub(a.fname, arg)); Q = try_combo(b, a); if(Q) { return __.integration.poly_integrate(a); } } else if(g2 === FN && g1 !== FN) { //e.g. 2*(x+1)*cos((x+1)^2 var arg = b.args[0]; Q = try_combo(a, arg.clone()); if(Q) return _.multiply(Q, do_fn_sub(b.fname, arg)); } else if(g1 === FN && g2 === FN) { Q = try_combo(a.clone(), b.clone()); if(Q) return _.multiply(__.integration.poly_integrate(b), Q); Q = try_combo(b.clone(), a.clone()); if(Q) return _.multiply(__.integration.poly_integrate(b), Q); } else if(g1 === EX && g2 !== EX) { var p = a.power; Q = try_combo(b, p.clone()); if(!Q) { //one more try var dc = __.integration.decompose_arg(p.clone(), dx); //consider the possibility of a^x^(n-1)*x^n dx var xp = __.diff(dc[2].clone(), dx); var dc2 = __.integration.decompose_arg(xp.clone(), dx); //if their powers equal, so if dx*p == b if(_.multiply(dc[1], dc2[1]).power.equals(b.power)) { var m = _.divide(dc[0].clone(), dc2[0].clone()); var new_val = _.multiply(m.clone(), _.pow(new Symbol(a.value), _.multiply(dc[0], new Symbol(u)))); new_val = _.multiply(new_val, new Symbol(u)); return __.integration.by_parts(new_val, u, 0, {}).sub(u, dc[1].clone()); } } var integrated = __.integrate(a.sub(p.clone(), new Symbol(u)), u, 0), retval = _.multiply(integrated.sub(new Symbol(u), p), Q); return retval; } else if(g2 === EX && g1 !== EX) { var p = b.power; Q = try_combo(a, p.clone()); var integrated = __.integrate(b.sub(p, new Symbol(u)), u, 0); return _.multiply(integrated.sub(new Symbol(u), p), Q); } else if(a.isComposite() || b.isComposite()) { var f = function (a, b) { var d = __.diff(b, dx); var A = core.Algebra.Factor.factor(a), B = core.Algebra.Factor.factor(d); var q = _.divide(A, B); return q; }; var f1 = a.isComposite() ? a.clone().toLinear() : a.clone(), f2 = b.isComposite() ? b.clone().toLinear() : b.clone(); Q = try_combo(f1.clone(), f2.clone(), f); if(Q) return _.multiply(__.integration.poly_integrate(b), Q); Q = try_combo(f2.clone(), f1.clone(), f); if(Q) return _.multiply(__.integration.poly_integrate(a), Q); } }, //simple integration of a single polynomial x^(n+1)/(n+1) poly_integrate: function (x) { var p = x.power.toString(), m = x.multiplier.toDecimal(), s = x.toUnitMultiplier().toLinear(); if(Number(p) === -1) { return _.multiply(new Symbol(m), _.symfunction(LOG, [s])); } return _.parse(format('({0})*({1})^(({2})+1)/(({2})+1)', m, s, p)); }, //If we're just spinning wheels we want to stop. This is why we //wrap integration in a try catch block and call this to stop. stop: function (msg) { msg = msg || 'Unable to compute integral!'; core.Utils.warn(msg); throw new NoIntegralFound(msg); }, partial_fraction: function (input, dx, depth, opt) { //TODO: This whole thing needs to be rolled into one but for now I'll leave it as two separate parts if(!isSymbol(dx)) dx = _.parse(dx); var result, partial_fractions; result = new Symbol(0); partial_fractions = core.Algebra.PartFrac.partfrac(input, dx); if(partial_fractions.group === CB && partial_fractions.isLinear()) { //perform a quick check to make sure that all partial fractions are linear partial_fractions.each(function (x) { if(!x.isLinear()) __.integration.stop(); }); partial_fractions.each(function (x) { result = _.add(result, __.integrate(x, dx, depth, opt)); }); } else { result = _.add(result, __.integrate(partial_fractions, dx, depth, opt)); } return result; }, get_udv: function (symbol) { var parts = [[/*L*/], [/*I*/], [/*A*/], [/*T*/], [/*E*/]]; //first we sort them var setSymbol = function (x) { var g = x.group; if(g === FN) { var fname = x.fname; if(core.Utils.in_trig(fname) || core.Utils.in_htrig(fname)) parts[3].push(x); else if(core.Utils.in_inverse_trig(fname)) parts[1].push(x); else if(fname === LOG) parts[0].push(x); else { __.integration.stop(); } } else if(g === S || x.isComposite() && x.isLinear() || g === CB && x.isLinear()) { parts[2].push(x); } else if(g === EX || x.isComposite() && !x.isLinear()) parts[4].push(x); else __.integration.stop(); }; if(symbol.group === CB) symbol.each(function (x) { setSymbol(Symbol.unwrapSQRT(x, true)); }); else setSymbol(symbol); var u, dv = new Symbol(1); //compile u and dv for(var i = 0; i < 5; i++) { var part = parts[i], t, l = part.length; if(l > 0) { if(l > 1) { t = new Symbol(1); for(var j = 0; j < l; j++) t = _.multiply(t, part[j].clone()); } else t = part[0].clone(); if(!u) { u = t;//the first u encountered gets chosen u.multiplier = u.multiplier.multiply(symbol.multiplier); //the first one gets the mutliplier } else dv = _.multiply(dv, t); //everything else belongs to dv } } return [u, dv]; }, trig_sub: function (symbol, dx, depth, opt, parts, symbols) { parts = parts || __.integration.decompose_arg(symbol.clone().toLinear(), dx); var b = parts[3], ax = parts[2], a = parts[0], x = parts[1]; if(x.power.equals(2) && a.greaterThan(0)) { //use tan(x) var t = core.Utils.getU(symbol), //get an appropriate u u = _.parse(TAN + inBrackets(t)), //u du = _.parse(SEC + inBrackets(t) + '^2'), //du f = _.multiply(symbol.sub(x, u), du); var integral = __.integrate(f, t, depth, opt).sub(u, x); core.Utils.clearU(u); return integral; } }, by_parts: function (symbol, dx, depth, o) { o.previous = o.previous || []; var udv, u, dv, du, v, vdu, uv, retval, integral_vdu, m, c, vdu_s; //first LIATE udv = __.integration.get_udv(symbol); u = udv[0]; dv = udv[1]; du = Symbol.unwrapSQRT(_.expand(__.diff(u.clone(), dx)), true); c = du.clone().stripVar(dx); //strip any coefficients du = _.divide(du, c.clone()); v = __.integrate(dv.clone(), dx, depth || 0); vdu = _.multiply(v.clone(), du); vdu_s = vdu.toString(); //currently only supports e^x*(some trig) if(o.previous.indexOf(vdu_s) !== -1 && (core.Utils.in_trig(u.fname)) && dv.isE()) { //We're going to exploit the fact that vdu can never be constant //to work out way out of this cycle. We'll return the length of //the this.previous array until we're back at level one o.is_cyclic = true; //return the integral. return new Symbol(1); } else o.previous.push(vdu_s); uv = _.multiply(u, v); //clear the multiplier so we're dealing with a bare integral m = vdu.multiplier.clone(); vdu.toUnitMultiplier(); integral_vdu = _.multiply(__.integrate(vdu.clone(), dx, depth, o), c); integral_vdu.multiplier = integral_vdu.multiplier.multiply(m); retval = _.subtract(uv, integral_vdu); //we know that there cannot be constants so they're a holdover from a cyclic integral if(o.is_cyclic) { //start popping the previous stack so we know how deep in we are o.previous.pop(); if(o.previous.length === 0) { retval = _.expand(retval); var rem = new Symbol(0); retval.each(function (x) { if(!x.contains(dx)) rem = _.add(rem, x.clone()); }); //get the actual uv retval = _.divide(_.subtract(retval, rem.clone()), _.subtract(new Symbol(1), rem)); } } return retval; }, /* * dependents: [Solve, integrate] */ decompose_arg: core.Utils.decompose_fn }, //TODO: nerdamer.integrate('-e^(-a*t)*sin(t)', 't') -> gives incorrect output integrate: function (original_symbol, dt, depth, opt) { //assume integration wrt independent variable if expression only has one variable if(!dt) { var vars = core.Utils.variables(original_symbol); if(vars.length === 1) dt = vars[0]; //defaults to x dt = dt || 'x'; } //add support for integrating vectors if(core.Utils.isVector(original_symbol)) { var vector = new core.Vector([]); original_symbol.each(function (x) { vector.elements.push(__.integrate(x, dt)); }); return vector; } if(!isNaN(dt)) _.error('variable expected but received ' + dt); //get rid of constants right away if(original_symbol.isConstant(true)) return _.multiply(original_symbol.clone(), _.parse(dt)); //configurations options for integral. This is needed for tracking extra options //e.g. cyclic integrals or additional settings opt = opt || {}; return core.Utils.block('PARSE2NUMBER', function () { //make a note of the original symbol. Set only if undefined depth = depth || 0; var dx = isSymbol(dt) ? dt.toString() : dt, //we don't want the symbol in sqrt form. x^(1/2) is prefererred symbol = Symbol.unwrapSQRT(original_symbol.clone(), true), g = symbol.group, retval; try { //We stop integration after x amount of recursive calls if(++depth > core.Settings.integration_depth) __.integration.stop('Maximum depth reached. Exiting!'); //constants. We first eliminate anything that doesn't have dx. Everything after this has //to have dx or else it would have been taken care of below if(!symbol.contains(dx, true)) { retval = _.multiply(symbol.clone(), _.parse(dx)); } //e.g. 2*x else if(g === S) { retval = __.integration.poly_integrate(symbol, dx, depth); } else if(g === EX) { if(symbol.previousGroup === FN && !(symbol.fname === 'sqrt' || symbol.fname === Settings.PARENTHESIS)) __.integration.stop(); //check the base if(symbol.contains(dx) && symbol.previousGroup !== FN) { //if the symbol also contains dx then we stop since we currently //don't know what to do with it e.g. x^x if(symbol.power.contains(dx)) __.integration.stop(); else { var t = __.diff(symbol.clone().toLinear(), dx); if(t.contains(dx)) __.integration.stop(); //since at this point it's the base only then we do standard single poly integration //e.g. x^y retval = __.integration.poly_integrate(symbol, dx, depth); } } //e.g. a^x or 9^x else { var a = __.diff(symbol.power.clone(), dx); if(a.contains(dx)) { var aa = a.stripVar(dx), x = _.divide(a.clone(), aa.clone()); if(x.group === S && x.isLinear()) { aa.multiplier = aa.multiplier.divide(new Frac(2)); return _.parse(format('({2})*(sqrt(pi)*erf(sqrt(-{0})*{1}))/(2*sqrt(-{0}))', aa, dx, symbol.multiplier)); } else __.integration.stop(); } if(symbol.isE()) { if(a.isLinear()) retval = symbol; else { if(a.isE() && a.power.group === S && a.power.power.equals(1)) retval = _.multiply(_.symfunction('Ei', [symbol.power.clone()]), symbol.power); else __.integration.stop(); } } else { var d = _.symfunction(LOG, [_.parse(symbol.value)]); retval = _.divide(symbol, d); } retval = _.divide(retval, a); } } else if(symbol.isComposite() && symbol.isLinear()) { var m = _.parse(symbol.multiplier); symbol.toUnitMultiplier(); retval = new Symbol(0); symbol.each(function (x) { retval = _.add(retval, __.integrate(x, dx, depth)); }); retval = _.multiply(m, retval); } else if(g === CP) { if(symbol.power.greaterThan(1)) symbol = _.expand(symbol)