From f2413316581283afd941d1807e6cda781bf18ba1 Mon Sep 17 00:00:00 2001 From: Martin Donk Date: Mon, 20 Apr 2020 14:56:50 -0500 Subject: [PATCH 01/20] Fix #537 --- Calculus.js | 89 +++++++++++++++++++++++++---------------------------- 1 file changed, 42 insertions(+), 47 deletions(-) diff --git a/Calculus.js b/Calculus.js index b48f04b4..993ac36f 100644 --- a/Calculus.js +++ b/Calculus.js @@ -2100,15 +2100,16 @@ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { var retval; do { - var lim1 = evaluate(__.Limit.limit(f, x, lim, depth)); - var lim2 = evaluate(__.Limit.limit(g, x, lim, depth)); + var lim1 = evaluate(__.Limit.limit(f.clone(), x, lim, depth)); + var lim2 = evaluate(__.Limit.limit(g.clone(), x, lim, depth)); //if it's in indeterminate form apply L'Hospital's rule var indeterminate = isInfinity(lim1) && isInfinity(lim2) || equals(lim1, 0) && equals(lim2, 0); //pull the derivatives if(indeterminate) { - var ft = __.diff(fin.clone(), x); - var gt = __.diff(gin.clone(), x); + var ft = __.diff(f.clone(), x); + var gt = __.diff(g.clone(), x); + var t_symbol = _.expand(_.divide(ft, gt)); f = t_symbol.getNum(); g = t_symbol.getDenom(); @@ -2184,7 +2185,6 @@ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { }, limit: function(symbol, x, lim, depth) { //Simplify the symbol - symbol = core.Algebra.Simplify.simplify(symbol); depth = depth || 1; @@ -2208,6 +2208,7 @@ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { var point = {}; point[x] = lim; //lim x as x->c = c where c + try { //evaluate the function at the given limit var t = _.parse(symbol.sub(x, lim), point); @@ -2333,52 +2334,46 @@ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { } } else if(symbol.group === CB) { + + var lim1, lim2; + //loop through all the symbols + //thus => lim f*g*h = lim (f*g)*h = (lim f*g)*(lim h) + //symbols of lower groups are generally easier to differentiatee so get them to the right by first sorting + var symbols = symbol.collectSymbols().sort(function(a, b) { + return a.group - b.group; + }); - //if the group no longer is CB then feed it back to this function - if(symbol.group !== CB) { - retval = __.Limit.limit(symbol, x, lim, depth); - } - else { - var lim1, lim2; - //loop through all the symbols - //thus => lim f*g*h = lim (f*g)*h = (lim f*g)*(lim h) - //symbols of lower groups are generally easier to differentiatee so get them to the right by first sorting - var symbols = symbol.collectSymbols().sort(function(a, b) { - return a.group - b.group; - }); - - var f = symbols.pop(); - //calculate the first limit so we can keep going down the list - lim1 = evaluate(__.Limit.limit(f, x, lim, depth)); - - //reduces all the limits one at a time - while(symbols.length) { - //get the second limit - var g = symbols.pop(); - //get the limit of g - lim2 = evaluate(__.Limit.limit(g, x, lim, depth)); - - //if the limit is in indeterminate form aplly L'Hospital by inverting g and then f/(1/g) - if((lim1.isInfinity || !__.Limit.isConvergent(lim1) && lim2.equals(0) || lim1.equals(0) && __.Limit.isConvergent(lim2))) { - if(g.containsFunction(LOG)) { - //swap them - g = [f, f = g][0]; - } - //invert the symbol - g.invert(); - - lim1 = __.Limit.divide(f, g, x, lim, depth); - } - else { - //lim f*g = (lim f)*(lim g) - lim1 = _.multiply(lim1, lim2); - //let f*g equal f and h equal g - f = _.multiply(f, g); + var f = symbols.pop(); + //calculate the first limit so we can keep going down the list + lim1 = evaluate(__.Limit.limit(f, x, lim, depth)); + + //reduces all the limits one at a time + while(symbols.length) { + //get the second limit + var g = symbols.pop(); + //get the limit of g + lim2 = evaluate(__.Limit.limit(g, x, lim, depth)); + + //if the limit is in indeterminate form aplly L'Hospital by inverting g and then f/(1/g) + if((lim1.isInfinity || !__.Limit.isConvergent(lim1) && lim2.equals(0) || lim1.equals(0) && __.Limit.isConvergent(lim2))) { + if(g.containsFunction(LOG)) { + //swap them + g = [f, f = g][0]; } + //invert the symbol + g.invert(); + + lim1 = __.Limit.divide(f, g, x, lim, depth); + } + else { + //lim f*g = (lim f)*(lim g) + lim1 = _.multiply(lim1, lim2); + //let f*g equal f and h equal g + f = _.multiply(f, g); } - //Done, lim1 is the limit we're looking for - retval = lim1; } + //Done, lim1 is the limit we're looking for + retval = lim1; } else if(symbol.isComposite()) { var _lim; From 14e521cee47bdeb07cb2566f8610c4de09b2dc63 Mon Sep 17 00:00:00 2001 From: Martin Donk Date: Mon, 20 Apr 2020 21:23:52 -0500 Subject: [PATCH 02/20] Fix to factor --- Algebra.js | 16 ++++++++++++---- nerdamer.core.js | 2 +- package.json | 2 +- spec/algebra.spec.js | 4 ++++ 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Algebra.js b/Algebra.js index 719d90da..e88131a3 100644 --- a/Algebra.js +++ b/Algebra.js @@ -958,8 +958,7 @@ if((typeof module) !== 'undefined') { return subs; }; var __ = core.Algebra = { - version: '1.4.5', - init: (function() {})(), + version: '1.4.6', proots: function(symbol, decp) { //the roots will be rounded up to 7 decimal places. //if this causes trouble you can explicitly pass in a different number of places @@ -2142,6 +2141,9 @@ if((typeof module) !== 'undefined') { if(retval.group === CB) { var t = new Symbol(1); + //store the multiplier and strip it + var m = _.parse(retval.multiplier); + retval.toUnitMultiplier(); /* * NOTE: for sign issues with factor START DEBUGGING HERE */ @@ -2163,7 +2165,8 @@ if((typeof module) !== 'undefined') { t = _.multiply(t, factored); } }); - retval = t; + //put back the multiplier + retval = _.multiply(m, t); } return retval; }, @@ -2244,6 +2247,7 @@ if((typeof module) !== 'undefined') { //grab the denominator and strip the multiplier and power. Store them in an array den_array = __.Simplify.strip(symbol.getDenom()); num_array = __.Simplify.strip(symbol.getNum()); + den = den_array.pop(); num = num_array.pop(); @@ -2252,9 +2256,12 @@ if((typeof module) !== 'undefined') { return symbol; nfact = __.Factor.factor(num); dfact = __.Factor.factor(den); + var n = __.Simplify.unstrip(num_array, nfact); var d = __.Simplify.unstrip(den_array, dfact); + var retval = _.divide(n, d); + return retval; } if(symbol.group === S) @@ -3739,7 +3746,7 @@ if((typeof module) !== 'undefined') { } den = _.expand(a.getDenom()); num = _.expand(a.getNum()); - + //simplify imaginary if(num.isImaginary() && den.isImaginary()) { retval = __.Simplify.complexSimp(num, den); @@ -3752,6 +3759,7 @@ if((typeof module) !== 'undefined') { if(retval.equals(symbol)) { return symbol; } + //otherwise simplify it some more return __.Simplify.simplify(retval); } diff --git a/nerdamer.core.js b/nerdamer.core.js index ba0ea5af..841c3fd4 100644 --- a/nerdamer.core.js +++ b/nerdamer.core.js @@ -16,7 +16,7 @@ var nerdamer = (function (imports) { "use strict"; //version ====================================================================== - var version = '1.1.4'; + var version = '1.1.5'; //inits ======================================================================== var _ = new Parser(); //nerdamer's parser diff --git a/package.json b/package.json index dfa9c60d..4cda38e9 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "description": "javascript light-weight symbolic math expression evaluator", "name": "nerdamer", "license": "MIT", - "version": "1.1.4", + "version": "1.1.5", "homepage": "http://nerdamer.com/", "directory": { "lib": "./" diff --git a/spec/algebra.spec.js b/spec/algebra.spec.js index 4d4c9eda..4c8b081f 100644 --- a/spec/algebra.spec.js +++ b/spec/algebra.spec.js @@ -470,6 +470,10 @@ describe('Algebra', function () { { given: 'factor(-1866240-311040*x^2-3265920*x+1120*x^8+150080*x^6+17610*x^7+2026080*x^4+2509920*x^3+30*x^9+738360*x^5)', expected: '10*(-1+x)*(1+x)*(3*x+4)*(6+x)^6' + }, + { + given: 'factor((7x^3+4x^2+x)/(12x^3+6x^2-2x))', + expected: '(1/2)*(-1+3*x+6*x^2)^(-1)*(1+4*x+7*x^2)' } ]; From 1211804ecf1acfe43b677bb970dd31a46dd43a68 Mon Sep 17 00:00:00 2001 From: Martin Donk Date: Mon, 20 Apr 2020 22:43:56 -0500 Subject: [PATCH 03/20] Simplify and Limit bug fix --- Algebra.js | 10 ++++++---- Calculus.js | 9 ++++++--- spec/calculus.spec.js | 8 ++++++++ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/Algebra.js b/Algebra.js index e88131a3..a47c64de 100644 --- a/Algebra.js +++ b/Algebra.js @@ -3767,9 +3767,11 @@ if((typeof module) !== 'undefined') { }, ratSimp: function(symbol) { if(symbol.group === CB) { - var den = __.Simplify.fracSimp(symbol.getDenom()); - var num = __.Simplify.fracSimp(symbol.getNum()); - symbol = _.divide(num, den); + var den = symbol.getDenom(); + var num = symbol.getNum().distributeMultiplier(); + var d = __.Simplify.fracSimp(den); + var n = __.Simplify.fracSimp(num); + symbol = _.divide(n, d); } return symbol; }, @@ -3793,7 +3795,7 @@ if((typeof module) !== 'undefined') { ////1. Try cos(x)^2+sin(x)^2 simplified = __.Simplify.trigSimp(symbol); - + //simplify common denominators simplified = __.Simplify.ratSimp(simplified); diff --git a/Calculus.js b/Calculus.js index 993ac36f..8d847a55 100644 --- a/Calculus.js +++ b/Calculus.js @@ -2113,7 +2113,7 @@ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { var t_symbol = _.expand(_.divide(ft, gt)); f = t_symbol.getNum(); g = t_symbol.getDenom(); - + } } while(indeterminate) @@ -2141,8 +2141,10 @@ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { else if(den_is_zero) { retval = __.Limit.diverges(); } - else + else { retval = _.divide(lim1, lim2); +// retval = __.Limit.limit(_.divide(f, g, x, lim, depth), x, lim, depth); + } return retval; }, @@ -2185,6 +2187,7 @@ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { }, limit: function(symbol, x, lim, depth) { //Simplify the symbol + symbol = core.Algebra.Simplify.simplify(symbol); depth = depth || 1; @@ -2518,4 +2521,4 @@ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { //link registered functions externally nerdamer.api(); -})(); \ No newline at end of file +})(); diff --git a/spec/calculus.spec.js b/spec/calculus.spec.js index e6f22c85..3712ee05 100644 --- a/spec/calculus.spec.js +++ b/spec/calculus.spec.js @@ -275,6 +275,14 @@ describe('calculus', function () { given: 'limit((4x^2-x)/(3x^2+x),x,∞)', expected: '4/3' }, + { + given: 'limit((x^(1/2)+x^(-1/2))/(x^(1/2)-x^(-1/2)),x,Infinity)', + expected: '1' + }, + { + given: 'limit((2sin(x)-sin(2x))/(x-sin(x)),x,0)', + expected: '6' + }, /* { given: 'limit((x+1)^(1+1/x)-x^(1+x),x, Infinity)', From 8debcbaa760125c6a2f6e1c1fb618c87877e88fc Mon Sep 17 00:00:00 2001 From: Martin Donk Date: Tue, 21 Apr 2020 09:01:21 -0500 Subject: [PATCH 04/20] Fixes and updates to limit --- Calculus.js | 23 ++++------------------- spec/calculus.spec.js | 12 +++++++++--- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/Calculus.js b/Calculus.js index 8d847a55..abae1721 100644 --- a/Calculus.js +++ b/Calculus.js @@ -2118,32 +2118,20 @@ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { } while(indeterminate) - //TODO: //REMEMBER: //- 1/cos(x) //n/0 is still possible since we only checked for 0/0 var den_is_zero = lim2.equals(0); var p = Number(gin.power); - - if(lim1.isConstant(true) && den_is_zero && core.Utils.isInt(p)) { - if(core.Utils.even(p)) { - retval = core.Symbol.infinity(); - //Add the correct sign to infinity based on the sign of the constant - if(lim1.lessThan(0)) { - retval.negate(); - } - } - else { - //The limit diverges for odd powers - retval = __.Limit.diverges(); - } + + if(lim.isConstant(true) && den_is_zero) { + retval = Symbol.infinity(core.Utils.even(p) && lim1.lessThan(0) ? -1 : undefined); } else if(den_is_zero) { retval = __.Limit.diverges(); } else { retval = _.divide(lim1, lim2); -// retval = __.Limit.limit(_.divide(f, g, x, lim, depth), x, lim, depth); } return retval; @@ -2425,10 +2413,6 @@ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { return; }; retval = __.Limit.limit(__.diff(symbol, x), x, lim, depth); - //rewrite the function to have a common denominator. - //TODO: This is soooo slow at the moment. -// symbol = core.Utils.toCommonDenominator(original); -// retval = __.Limit.limit(symbol, x, lim); } } } @@ -2522,3 +2506,4 @@ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { nerdamer.api(); })(); + \ No newline at end of file diff --git a/spec/calculus.spec.js b/spec/calculus.spec.js index 3712ee05..b04fd309 100644 --- a/spec/calculus.spec.js +++ b/spec/calculus.spec.js @@ -283,7 +283,14 @@ describe('calculus', function () { given: 'limit((2sin(x)-sin(2x))/(x-sin(x)),x,0)', expected: '6' }, - /* + { + given: 'limit((3*sin(x)-sin(2*x))/(x-sin(x)),x,0)', + expected: 'Infinity' + }, + { + given: 'limit(x/(x+1)^2, x, -1)', + expected: '-Infinity' + }, { given: 'limit((x+1)^(1+1/x)-x^(1+x),x, Infinity)', expected: '-Infinity' @@ -291,8 +298,7 @@ describe('calculus', function () { { given: 'limit(log(x),x, 0)', expected: 'Infinity' - }, - */ + } ]; for (var i = 0; i < testCases.length; ++i) { From 20cf703ac86c518a779fc60470bd6ac5c47d881c Mon Sep 17 00:00:00 2001 From: Martin Donk Date: Tue, 21 Apr 2020 12:51:11 -0500 Subject: [PATCH 05/20] Added derivative sinc(x) --- Calculus.js | 6 ++++-- spec/calculus.spec.js | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Calculus.js b/Calculus.js index abae1721..6b88ef94 100644 --- a/Calculus.js +++ b/Calculus.js @@ -602,6 +602,9 @@ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { 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; @@ -2505,5 +2508,4 @@ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { //link registered functions externally nerdamer.api(); -})(); - \ No newline at end of file +})(); \ No newline at end of file diff --git a/spec/calculus.spec.js b/spec/calculus.spec.js index b04fd309..7d5fc31a 100644 --- a/spec/calculus.spec.js +++ b/spec/calculus.spec.js @@ -142,6 +142,10 @@ describe('calculus', function () { { given: 'diff([sin(x), x^2, x],x)', expected: '[cos(x),2*x,1]' + }, + { + given: 'diff(sinc(a*x^3+b),x)', + expected: '3*((a*x^3+b)*cos(a*x^3+b)-sin(a*x^3+b))*(a*x^3+b)^(-2)*a*x^2' } ]; From 80ffa766761aff8060df871d98a1951ee513d696 Mon Sep 17 00:00:00 2001 From: Martin Donk Date: Tue, 21 Apr 2020 21:01:37 -0500 Subject: [PATCH 06/20] Added scientific number option --- nerdamer.core.js | 48 ++++++++++++++++++++++++++++++++++++++++++----- spec/core.spec.js | 4 ++++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/nerdamer.core.js b/nerdamer.core.js index 841c3fd4..c0ca2831 100644 --- a/nerdamer.core.js +++ b/nerdamer.core.js @@ -105,7 +105,11 @@ var nerdamer = (function (imports) { LOG: 'log', LOG10: 'log10', LOG10_LATEX: 'log_{10}', - MAX_EXP: 200000 + MAX_EXP: 200000, + //The number of scientific place to round to + SCIENTIFIC_MAX_DECIMAL_PLACES: 14, + //True if ints should not be converted to + SCIENTIFIC_IGNORE_ZERO_EXPONENTS: true }; (function () { @@ -2099,7 +2103,7 @@ var nerdamer = (function (imports) { if(asDecimal && typeof decp === 'undefined') decp = 16; - + function toString(obj) { switch (option) { @@ -2177,6 +2181,11 @@ var nerdamer = (function (imports) { var remainder = divmod.remainder; var operator = parts[0][0] === '-' || quotient.equals(0) || remainder.equals(0) ? '' : '+'; return (quotient.equals(0) ? '' : quotient.toString()) + operator + (remainder.equals(0) ? '' : (remainder.toString() + '/' + parts[1])); + case 'scientific': + wrapCondition = wrapCondition || function(str) { + return false; + } + return new Scientific(obj.valueOf()).toString(Settings.SCIENTIFIC_MAX_DECIMAL_PLACES); default: wrapCondition = wrapCondition || function (str) { return str.indexOf('/') !== -1; @@ -2701,7 +2710,15 @@ var nerdamer = (function (imports) { }, toString: function (n) { var coeff = typeof n === 'undefined' ? this.coeff : Scientific.round(this.coeff, n); - return (this.sign === -1 ? '-' : '') + coeff + 'e' + this.exponent; + + var c; + if(this.exponent === 0 && Settings.SCIENTIFIC_IGNORE_INTS) { + c = this.coeff; + } + else { + c = coeff + 'e' + this.exponent; + } + return (this.sign === -1 ? '-' : '') + c; } }; @@ -2791,6 +2808,15 @@ var nerdamer = (function (imports) { //set the coeff but first remove leading zeroes var coeff = Scientific.removeLeadingZeroes(n); this.coeff = coeff.charAt(0)+'.'+(coeff.substr(1, coeff.length) || '0'); + + //the coeff decimal places + var dec = this.coeff.split('.')[1] || ''; //if it's undefined or zero it's going to blank + + this.decp = dec === '0' ? 0 : dec.length; + //decimals + this.dec = d; + //wholes + this.wholes = w; return this; }, @@ -2826,8 +2852,20 @@ var nerdamer = (function (imports) { return n; }, toString: function(n) { - var coeff = typeof n === 'undefined' ? this.coeff : Scientific.round(this.coeff, n); - return (this.sign === -1 ? '-' : '' )+coeff+'e'+this.exponent; + var retval; + + if(Settings.SCIENTIFIC_IGNORE_ZERO_EXPONENTS && this.exponent === 0 && this.decp < n) { + if(this.decp === 0) + retval = this.wholes; + else + retval = this.coeff; + } + else { + var coeff = typeof n === 'undefined' ? this.coeff : Scientific.round(this.coeff, Math.min(n, this.decp || 1)); + retval = this.exponent === 0 ? coeff : coeff+'e'+this.exponent; + } + + return (this.sign === -1 ? '-' : '' )+retval; } }; diff --git a/spec/core.spec.js b/spec/core.spec.js index 067007dd..3d436e10 100644 --- a/spec/core.spec.js +++ b/spec/core.spec.js @@ -1777,6 +1777,10 @@ describe('Nerdamer core', function () { expect(nerdamer('matrix([1,2])/matrix([8,4])').toString()).toEqual('matrix([1/8,1/2])'); expect(nerdamer('16/matrix([8,4])').toString()).toEqual('matrix([2,4])'); }); + it('should perform scientific rounding', function() { + expect(nerdamer('12/7*x+cos(33333333333333333)-11/17').text('scientific')).toEqual('-6.47058823529412e-1+1.71428571428571*x+cos(3.33333333333333e16)'); + expect(nerdamer('7/(11*x-24*x^2)+cos(13/44)^(300/21)').text('scientific')).toEqual('7*(-2.4e1*x^2+1.1e1*x)^(-1)+cos(2.95454545454545e-1)^1.42857142857143e1'); + }); }); describe('Further arithmetic test cases', function () { From 335162ebad9096109ff33dd5020d617f4f18f281 Mon Sep 17 00:00:00 2001 From: Martin Donk Date: Wed, 22 Apr 2020 14:04:13 -0500 Subject: [PATCH 07/20] Update to ilt --- Algebra.js | 35 ++++++++++++++++++++++++++++++++--- Calculus.js | 5 +++-- Extra.js | 25 +++++++++++++++++++++---- nerdamer.core.js | 5 ++++- 4 files changed, 60 insertions(+), 10 deletions(-) diff --git a/Algebra.js b/Algebra.js index a47c64de..da252006 100644 --- a/Algebra.js +++ b/Algebra.js @@ -3435,7 +3435,9 @@ if((typeof module) !== 'undefined') { return [f_array, factors_vec, degrees]; }, partfrac: function(symbol, v, as_array) { + var vars = variables(symbol); + v = v || _.parse(vars[0]); //make wrt optional and assume first variable try { var num, den, factors, tfactors, ofactors, nterms, degrees, @@ -3519,11 +3521,35 @@ if((typeof module) !== 'undefined') { else retval = _.add(retval, term); }); - + //done return retval; } - catch(e){}; + catch(e){ + //try to group symbols + try { + if(symbol.isComposite()) { + //group denominators + var denominators = {}; + + symbol.each(function(x) { + var d = x.getDenom(); + var n = x.getNum(); + var e = denominators[d]; + denominators[d] = e ? _.add(e, n) : n; + }); + + var t = new Symbol(0); + + for(var x in denominators) { + t = _.add(t, _.divide(denominators[x], _.parse(x))); + } + + symbol = t; + } + } + catch(e2) {}; + }; return symbol; } @@ -3947,4 +3973,7 @@ if((typeof module) !== 'undefined') { } ]); nerdamer.api(); -})(); \ No newline at end of file +})(); + +var x = nerdamer('partfrac(15*(9+s^2)^(-1)*cos(1)+5*(9+s^2)^(-1)*s*sin(1),s)'); +console.log(x.toString()) \ No newline at end of file diff --git a/Calculus.js b/Calculus.js index 6b88ef94..13d2be40 100644 --- a/Calculus.js +++ b/Calculus.js @@ -800,7 +800,7 @@ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { partial_fractions.each(function(x) { if(!x.isLinear()) __.integration.stop(); - }) + }); partial_fractions.each(function(x) { result = _.add(result, __.integrate(x, dx, depth, opt)); }); @@ -2508,4 +2508,5 @@ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { //link registered functions externally nerdamer.api(); -})(); \ No newline at end of file +})(); + diff --git a/Extra.js b/Extra.js index 381c9ce8..e285bf89 100644 --- a/Extra.js +++ b/Extra.js @@ -142,7 +142,11 @@ if ((typeof module) !== 'undefined') { inverse: function (symbol, s_, t) { var input_symbol = symbol.clone(); return core.Utils.block('POSITIVE_MULTIPLIERS', function () { - if (symbol.group === S || symbol.group === CB || symbol.group === CP) { + //expand and get partial fractions + if(symbol.group === CB) + symbol = core.Algebra.PartFrac.partfrac(_.expand(symbol), s_); + + if (symbol.group === S || symbol.group === CB || symbol.isComposite()) { var finalize = function () { //put back the numerator retval = _.multiply(retval, num); @@ -189,10 +193,9 @@ if ((typeof module) !== 'undefined') { } else if (den.group === CP && den_p.equals(1)) { // a/(b*s-c) -> ae^(-bt) - if (f.x.isLinear() && !num.contains(s)) {console.log(f.a.toString(), f.b.toString()) + if (f.x.isLinear() && !num.contains(s)) { t = _.divide(t, f.a.clone()); - retval = _.parse(format('(({0})^({3}-1)*e^(-(({2})*({0}))/({1})))/(({3}-1)!*({1})^({3}))', t, f.a, f.b, den_p)) -// retval = _.pow(new Symbol('e'), _.multiply(t, f.b.negate())); + retval = _.parse(format('(({0})^({3}-1)*e^(-(({2})*({0}))/({1})))/(({3}-1)!*({1})^({3}))', t, f.a, f.b, den_p)); //wrap it up finalize(); } @@ -324,6 +327,13 @@ if ((typeof module) !== 'undefined') { } } } + else if(symbol.isComposite()) { + retval = new Symbol(0); + + symbol.each(function(x) { + retval = _.add(retval, __.LaPlace.inverse(x, s_, t)); + }, true); + } } if (!retval) @@ -576,3 +586,10 @@ if ((typeof module) !== 'undefined') { //link registered functions externally nerdamer.api(); }()); + +//Holding +//var ans = nerdamer('ilt(((s+1)*(s+2)*(s+3))^(-1), s, t)') + +var ans = nerdamer('ilt(8*(2*s^2+3)^(-2)*s^2,s,t)') +//var ans = nerdamer('ilt(4*(2*s^2+3)^(-1),s,t)') +console.log(ans.toString()) \ No newline at end of file diff --git a/nerdamer.core.js b/nerdamer.core.js index c0ca2831..4eacc6ac 100644 --- a/nerdamer.core.js +++ b/nerdamer.core.js @@ -11800,4 +11800,7 @@ var nerdamer = (function (imports) { if ((typeof module) !== 'undefined') { module.exports = nerdamer; -}; \ No newline at end of file +}; + +//complex erf +//force simpson defint From 0ae4094209a0df9e4aae79a68a72dc8346182b16 Mon Sep 17 00:00:00 2001 From: Martin Donk Date: Wed, 22 Apr 2020 14:34:02 -0500 Subject: [PATCH 08/20] Update ilt --- Algebra.js | 5 +---- Extra.js | 14 +++++--------- spec/algebra.spec.js | 4 ++++ spec/extra.spec.js | 9 +++++++++ 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Algebra.js b/Algebra.js index da252006..fddf9619 100644 --- a/Algebra.js +++ b/Algebra.js @@ -3973,7 +3973,4 @@ if((typeof module) !== 'undefined') { } ]); nerdamer.api(); -})(); - -var x = nerdamer('partfrac(15*(9+s^2)^(-1)*cos(1)+5*(9+s^2)^(-1)*s*sin(1),s)'); -console.log(x.toString()) \ No newline at end of file +})(); \ No newline at end of file diff --git a/Extra.js b/Extra.js index e285bf89..5028412a 100644 --- a/Extra.js +++ b/Extra.js @@ -54,6 +54,8 @@ if ((typeof module) !== 'undefined') { LaPlace: { //Using: integral_0^oo f(t)*e^(-s*t) dt transform: function (symbol, t, s) { + symbol = symbol.clone(); + t = t.toString(); //First try a lookup for a speed boost symbol = Symbol.unwrapSQRT(symbol, true); @@ -121,7 +123,8 @@ if ((typeof module) !== 'undefined') { var integration_expr = _.parse('e^(-' + s + '*' + u + ')*' + sym); retval = core.Calculus.integrate(integration_expr, u); if (retval.hasIntegral()) - _.error('Unable to compute transform'); + return _.symfunction('laplace', arguments); +// _.error('Unable to compute transform'); retval = retval.sub(t, 0); retval = _.expand(_.multiply(retval, new Symbol(-1))); retval = retval.sub(u, t); @@ -585,11 +588,4 @@ if ((typeof module) !== 'undefined') { //link registered functions externally nerdamer.api(); -}()); - -//Holding -//var ans = nerdamer('ilt(((s+1)*(s+2)*(s+3))^(-1), s, t)') - -var ans = nerdamer('ilt(8*(2*s^2+3)^(-2)*s^2,s,t)') -//var ans = nerdamer('ilt(4*(2*s^2+3)^(-1),s,t)') -console.log(ans.toString()) \ No newline at end of file +}()); \ No newline at end of file diff --git a/spec/algebra.spec.js b/spec/algebra.spec.js index 4c8b081f..52e51d62 100644 --- a/spec/algebra.spec.js +++ b/spec/algebra.spec.js @@ -562,6 +562,10 @@ describe('Algebra', function () { given: 'partfrac((3*x^2-3*x-8)/((x-5)*(x^2+x-4)),x)', expected: '(-4+x+x^2)^(-1)*x+2*(-5+x)^(-1)' }, + { + given: 'partfrac(15*(9+s^2)^(-1)*cos(1)+5*(9+s^2)^(-1)*s*sin(1),s)', + expected: '(15*cos(1)+5*s*sin(1))*(9+s^2)^(-1)' + }, ]; for (var i = 0; i < testCases.length; ++i) { diff --git a/spec/extra.spec.js b/spec/extra.spec.js index 106cb5bb..dea36ba0 100644 --- a/spec/extra.spec.js +++ b/spec/extra.spec.js @@ -62,6 +62,10 @@ describe('calculus', function () { given: 'laplace(t^2*e^(a*t), t, s)', expected: '-2*(-s+a)^(-3)' }, + { + given: 'laplace(6*t*e^(-9*t)*sin(6*t), t, s)', + expected: '-72*(-9-s)^(-3)*(1+36*(-9-s)^(-2))^(-2)' + }, { //NOTE: this unit test was incorrect before. I don't know how this was missed. given: 'laplace(sinh(t)*e^t, t, s)', @@ -120,6 +124,11 @@ describe('calculus', function () { { given: 'ilt((5*(sin(1)*s+3*cos(1)))/(s^2+9),s, t)', expected: '5*cos(1)*sin(3*t)+5*cos(3*t)*sin(1)' + }, + { + //TODO: Although this is computed correctly we need to get rid of the silly factorial(0)^(-1) + given: 'ilt(((s+1)*(s+2)*(s+3))^(-1), s, t)', + expected: '(1/2)*e^(-3*t)*factorial(0)^(-1)+(1/2)*e^(-t)*factorial(0)^(-1)-e^(-2*t)*factorial(0)^(-1)' } ]; From cf44784ca1067e6fdcca6dd7a915688dbeec3898 Mon Sep 17 00:00:00 2001 From: Martin Donk Date: Wed, 22 Apr 2020 17:20:32 -0500 Subject: [PATCH 09/20] update to erf --- Calculus.js | 3 +-- nerdamer.core.js | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/Calculus.js b/Calculus.js index 13d2be40..a9fb311b 100644 --- a/Calculus.js +++ b/Calculus.js @@ -2508,5 +2508,4 @@ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { //link registered functions externally nerdamer.api(); -})(); - +})(); \ No newline at end of file diff --git a/nerdamer.core.js b/nerdamer.core.js index 4eacc6ac..0d8d09b1 100644 --- a/nerdamer.core.js +++ b/nerdamer.core.js @@ -4679,6 +4679,34 @@ var nerdamer = (function (imports) { im = _.parse(Math.atan2(i, r)); return _.add(re, _.multiply(Symbol.imaginary(), im)); }, + erf(symbol, n) { + //Do nothing for now. Revisit this in the future. + return _.symfunction('erf', [symbol]); + + n = n || 30; + + var f = function(R, I) { + return block('PARSE2NUMBER', function() { + var retval = new Symbol(0); + for(var i=0; i Date: Thu, 23 Apr 2020 18:09:57 -0500 Subject: [PATCH 10/20] =?UTF-8?q?Update=20=F0=9F=91=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Algebra.js | 31 ++++++++++++++++++++++++------- Calculus.js | 2 +- nerdamer.core.js | 7 +------ 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/Algebra.js b/Algebra.js index fddf9619..b22f29eb 100644 --- a/Algebra.js +++ b/Algebra.js @@ -2141,8 +2141,10 @@ if((typeof module) !== 'undefined') { if(retval.group === CB) { var t = new Symbol(1); + var p = _.parse(retval.power); //store the multiplier and strip it var m = _.parse(retval.multiplier); + retval.toUnitMultiplier(); /* * NOTE: for sign issues with factor START DEBUGGING HERE @@ -2165,9 +2167,10 @@ if((typeof module) !== 'undefined') { t = _.multiply(t, factored); } }); - //put back the multiplier - retval = _.multiply(m, t); + //put back the multiplier and power + retval = _.pow(_.multiply(m, t), p); } + return retval; }, quadFactor: function(symbol, factors) { @@ -2242,7 +2245,9 @@ if((typeof module) !== 'undefined') { //make a copy of the symbol to return if something goes wrong var untouched = symbol.clone(); try { - if(symbol.group === CB) { + if(symbol.group === CB) { + var p = _.parse(symbol.power); + var den_array, num_array, den, num, dfact, nfact; //grab the denominator and strip the multiplier and power. Store them in an array den_array = __.Simplify.strip(symbol.getDenom()); @@ -2250,7 +2255,7 @@ if((typeof module) !== 'undefined') { den = den_array.pop(); num = num_array.pop(); - + //if the numerator equals the symbol then we've hit the simplest form and then we're done if(num.equals(symbol)) return symbol; @@ -3715,9 +3720,21 @@ if((typeof module) !== 'undefined') { retval = _.pow(_.multiply(new Symbol(symbol.multiplier), sym), new Symbol(symbol.power)); } else if(symbol.group === CB) { - //try for tangent + var n = symbol.getNum(); var d = symbol.getDenom(); + +// if(n.isComposite() || d.isComposite()) { +// if(n.isComposite()) +// n = __.Simplify.trigSimp(n); +// +// if(d.isComposite()) +// d = __.Simplify.trigSimp(d); +// +// sym_array = __.Simplify.strip(_.divide(n, d)); +// } + + //try for tangent if(n.fname === 'sin' && d.fname === 'cos' && n.args[0].equals(d.args[0]) && n.power.equals(d.power)) { retval =_.parse(core.Utils.format('({0})*({1})*tan({2})^({3})', d.multiplier, n.multiplier, n.args[0], n.power)); } @@ -3821,7 +3838,7 @@ if((typeof module) !== 'undefined') { ////1. Try cos(x)^2+sin(x)^2 simplified = __.Simplify.trigSimp(symbol); - + //simplify common denominators simplified = __.Simplify.ratSimp(simplified); @@ -3973,4 +3990,4 @@ if((typeof module) !== 'undefined') { } ]); nerdamer.api(); -})(); \ No newline at end of file +})(); diff --git a/Calculus.js b/Calculus.js index a9fb311b..913dc1e7 100644 --- a/Calculus.js +++ b/Calculus.js @@ -2508,4 +2508,4 @@ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { //link registered functions externally nerdamer.api(); -})(); \ No newline at end of file +})(); diff --git a/nerdamer.core.js b/nerdamer.core.js index 0d8d09b1..a6b6d2fc 100644 --- a/nerdamer.core.js +++ b/nerdamer.core.js @@ -11844,9 +11844,4 @@ var nerdamer = (function (imports) { if ((typeof module) !== 'undefined') { module.exports = nerdamer; -}; - -//complex erf -//force simpson defint -var ans = nerdamer('erf(1+i)'); -console.log(ans.toString()); \ No newline at end of file +}; \ No newline at end of file From c9948d571f391cd97d0720d2196b2120f929fb73 Mon Sep 17 00:00:00 2001 From: Martin Donk Date: Tue, 28 Apr 2020 18:50:29 -0500 Subject: [PATCH 11/20] Fix #541 --- Algebra.js | 24 ++++++++-------- Solve.js | 4 +++ all.js | 2 +- nerdamer.core.js | 73 +++++++++++++++++++++++++++++------------------- 4 files changed, 63 insertions(+), 40 deletions(-) diff --git a/Algebra.js b/Algebra.js index b22f29eb..e61cf2a0 100644 --- a/Algebra.js +++ b/Algebra.js @@ -2502,7 +2502,7 @@ if((typeof module) !== 'undefined') { var poly = new Polynomial(symbol, variable), cnst = poly.coeffs[0], cfactors = core.Math2.ifactor(cnst), - roots = __.proots(symbol); + roots = __.proots(symbol); for(var i=0; i 1) { @@ -1239,6 +1241,7 @@ if ((typeof module) !== 'undefined') { } } } + } else { try { @@ -1272,6 +1275,7 @@ if ((typeof module) !== 'undefined') { console.log(e); } } + } else { //The idea here is to go through the equation and collect the coefficients diff --git a/all.js b/all.js index a6f74196..5c996164 100644 --- a/all.js +++ b/all.js @@ -13,4 +13,4 @@ require('./Solve.js'); require('./Extra.js'); //export nerdamer -module.exports = nerdamer; +module.exports = nerdamer; \ No newline at end of file diff --git a/nerdamer.core.js b/nerdamer.core.js index a6b6d2fc..57473cf3 100644 --- a/nerdamer.core.js +++ b/nerdamer.core.js @@ -1570,39 +1570,55 @@ var nerdamer = (function (imports) { else factors[e] = (factors[e] || 0) + 1; }; + + try { + //set a safety + var max = 1e3; + var safety = 0; + + while (!n.abs().equals(1)) { + if (n.isPrime()) { + add(n); + break; + } + else { + function rho(c) { + var xf = new bigInt(c), + cz = 2, + x = new bigInt(c), + factor = new bigInt(1); + + while (factor.equals(1)) { + for (var i = 0; i <= cz && factor.equals(1); i++) { + //trigger the safety + if(safety++ > max) + throw new Error('stopping'); + + x = x.pow(2).add(1).mod(n); + factor = bigInt.gcd(x.minus(xf).abs(), n); + } - while (!n.abs().equals(1)) { - if (n.isPrime()) { - add(n); - break; - } - else { - function rho(c) { - var xf = new bigInt(c), - cz = 2, - x = new bigInt(c), - factor = new bigInt(1); - - while (factor.equals(1)) { - for (var i = 0; i <= cz && factor.equals(1); i++) { - x = x.pow(2).add(1).mod(n); - factor = bigInt.gcd(x.minus(xf).abs(), n); + cz = cz * 2; + xf = x; } - - cz = cz * 2; - xf = x; - } - if (factor.equals(n)) { - return rho(c + 1); + if (factor.equals(n)) { + return rho(c + 1); + } + return factor; } - return factor; + var factor = rho(2); + add(factor); + /*divide out the factor*/ + n = n.divide(factor); } - var factor = rho(2); - add(factor); - /*divide out the factor*/ - n = n.divide(factor); } } + catch(e) { + //reset factors + factors = {}; + add(n); + } + } /*put the sign back*/ @@ -11844,4 +11860,5 @@ var nerdamer = (function (imports) { if ((typeof module) !== 'undefined') { module.exports = nerdamer; -}; \ No newline at end of file +}; + From d29b21fca345d0396f753ec9aea26773814a65ea Mon Sep 17 00:00:00 2001 From: Hovhannes Menejan Date: Wed, 17 Jun 2020 09:54:27 -0700 Subject: [PATCH 12/20] Starting work on callPeekers --- nerdamer.core.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nerdamer.core.js b/nerdamer.core.js index 57473cf3..e0244537 100644 --- a/nerdamer.core.js +++ b/nerdamer.core.js @@ -51,6 +51,10 @@ var nerdamer = (function (imports) { var CUSTOM_OPERATORS = {}; var Settings = { + //Enables/Disables call peekers. False means callPeekers are disabled and true means callPeekers are enabled. + callPeekers: false, + + //the max number up to which to cache primes. Making this too high causes performance issues init_primes: 1000, From 9847f0c560ce2d16ce4617d8f766d60d48e2e1ec Mon Sep 17 00:00:00 2001 From: Hovhannes Menejan Date: Wed, 17 Jun 2020 09:57:39 -0700 Subject: [PATCH 13/20] Added settings.callPeekers which is false by default. While settings.callPeekers is set to false, this.callPeekers function will not execute any code --- nerdamer.core.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/nerdamer.core.js b/nerdamer.core.js index e0244537..c93e0ce8 100644 --- a/nerdamer.core.js +++ b/nerdamer.core.js @@ -5984,13 +5984,15 @@ var nerdamer = (function (imports) { }; this.callPeekers = function(name) { - var peekers = this.peekers[name]; - //remove the first items and stringify - var args = arguments2Array(arguments).slice(1).map(stringify); - //call each one of the peekers - for(var i=0; i Date: Wed, 17 Jun 2020 10:12:28 -0700 Subject: [PATCH 14/20] Fixed a small typo --- nerdamer.core.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nerdamer.core.js b/nerdamer.core.js index c93e0ce8..0828b078 100644 --- a/nerdamer.core.js +++ b/nerdamer.core.js @@ -5984,7 +5984,7 @@ var nerdamer = (function (imports) { }; this.callPeekers = function(name) { - if (settings.callPeekers) { + if (Settings.callPeekers) { var peekers = this.peekers[name]; //remove the first items and stringify var args = arguments2Array(arguments).slice(1).map(stringify); From 3a640e6d7ac6420c4e5888bca65712c653bf6f83 Mon Sep 17 00:00:00 2001 From: Martin Donk Date: Tue, 23 Jun 2020 21:12:50 -0500 Subject: [PATCH 15/20] Fixed sign bug in text --- nerdamer.core.js | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/nerdamer.core.js b/nerdamer.core.js index 0828b078..cee03acb 100644 --- a/nerdamer.core.js +++ b/nerdamer.core.js @@ -2210,7 +2210,7 @@ var nerdamer = (function (imports) { wrapCondition = wrapCondition || function (str) { return str.indexOf('/') !== -1; }; - + return obj.toString(); } } @@ -2222,11 +2222,12 @@ var nerdamer = (function (imports) { sign = '', group = obj.group || useGroup, value = obj.value; + //if the value is to be used as a hash then the power and multiplier need to be suppressed if (!asHash) { //use asDecimal to get the object back as a decimal var om = toString(obj.multiplier); - if (om == '-1') { + if (om == '-1' && String(obj.multiplier) === '-1') { sign = '-'; om = '1'; } @@ -2252,9 +2253,8 @@ var nerdamer = (function (imports) { multiplier = ''; //round if requested var m = decp && asDecimal ? obj.multiplier.toDecimal(decp) : toString(obj.multiplier); - //if it's numerical then all we need is the multiplier - value = obj.multiplier == '-1' ? '1' : m; + value = String(obj.multiplier) == '-1' ? '1' : m; power = ''; break; case PL: @@ -2332,8 +2332,9 @@ var nerdamer = (function (imports) { value = inBrackets(value); } - if(decp && (option === 'decimal' || option === 'decimals' && multiplier)) - multiplier = nround(multiplier, decp) + if(decp && (option === 'decimal' || option === 'decimals' && multiplier)) { + multiplier = nround(multiplier, decp); + } //add the sign back var c = sign + multiplier; @@ -2470,7 +2471,9 @@ var nerdamer = (function (imports) { * @returns {Expression} */ evaluate: function () { + var first_arg = arguments[0], expression, idx = 1; + //Enable getting of expressions using the % so for example %1 should get the first expression if (typeof first_arg === 'string') { expression = (first_arg.charAt(0) === '%') ? Expression.getExpression(first_arg.substr(1)).text() : first_arg; @@ -2484,10 +2487,12 @@ var nerdamer = (function (imports) { } var subs = arguments[idx] || {}; - - return new Expression(block('PARSE2NUMBER', function () { + + var retval = new Expression(block('PARSE2NUMBER', function () { return _.parse(expression, subs); }, true)); + + return retval; }, /** * Converts a symbol to a JS function. Pass in an array of variables to use that order instead of @@ -11866,5 +11871,4 @@ var nerdamer = (function (imports) { if ((typeof module) !== 'undefined') { module.exports = nerdamer; -}; - +}; \ No newline at end of file From ab76d8d925498e2dd71307b924c6a4645fc6d8d7 Mon Sep 17 00:00:00 2001 From: Martin Donk Date: Wed, 1 Jul 2020 09:00:17 -0500 Subject: [PATCH 16/20] Fix sqrt of negative numbers bug --- nerdamer.core.js | 8 +++++++- package.json | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/nerdamer.core.js b/nerdamer.core.js index cee03acb..c4e094c6 100644 --- a/nerdamer.core.js +++ b/nerdamer.core.js @@ -7274,6 +7274,11 @@ var nerdamer = (function (imports) { symbol.setPower(symbol.power.multiply(new Frac(0.5))); retval = symbol; } + else if(symbol.multiplier < 0 && symbol.group === S) { + var a = _.parse(symbol.multiplier).negate(); + var b = _.parse(symbol).toUnitMultiplier().negate(); + retval = _.multiply(_.symfunction(Settings.SQRT, [b]), sqrt(a)); + } else { //Related to issue #401. Since sqrt(a)*sqrt(b^-1) relates in issues, we'll change the form //to sqrt(a)*sqrt(b)^1 for better simplification @@ -7362,6 +7367,7 @@ var nerdamer = (function (imports) { if (is_negative && Settings.PARSE2NUMBER) return _.parse(retval); + return retval; } @@ -11871,4 +11877,4 @@ var nerdamer = (function (imports) { if ((typeof module) !== 'undefined') { module.exports = nerdamer; -}; \ No newline at end of file +}; diff --git a/package.json b/package.json index 4cda38e9..0192f0b8 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,6 @@ "test": "jasmine" }, "devDependencies": { - "jasmine": "^2.6.0" + "jasmine": "^2.99.0" } } From b4a4d80ee58cca8bdea27fbf6df6ee8a7f4b5483 Mon Sep 17 00:00:00 2001 From: Martin Donk Date: Wed, 1 Jul 2020 12:53:29 -0500 Subject: [PATCH 17/20] Update sqrt --- nerdamer.core.js | 7 +++++-- spec/core.spec.js | 5 +++++ spec/solve.spec.js | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/nerdamer.core.js b/nerdamer.core.js index c4e094c6..a7a25868 100644 --- a/nerdamer.core.js +++ b/nerdamer.core.js @@ -7244,6 +7244,9 @@ var nerdamer = (function (imports) { else if (symbol.isImaginary()) { return complex.sqrt(symbol); } + else { + return _.symfunction('sqrt', [symbol]); + } } var img, retval, @@ -7425,7 +7428,7 @@ var nerdamer = (function (imports) { function pfactor(symbol) { //Fix issue #458 | nerdamer("sqrt(1-(3.3333333550520926e-7)^2)").evaluate().text() //More Big Number issues >:( - if (symbol.greaterThan(9.999999999998891e+41)) + if (symbol.greaterThan(9.999999999998891e+41) || symbol.equals(-1)) return symbol; //Fix issue #298 if (symbol.equals(Math.PI)) @@ -11877,4 +11880,4 @@ var nerdamer = (function (imports) { if ((typeof module) !== 'undefined') { module.exports = nerdamer; -}; +}; \ No newline at end of file diff --git a/spec/core.spec.js b/spec/core.spec.js index 3d436e10..5a0e944f 100644 --- a/spec/core.spec.js +++ b/spec/core.spec.js @@ -915,6 +915,11 @@ describe('Nerdamer core', function () { expect(nerdamer('expand((sqrt(7)+3sqrt(2))(sqrt(7)-3sqrt(2)))').toString()).toEqual('-11'); expect(nerdamer('3sqrt(2)*2sqrt(6)').toString()).toEqual('12*sqrt(3)'); }); + it('should handle square roots of negative values', function() { + expect(nerdamer('sqrt(-x)').evaluate().text()).toEqual('sqrt(-x)'); + expect(nerdamer('sqrt(-0.5*x)').evaluate().text()).toEqual('0.7071067811865475*sqrt(-x)'); + expect(nerdamer('sqrt(-4)').evaluate().text()).toEqual('2*i'); + }); it('expand square roots', function () { // given var testCases = [ diff --git a/spec/solve.spec.js b/spec/solve.spec.js index 568b3ceb..8aaa5ab3 100644 --- a/spec/solve.spec.js +++ b/spec/solve.spec.js @@ -277,7 +277,7 @@ describe('Solve', function () { //NOTE: contains duplicates it('should solve functions with factorials', function() { - expect(nerdamer('solve(x!-x^2,x)').text('decimals', 20)).toEqual('[-2.200391782610595,-4.010232827899529,-2.938361683501947,1,1.000000000000001,1.000000000000001,3.562382285390896,3.562382285390897,0.9999999999999910,1.000000000000000]'); + expect(nerdamer('solve(x!-x^2,x)').text('decimals', 20)).toEqual('[-2.200391782610595,-4.010232827899529,-2.938361683501947,1,1.000000000000001,1.000000000000001,3.562382285390896,3.562382285390897,1.000000000000000]'); }); it('should solve for variables other than x', function() { From 1c1710a1228e6c0eeb304d0b8931f91c477d5b45 Mon Sep 17 00:00:00 2001 From: Martin Donk Date: Fri, 3 Jul 2020 18:35:19 -0500 Subject: [PATCH 18/20] more sqrt --- all.js | 2 +- nerdamer.core.js | 16 ++++++++++++---- spec/core.spec.js | 1 + spec/solve.spec.js | 2 +- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/all.js b/all.js index 5c996164..a6f74196 100644 --- a/all.js +++ b/all.js @@ -13,4 +13,4 @@ require('./Solve.js'); require('./Extra.js'); //export nerdamer -module.exports = nerdamer; \ No newline at end of file +module.exports = nerdamer; diff --git a/nerdamer.core.js b/nerdamer.core.js index a7a25868..a2d8eb5c 100644 --- a/nerdamer.core.js +++ b/nerdamer.core.js @@ -7244,7 +7244,7 @@ var nerdamer = (function (imports) { else if (symbol.isImaginary()) { return complex.sqrt(symbol); } - else { + else if(symbol.group === S) { return _.symfunction('sqrt', [symbol]); } } @@ -7391,14 +7391,20 @@ var nerdamer = (function (imports) { */ //TODO: this method needs serious optimization function nthroot(num, p, prec, asbig) { + if(num < 0 && even(p)) + throw new Error('Cannot calculate nthroot of negative number for even powers'); + if (typeof asbig === 'undefined') asbig = true; prec = prec || 25; + if (!isSymbol(p)) p = _.parse(p); + if (isInt(num) && p.isConstant()) { var sign = num.sign(), x; + num = abs(num); //remove the sign var idx = num + '-' + p; if (idx in Settings.CACHE.roots) { @@ -7413,9 +7419,11 @@ var nerdamer = (function (imports) { x = Math2.nthroot(num, p); } if (isInt(x) || Settings.PARSE2NUMBER) { - if (asbig) - return new Symbol(x); - return new Symbol(x.toDecimal(prec)); + if (asbig) { + console.log(sign) + return _.multiply(new Symbol(sign), new Symbol(x)); + } + return _.multiply(new Symbol(sign), new Symbol(x.toDecimal(prec))); } } diff --git a/spec/core.spec.js b/spec/core.spec.js index 5a0e944f..1c3cb940 100644 --- a/spec/core.spec.js +++ b/spec/core.spec.js @@ -919,6 +919,7 @@ describe('Nerdamer core', function () { expect(nerdamer('sqrt(-x)').evaluate().text()).toEqual('sqrt(-x)'); expect(nerdamer('sqrt(-0.5*x)').evaluate().text()).toEqual('0.7071067811865475*sqrt(-x)'); expect(nerdamer('sqrt(-4)').evaluate().text()).toEqual('2*i'); + expect(nerdamer('sqrt(-pi)').evaluate().text()).toEqual('1.7724538509055163*i'); }); it('expand square roots', function () { // given diff --git a/spec/solve.spec.js b/spec/solve.spec.js index 8aaa5ab3..568b3ceb 100644 --- a/spec/solve.spec.js +++ b/spec/solve.spec.js @@ -277,7 +277,7 @@ describe('Solve', function () { //NOTE: contains duplicates it('should solve functions with factorials', function() { - expect(nerdamer('solve(x!-x^2,x)').text('decimals', 20)).toEqual('[-2.200391782610595,-4.010232827899529,-2.938361683501947,1,1.000000000000001,1.000000000000001,3.562382285390896,3.562382285390897,1.000000000000000]'); + expect(nerdamer('solve(x!-x^2,x)').text('decimals', 20)).toEqual('[-2.200391782610595,-4.010232827899529,-2.938361683501947,1,1.000000000000001,1.000000000000001,3.562382285390896,3.562382285390897,0.9999999999999910,1.000000000000000]'); }); it('should solve for variables other than x', function() { From bbe5f4f9045cbcaddf30cec0200e4d9b7e85fbb8 Mon Sep 17 00:00:00 2001 From: Martin Donk Date: Sat, 4 Jul 2020 16:44:09 -0500 Subject: [PATCH 19/20] Fix nthroot Added cbrt Some fixes to trig identities --- Calculus.js | 50 +++++++++++++++++++--- nerdamer.core.js | 107 ++++++++++++++++++++++++++++++++++------------ spec/core.spec.js | 19 ++++++++ 3 files changed, 141 insertions(+), 35 deletions(-) diff --git a/Calculus.js b/Calculus.js index 913dc1e7..278be3d2 100644 --- a/Calculus.js +++ b/Calculus.js @@ -157,6 +157,21 @@ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { 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; @@ -253,9 +268,12 @@ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { retval = new Symbol(1); for(var i=0, l=arr.length; i 0^(1/0) => undefined + if(p.equals(0)) { + throw new UndefinedError('Unable to calculate nthroots of zero'); + } + + //Stop computation if it negative and even since we have an imaginary result if(num < 0 && even(p)) throw new Error('Cannot calculate nthroot of negative number for even powers'); + //return non numeric values unevaluated + if(!num.isConstant(true)) { + return _.symfunction('nthroot', arguments); + } + + //evaluate numeric values + if(num.group !== N) { + num = evaluate(num); + } + + //default is to return a big value if (typeof asbig === 'undefined') asbig = true; + prec = prec || 25; - if (!isSymbol(p)) - p = _.parse(p); + var sign = num.sign(); + var retval; + var ans; - if (isInt(num) && p.isConstant()) { - var sign = num.sign(), - x; - + if(sign < 0) { num = abs(num); //remove the sign - var idx = num + '-' + p; - if (idx in Settings.CACHE.roots) { - x = new bigInt(Settings.CACHE.roots[idx]); - if (!even(p)) - x = x.multiply(sign); + } + + if (isInt(num) && p.isConstant()) { + + if (num < 18446744073709551616) { + //2^64 + ans = Frac.create(Math.pow(num, 1 / p)); } else { - if (num < 18446744073709551616) //2^64 - x = Frac.create(Math.pow(num, 1 / p)); - else - x = Math2.nthroot(num, p); - } - if (isInt(x) || Settings.PARSE2NUMBER) { - if (asbig) { - console.log(sign) - return _.multiply(new Symbol(sign), new Symbol(x)); - } - return _.multiply(new Symbol(sign), new Symbol(x.toDecimal(prec))); + ans = Math2.nthroot(num, p); } - } - if (Number(p) === 2) - return _.sqrt(num); + var retval; + if (asbig) { + retval = new Symbol(ans); + } + retval = new Symbol(ans.toDecimal(prec)); - return _.symfunction('nthroot', arguments); + return _.multiply(new Symbol(sign), retval); + } } function pfactor(symbol) { @@ -8328,6 +8378,7 @@ var nerdamer = (function (imports) { this.round = round; this.clean = clean; this.sqrt = sqrt; + this.cbrt = cbrt; this.abs = abs; this.log = log; this.rationalize = rationalize; @@ -11888,4 +11939,4 @@ var nerdamer = (function (imports) { if ((typeof module) !== 'undefined') { module.exports = nerdamer; -}; \ No newline at end of file +}; diff --git a/spec/core.spec.js b/spec/core.spec.js index 1c3cb940..af5cf896 100644 --- a/spec/core.spec.js +++ b/spec/core.spec.js @@ -1787,6 +1787,25 @@ describe('Nerdamer core', function () { expect(nerdamer('12/7*x+cos(33333333333333333)-11/17').text('scientific')).toEqual('-6.47058823529412e-1+1.71428571428571*x+cos(3.33333333333333e16)'); expect(nerdamer('7/(11*x-24*x^2)+cos(13/44)^(300/21)').text('scientific')).toEqual('7*(-2.4e1*x^2+1.1e1*x)^(-1)+cos(2.95454545454545e-1)^1.42857142857143e1'); }); + it('should throw for even negative powers in nthroots', function() { + expect(function() {nerdamer('nthroot(-9, 2)').evaluate(); }).toThrowError(); + }); + it('should throw for zero powers in nthroots', function() { + expect(function() {nerdamer('nthroot(-9, 0)').evaluate(); }).toThrowError(); + }); + it('should calculate nthroots', function() { + expect(nerdamer('nthroot(-8, 3)').evaluate().text()).toEqual('-2'); + expect(nerdamer('nthroot(sqrt(64), sqrt(9))').evaluate().text()).toEqual('2'); + expect(nerdamer('nthroot(-1, 3)').evaluate().text()).toEqual('-1'); + expect(nerdamer('nthroot(-7, 3)').evaluate().text()).toEqual('-1.912931182772388873'); + expect(nerdamer('nthroot(-x*8, 3)').evaluate().text()).toEqual('nthroot(-8*x,3)'); + }); + it('should calculate cube roots', function () { + expect(nerdamer('cbrt(8)').evaluate().text()).toEqual('2'); + expect(nerdamer('cbrt(x)').evaluate().text()).toEqual('cbrt(x)'); + expect(nerdamer('cbrt(27*x^3)').evaluate().text()).toEqual('3*x'); + expect(nerdamer('cbrt((y^3*x^3))').evaluate().text()).toEqual('x*y'); + }); }); describe('Further arithmetic test cases', function () { From 26dfc047678f737f4c609bd558d77f4d5d41e889 Mon Sep 17 00:00:00 2001 From: Martin Donk Date: Sun, 5 Jul 2020 14:38:34 -0500 Subject: [PATCH 20/20] Fix #542 --- Algebra.js | 14 ++------------ Calculus.js | 30 ++++++++++++++++++++++++++---- nerdamer.core.js | 44 +++++++++++++++++++++++++++++++++++--------- spec/algebra.spec.js | 2 +- 4 files changed, 64 insertions(+), 26 deletions(-) diff --git a/Algebra.js b/Algebra.js index e61cf2a0..85e518c2 100644 --- a/Algebra.js +++ b/Algebra.js @@ -3725,17 +3725,7 @@ if((typeof module) !== 'undefined') { var n = symbol.getNum(); var d = symbol.getDenom(); - -// if(n.isComposite() || d.isComposite()) { -// if(n.isComposite()) -// n = __.Simplify.trigSimp(n); -// -// if(d.isComposite()) -// d = __.Simplify.trigSimp(d); -// -// sym_array = __.Simplify.strip(_.divide(n, d)); -// } - + //try for tangent if(n.fname === 'sin' && d.fname === 'cos' && n.args[0].equals(d.args[0]) && n.power.equals(d.power)) { retval =_.parse(core.Utils.format('({0})*({1})*tan({2})^({3})', d.multiplier, n.multiplier, n.args[0], n.power)); @@ -3992,4 +3982,4 @@ if((typeof module) !== 'undefined') { } ]); nerdamer.api(); -})(); \ No newline at end of file +})(); diff --git a/Calculus.js b/Calculus.js index 278be3d2..db738db9 100644 --- a/Calculus.js +++ b/Calculus.js @@ -1666,9 +1666,31 @@ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { } //TODO: In progress else if((fn1 === SIN || fn1 === COS) && (fn2 === SIN || fn2 === COS)) { - if(symbols[1].isLinear() && symbols[0].isLinear()) { - var transformed = trigTransform(symbols); - retval = __.integrate(_.expand(transformed), dx, depth); + + if(sym1.isLinear() && sym2.isLinear()) { + //if in the form cos(a*x)*sin(b*x) + if(sym1.args[0].isLinear() && sym2.args[0].isLinear()) { + //use identity (sin(b*x+a*x)+sin(b*x-a*x))/2 + var ax, bx; + if(fn2 === SIN) { + ax = sym1.args[0]; + bx = sym2.args[0]; + } + else { + bx = sym1.args[0]; + ax = sym2.args[0]; + } + + //make the transformation + f = _.parse(format('(sin(({1})+({0}))+sin(({1})-({0})))/2', ax.toString(), bx.toString())); + + //integrate it + retval = __.integrate(f, dx, depth); + } + else { + var transformed = trigTransform(symbols); + retval = __.integrate(_.expand(transformed), dx, depth); + } } else { var transformed = new Symbol(1); @@ -2544,4 +2566,4 @@ if((typeof module) !== 'undefined' && typeof nerdamer === 'undefined') { //link registered functions externally nerdamer.api(); -})(); \ No newline at end of file +})(); diff --git a/nerdamer.core.js b/nerdamer.core.js index 5f15e1eb..5f3df22d 100644 --- a/nerdamer.core.js +++ b/nerdamer.core.js @@ -8890,17 +8890,43 @@ var nerdamer = (function (imports) { b = new Symbol(1); } } - if (a.fname === FACTORIAL && b.fname === FACTORIAL) { - if (a.power.equals(1) && b.power.equals(-1) && _.subtract(v.clone(), u.clone()).equals(1)) { - if(u.isConstant(true) && v.isConstant(true)) { - var _a = evaluate(a.clone()); - var _b = evaluate(b.clone()); - result = _.multiply(_a, _b); + //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 === FACTORIAL && b.fname === FACTORIAL && !u.isConstant() && !v.isConstant() && b.power < 0) { + //assume that n = positive + var d = _.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()) { + b = new Symbol(1); + } + else { + //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); + 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 = _.add(u.clone(), new Symbol(i)); + t = _.multiply(t, s); + } + + result = _.multiply(_.pow(u, new Symbol(a.power)), _.pow(t, new Symbol(b.power))); + + b = new Symbol(1); } else { - result = _.divide(u, v); + //Otherwise the denominator is larger so expand that + for(var i=0, n = Math.abs(d); i<=n; i++) { + var s = _.add(v.clone(), new Symbol(i)); + t = _.multiply(t, s); + } + + result = _.multiply(_.pow(t, new Symbol(a.power)), _.pow(v, new Symbol(b.power))); + + b = new Symbol(1); } - b = new Symbol(1); } } } @@ -11939,4 +11965,4 @@ var nerdamer = (function (imports) { if ((typeof module) !== 'undefined') { module.exports = nerdamer; -}; +}; \ No newline at end of file diff --git a/spec/algebra.spec.js b/spec/algebra.spec.js index 52e51d62..7a4c2d0a 100644 --- a/spec/algebra.spec.js +++ b/spec/algebra.spec.js @@ -736,7 +736,7 @@ describe('Algebra', function () { }, { given: 'simplify(n!/(n+1)!)', - expected: '(1+n)^(-1)*n' + expected: '(1+n)^(-1)' }, //imaginary number {