├── .github └── workflows │ └── main.yml ├── .gitignore ├── Algebra.js ├── BREAKING_CHANGES.md ├── CITATION.cff ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Calculus.js ├── Extra.js ├── LICENSE ├── README.md ├── Solve.js ├── all.js ├── all.min.js ├── gulpfile.js ├── index.d.ts ├── index.html ├── license.txt ├── nerdamer.core.js ├── package-lock.json ├── package.json └── spec ├── LaTeX.spec.js ├── TeXConvert.spec.js ├── algebra.spec.js ├── basic_parser.spec.js ├── build.spec.js ├── calculus.spec.js ├── core.spec.js ├── extra.spec.js ├── solve.spec.js ├── support ├── jasmine.json └── utils.js ├── test.js ├── text.spec.js └── unsolved.spec.js /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: nerdamer prime CI 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | branches: ["main"] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build-and-test: 12 | runs-on: [ubuntu-latest] 13 | name: Build and test 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Use Node.js 20 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: 20 21 | cache: 'npm' 22 | cache-dependency-path: | 23 | ./package-lock.json 24 | 25 | - name: Install dependencies 26 | shell: bash 27 | run: npm ci 28 | 29 | # results in an error, so commenting out 30 | 31 | #- name: Audit fix 32 | # shell: bash 33 | # run: npm audit fix --audit-level=high 34 | 35 | - name: Run tests 36 | shell: bash 37 | run: npm run test 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject/private/ 2 | /nbproject/ 3 | /Algebra.temp.js 4 | /.idea/ 5 | /node_modules/ 6 | /package-lock.json 7 | /.vscode -------------------------------------------------------------------------------- /BREAKING_CHANGES.md: -------------------------------------------------------------------------------- 1 | *version 0.8.0* 2 | - Order of arguments changed for *defint* in `nerdamer.convertToLaTeX` 3 | - `nerdamer.toRPN` not returns array of tokens. Token class can be found in Parser.classes 4 | - The `Operator` class was removed. A simple `object` can be passed to method 5 | - nerdamer.setOperator and nerdamer.getOperators have been deprecated. Use `core.PARSER.setOperator` instead. 6 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | title: "Nerdamer" 3 | message: "If you use this software, please cite it as below."" 4 | version: 1.1.3 5 | date-released: 2021-11-01 6 | url: "https://nerdamer.com" 7 | license: "MIT License" 8 | authors: 9 | - family-names: Donk 10 | given-names: Martin 11 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at martin.r.donk@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | Thank you for your interest in contributing to the project. You can submit a pull request at any time with your changes. Please make sure to target the `dev` branch when submitting and not the `master` branch. 3 | 4 | Any contribution is welcomed granted the following are performed: 5 | 6 | 1. Make sure that all unit tests pass. The unit tests are written for **jasmine**. To run the tests, first make sure you install [node.js](https://nodejs.org/en/download/) and [jasmine](https://www.npmjs.com/package/jasmine). Follow the link to install nodejs. You can install jasmine by running `npm install -g jasmine`. Once that's complete you can navigate to your project folder root and type `jasmine`. Please be aware that at the moment, some tests will fail for versions of node.js > 10.xx due to rounding errors. 7 | 2. Make sure that there's isn't already an existing solution on the `dev` branch. 8 | 9 | Feel free to reach out if you need any guidance navigating the code. Seriously. A slow response doesn't mean you're being ignored but simply that life is in progress. You will however get a response at some point. 10 | 11 | # Issues 12 | Please do report any issues you find. If you don't report it, then there's no way of knowing the issue exists in the first place. There are existing issues labeled "Pending issues with xxx". These are just to keep related issues organized. So for instance if you're having a problem with `simplify` you can just add your issue under "Pending issues with simplify". Be sure to mention @jiggzson when reporting. 13 | 14 | Give as much information as possible about your issue and try to give it a meaningful subject line. If possible, add an example of what works and what doesn't. If you've already done some debugging, feel free to share since it may help speed up the fix. -------------------------------------------------------------------------------- /Extra.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Author : Martin Donk 3 | * Website : http://www.nerdamer.com 4 | * Email : martin.r.donk@gmail.com 5 | * License : MIT 6 | * Source : https://github.com/jiggzson/nerdamer 7 | */ 8 | 9 | /* global module */ 10 | 11 | if((typeof module) !== 'undefined') { 12 | var nerdamer = require('./nerdamer.core.js'); 13 | require('./Calculus'); 14 | require('./Algebra'); 15 | } 16 | 17 | (function () { 18 | "use strict"; 19 | 20 | var core = nerdamer.getCore(), 21 | _ = core.PARSER, 22 | Symbol = core.Symbol, 23 | format = core.Utils.format, 24 | isVector = core.Utils.isVector, 25 | isArray = core.Utils.isArray, 26 | Vector = core.Vector, 27 | S = core.groups.S, 28 | EX = core.groups.EX, 29 | CP = core.groups.CP, 30 | PL = core.groups.PL, 31 | CB = core.groups.CB, 32 | FN = core.groups.FN; 33 | core.Settings.Laplace_integration_depth = 40; 34 | 35 | 36 | Symbol.prototype.findFunction = function (fname) { 37 | //this is what we're looking for 38 | if(this.group === FN && this.fname === fname) 39 | return this.clone(); 40 | var found; 41 | if(this.symbols) 42 | for(var x in this.symbols) { 43 | found = this.symbols[x].findFunction(fname); 44 | if(found) 45 | break; 46 | } 47 | 48 | return found; 49 | }; 50 | 51 | var __ = core.Extra = { 52 | version: '1.4.2', 53 | //http://integral-table.com/downloads/LaplaceTable.pdf 54 | //Laplace assumes all coefficients to be positive 55 | LaPlace: { 56 | //Using: integral_0^oo f(t)*e^(-s*t) dt 57 | transform: function (symbol, t, s) { 58 | symbol = symbol.clone(); 59 | 60 | t = t.toString(); 61 | //First try a lookup for a speed boost 62 | symbol = Symbol.unwrapSQRT(symbol, true); 63 | var retval, 64 | coeff = symbol.stripVar(t), 65 | g = symbol.group; 66 | 67 | symbol = _.divide(symbol, coeff.clone()); 68 | 69 | if(symbol.isConstant() || !symbol.contains(t, true)) { 70 | retval = _.parse(format('({0})/({1})', symbol, s)); 71 | } 72 | else if(g === S && core.Utils.isInt(symbol.power)) { 73 | var n = String(symbol.power); 74 | retval = _.parse(format('factorial({0})/({1})^({0}+1)', n, s)); 75 | } 76 | else if(symbol.group === S && symbol.power.equals(1 / 2)) { 77 | retval = _.parse(format('sqrt(pi)/(2*({0})^(3/2))', s)); 78 | } 79 | else if(symbol.isComposite()) { 80 | retval = new Symbol(0); 81 | symbol.each(function (x) { 82 | retval = _.add(retval, __.LaPlace.transform(x, t, s)); 83 | }, true); 84 | } 85 | else if(symbol.isE() && (symbol.power.group === S || symbol.power.group === CB)) { 86 | var a = symbol.power.stripVar(t); 87 | retval = _.parse(format('1/(({1})-({0}))', a, s)); 88 | } 89 | else { 90 | var fns = ['sin', 'cos', 'sinh', 'cosh']; 91 | //support for symbols in fns with arguments in the form a*t or n*t where a = symbolic and n = Number 92 | if(symbol.group === FN && fns.indexOf(symbol.fname) !== -1 && (symbol.args[0].group === S || symbol.args[0].group === CB)) { 93 | var a = symbol.args[0].stripVar(t); 94 | 95 | switch(symbol.fname) { 96 | case 'sin': 97 | retval = _.parse(format('({0})/(({1})^2+({0})^2)', a, s)); 98 | break; 99 | case 'cos': 100 | retval = _.parse(format('({1})/(({1})^2+({0})^2)', a, s)); 101 | break; 102 | case 'sinh': 103 | retval = _.parse(format('({0})/(({1})^2-({0})^2)', a, s)); 104 | break; 105 | case 'cosh': 106 | retval = _.parse(format('({1})/(({1})^2-({0})^2)', a, s)); 107 | break; 108 | } 109 | 110 | } 111 | else { 112 | //Try to integrate for a solution 113 | //we need at least the Laplace integration depth 114 | var depth_is_lower = core.Settings.integration_depth < core.Settings.Laplace_integration_depth; 115 | 116 | if(depth_is_lower) { 117 | var integration_depth = core.Settings.integration_depth; //save the depth 118 | core.Settings.integration_depth = core.Settings.Laplace_integration_depth; //transforms need a little more room 119 | } 120 | 121 | core.Utils.block('PARSE2NUMBER', function () { 122 | var u = t; 123 | var sym = symbol.sub(t, u); 124 | var integration_expr = _.parse('e^(-' + s + '*' + u + ')*' + sym); 125 | retval = core.Calculus.integrate(integration_expr, u); 126 | if(retval.hasIntegral()) 127 | return _.symfunction('laplace', arguments); 128 | // _.error('Unable to compute transform'); 129 | retval = retval.sub(t, 0); 130 | retval = _.expand(_.multiply(retval, new Symbol(-1))); 131 | retval = retval.sub(u, t); 132 | }, false); 133 | 134 | retval = core.Utils.block('PARSE2NUMBER', function () { 135 | return _.parse(retval); 136 | }, true); 137 | 138 | if(depth_is_lower)//put the integration depth as it was 139 | core.Settings.integration_depth = integration_depth; 140 | } 141 | 142 | } 143 | 144 | return _.multiply(retval, coeff); 145 | }, 146 | inverse: function (symbol, s_, t) { 147 | var input_symbol = symbol.clone(); 148 | return core.Utils.block('POSITIVE_MULTIPLIERS', function () { 149 | //expand and get partial fractions 150 | if(symbol.group === CB) { 151 | symbol = core.Algebra.PartFrac.partfrac(_.expand(symbol), s_); 152 | } 153 | 154 | if(symbol.group === S || symbol.group === CB || symbol.isComposite()) { 155 | var finalize = function () { 156 | //put back the numerator 157 | retval = _.multiply(retval, num); 158 | retval.multiplier = retval.multiplier.multiply(symbol.multiplier); 159 | //put back a 160 | retval = _.divide(retval, f.a); 161 | }; 162 | var num, den, s, retval, f, p, m, den_p, fe; 163 | //remove the multiplier 164 | m = symbol.multiplier.clone(); 165 | symbol.toUnitMultiplier(); 166 | //get the numerator and denominator 167 | num = symbol.getNum(); 168 | den = symbol.getDenom().toUnitMultiplier(); 169 | 170 | //TODO: Make it so factor doesn't destroy pi 171 | //num = core.Algebra.Factor.factor(symbol.getNum()); 172 | //den = core.Algebra.Factor.factor(symbol.getDenom().invert(null, true)); 173 | 174 | if(den.group === CP || den.group === PL) { 175 | den_p = den.power.clone(); 176 | den.toLinear(); 177 | } 178 | else { 179 | den_p = new core.Frac(1); 180 | } 181 | 182 | //convert s to a string 183 | s = s_.toString(); 184 | //split up the denominator if in the form ax+b 185 | f = core.Utils.decompose_fn(den, s, true); 186 | //move the multiplier to the numerator 187 | fe = core.Utils.decompose_fn(_.expand(num.clone()), s, true); 188 | num.multiplier = num.multiplier.multiply(m); 189 | //store the parts in variables for easy recognition 190 | //check if in the form t^n where n = integer 191 | if((den.group === S || den.group === CB) && f.x.value === s && f.b.equals(0) && core.Utils.isInt(f.x.power)) { 192 | var fact, p; 193 | p = f.x.power - 1; 194 | fact = core.Math2.factorial(p); 195 | // n!/s^(n-1) 196 | retval = _.divide(_.pow(t, new Symbol(p)), new Symbol(fact)); 197 | //wrap it up 198 | finalize(); 199 | } 200 | else if(den.group === CP && den_p.equals(1)) { 201 | if(f.x.group === core.groups.PL && core.Algebra.degree(den).equals(2)) { 202 | // Possibly in the form 1/(s^2+2*s+1) 203 | // Try factoring to get it in a more familiar form{ 204 | // Apply inverse of F(s-a) 205 | var completed = core.Algebra.sqComplete(den, s); 206 | var u = core.Utils.getU(den); 207 | // Get a for the function above 208 | var a = core.Utils.decompose_fn(completed.a, s, true).b; 209 | var tf = __.LaPlace.inverse(_.parse(`1/((${u})^2+(${completed.c}))`), u, t); 210 | retval = _.multiply(tf, _.parse(`(${m})*e^(-(${a})*(${t}))`)); 211 | } 212 | else { 213 | // a/(b*s-c) -> ae^(-bt) 214 | if(f.x.isLinear() && !num.contains(s)) { 215 | t = _.divide(t, f.a.clone()); 216 | 217 | // Don't add factorial of one or zero 218 | var p = den_p - 1; 219 | var fact = p === 0 || p === 1 ? '1' : `(${den_p}-1)!` 220 | retval = _.parse(format('(({0})^({3}-1)*e^(-(({2})*({0}))/({1})))/(({4})*({1})^({3}))', t, f.a, f.b, den_p, fact)); 221 | //wrap it up 222 | finalize(); 223 | } 224 | else { 225 | if(f.x.group === S && f.x.power.equals(2)) { 226 | if(!num.contains(s)) { 227 | retval = _.parse(format('(({1})*sin((sqrt(({2})*({3}))*({0}))/({2})))/sqrt(({2})*({3}))', t, num, f.a, f.b)); 228 | } 229 | // a*s/(b*s^2+c^2) 230 | else { 231 | var a = new Symbol(1); 232 | if(num.group === CB) { 233 | var new_num = new Symbol(1); 234 | num.each(function (x) { 235 | if(x.contains(s)) 236 | new_num = _.multiply(new_num, x); 237 | else 238 | a = _.multiply(a, x); 239 | }); 240 | num = new_num; 241 | } 242 | 243 | //we need more information about the denominator to decide 244 | var f2 = core.Utils.decompose_fn(num, s, true); 245 | var fn1, fn2, a_has_sin, b_has_cos, a_has_cos, b_has_sin; 246 | fn1 = f2.a; 247 | fn2 = f2.b; 248 | a_has_sin = fn1.containsFunction('sin'); 249 | a_has_cos = fn1.containsFunction('cos'); 250 | b_has_cos = fn2.containsFunction('cos'); 251 | b_has_sin = fn2.containsFunction('sin'); 252 | if(f2.x.value === s && f2.x.isLinear() && !((a_has_sin && b_has_cos) || (a_has_cos || b_has_sin))) { 253 | retval = _.parse(format('(({1})*cos((sqrt(({2})*({3}))*({0}))/({2})))/({2})', t, f2.a, f.a, f.b)); 254 | } 255 | else { 256 | if(a_has_sin && b_has_cos) { 257 | var sin, cos; 258 | sin = fn1.findFunction('sin'); 259 | cos = fn2.findFunction('cos'); 260 | //who has the s? 261 | if(sin.args[0].equals(cos.args[0]) && !sin.args[0].contains(s)) { 262 | var b, c, d, e; 263 | b = _.divide(fn2, cos.toUnitMultiplier()).toString(); 264 | c = sin.args[0].toString(); 265 | d = f.b; 266 | e = _.divide(fn1, sin.toUnitMultiplier()); 267 | exp = '(({1})*({2})*cos({3})*sin(sqrt({4})*({0})))/sqrt({4})+({1})*sin({3})*({5})*cos(sqrt({4})*({0}))'; 268 | retval = _.parse(format(exp, t, a, b, c, d, e)); 269 | } 270 | } 271 | } 272 | } 273 | } 274 | } 275 | } 276 | } 277 | else if(f.x.power.num && f.x.power.num.equals(3) && f.x.power.den.equals(2) && num.contains('sqrt(pi)') && !num.contains(s) && num.isLinear()) { 278 | var b = _.divide(num.clone(), _.parse('sqrt(pi)')); 279 | retval = _.parse(format('(2*({2})*sqrt({0}))/({1})', t, f.a, b, num)); 280 | } 281 | else if(den_p.equals(2) && f.x.power.equals(2)) { 282 | var a, d, exp; 283 | if(!num.contains(s)) { 284 | a = _.divide(num, new Symbol(2)); 285 | exp = '(({1})*sin((sqrt(({2})*({3}))*({0}))/({2})))/(({3})*sqrt(({2})*({3})))-(({1})*({0})*cos((sqrt(({2})*({3}))*({0}))/({2})))/(({2})*({3}))'; 286 | retval = _.parse(format(exp, t, a, f.a, f.b)); 287 | } 288 | else { 289 | // Decompose the numerator to check value of s 290 | f2 = core.Utils.decompose_fn(_.expand(num.clone()), s, true); 291 | if(f2.x.isComposite()) { 292 | var s_terms = []; 293 | //first collect the factors e.g. (a)(bx)(cx^2+d) 294 | var symbols = num.collectSymbols(function (x) { 295 | x = Symbol.unwrapPARENS(x); 296 | var t = core.Utils.decompose_fn(x, s, true); 297 | t.symbol = x; 298 | return t; 299 | }). 300 | //then sort them by power hightest to lowest 301 | sort(function (a, b) { 302 | var p1, p2; 303 | p1 = a.x.value !== s ? 0 : a.x.power; 304 | p2 = b.x.value !== s ? 0 : b.x.power; 305 | return p2 - p1; 306 | }); 307 | a = new Symbol(-1); 308 | // Grab only the ones which have s 309 | for(var i = 0; i < symbols.length; i++) { 310 | var fc = symbols[i]; 311 | if(fc.x.value === s) 312 | s_terms.push(fc); 313 | else 314 | a = _.multiply(a, fc.symbol); 315 | } 316 | // The following 2 assumptions are made 317 | // 1. since the numerator was factored above then each s_term has a unique power 318 | // 2. because the terms are sorted by descending powers then the first item 319 | // has the highest power 320 | // We can now check for the next type s(s^2-a^2)/(s^2+a^2)^2 321 | if(s_terms[0].x.power.equals(2) && s_terms[1].x.power.equals(1) && s_terms[1].b.equals(0) && !s_terms[0].b.equals(0)) { 322 | b = s_terms[0].a.negate(); 323 | exp = '-(({1})*({2})*({5})*({0})*sin((sqrt(({4})*({5}))*({0}))/({4})))/' + 324 | '(2*({4})^2*sqrt(({4})*({5})))-(({1})*({3})*({0})*sin((sqrt(({4})*({5}))*({0}))/({4})))' + 325 | '/(2*({4})*sqrt(({4})*({5})))+(({1})*({2})*cos((sqrt(({4})*({5}))*({0}))/({4})))/({4})^2'; 326 | retval = _.parse(format(exp, t, a, b, s_terms[0].b, f.a, f.b)); 327 | } 328 | } 329 | else { 330 | if(f2.x.isLinear()) { 331 | a = _.divide(f2.a, new Symbol(2)); 332 | exp = '(({1})*({0})*sin((sqrt(({2})*({3}))*({0}))/({2})))/(({2})*sqrt(({2})*({3})))'; 333 | retval = _.parse(format(exp, t, a, f.a, f.b)); 334 | } 335 | else if(f2.x.power.equals(2)) { 336 | if(f2.b.equals(0)) { 337 | a = _.divide(f2.a, new Symbol(2)); 338 | exp = '(({1})*sin((sqrt(({2})*({3}))*({0}))/({2})))/(({2})*sqrt(({2})*({3})))+(({1})*({0})*cos((sqrt(({2})*({3}))*({0}))/({2})))/({2})^2'; 339 | retval = _.parse(format(exp, t, a, f.a, f.b)); 340 | } 341 | else { 342 | a = _.divide(f2.a, new Symbol(2)); 343 | d = f2.b.negate(); 344 | exp = '-((({2})*({4})-2*({1})*({3}))*sin((sqrt(({2})*({3}))*({0}))/({2})))/(2*({2})*({3})*sqrt(({2})*({3})))+' + 345 | '(({4})*({0})*cos((sqrt(({2})*({3}))*({0}))/({2})))/(2*({2})*({3}))+(({1})*({0})*cos((sqrt(({2})*({3}))*({0}))/({2})))/({2})^2'; 346 | retval = _.parse(format(exp, t, a, f.a, f.b, d)); 347 | 348 | } 349 | } 350 | } 351 | } 352 | } 353 | else if(symbol.isComposite()) { 354 | // 1/(s+1)^2 355 | if(den_p.equals(2) && f.x.group === S) { 356 | retval = _.parse(`(${m})*(${t})*e^(-(${f.b})*(${t}))`); 357 | } 358 | else { 359 | retval = new Symbol(0); 360 | 361 | symbol = core.Algebra.PartFrac.partfrac(_.expand(symbol), s_); 362 | 363 | symbol.each(function (x) { 364 | retval = _.add(retval, __.LaPlace.inverse(x, s_, t)); 365 | }, true); 366 | } 367 | } 368 | } 369 | 370 | if(!retval) { 371 | retval = _.symfunction('ilt', [input_symbol, s_, t]); 372 | } 373 | 374 | return retval; 375 | }, true); 376 | } 377 | }, 378 | Statistics: { 379 | frequencyMap: function (arr) { 380 | var map = {}; 381 | //get the frequency map 382 | for(var i = 0, l = arr.length; i < l; i++) { 383 | var e = arr[i], 384 | key = e.toString(); 385 | if(!map[key]) //default it to zero 386 | map[key] = 0; 387 | map[key]++; //increment 388 | } 389 | return map; 390 | }, 391 | sort: function (arr) { 392 | return arr.sort(function (a, b) { 393 | if(!a.isConstant() || !b.isConstant()) 394 | _.error('Unable to sort! All values must be numeric'); 395 | return a.multiplier.subtract(b.multiplier); 396 | }); 397 | }, 398 | count: function (arr) { 399 | return new Symbol(arr.length); 400 | }, 401 | sum: function (arr, x_) { 402 | var sum = new Symbol(0); 403 | for(var i = 0, l = arr.length; i < l; i++) { 404 | var xi = arr[i].clone(); 405 | if(x_) { 406 | sum = _.add(_.pow(_.subtract(xi, x_.clone()), new Symbol(2)), sum); 407 | } 408 | else 409 | sum = _.add(xi, sum); 410 | } 411 | 412 | return sum; 413 | }, 414 | mean: function () { 415 | var args = [].slice.call(arguments); 416 | //handle arrays 417 | if(isVector(args[0])) 418 | return __.Statistics.mean.apply(this, args[0].elements); 419 | return _.divide(__.Statistics.sum(args), __.Statistics.count(args)); 420 | }, 421 | median: function () { 422 | var args = [].slice.call(arguments), retval; 423 | //handle arrays 424 | if(isVector(args[0])) 425 | return __.Statistics.median.apply(this, args[0].elements); 426 | try { 427 | var sorted = __.Statistics.sort(args); 428 | var l = args.length; 429 | if(core.Utils.even(l)) { 430 | var mid = l / 2; 431 | retval = __.Statistics.mean(sorted[mid - 1], sorted[mid]); 432 | } 433 | else 434 | retval = sorted[Math.floor(l / 2)]; 435 | } 436 | catch(e) { 437 | if (e.message === "timeout") throw e; 438 | retval = _.symfunction('median', args); 439 | } 440 | return retval; 441 | }, 442 | mode: function () { 443 | var args = [].slice.call(arguments), 444 | retval; 445 | //handle arrays 446 | if(isVector(args[0])) 447 | return __.Statistics.mode.apply(this, args[0].elements); 448 | 449 | var map = __.Statistics.frequencyMap(args); 450 | 451 | //the mode of 1 item is that item as per issue #310 (verified by Happypig375). 452 | if(core.Utils.keys(map).length === 1) 453 | retval = args[0]; 454 | else { 455 | //invert by arraning them according to their frequency 456 | var inverse = {}; 457 | for(var x in map) { 458 | var freq = map[x]; 459 | //check if it's in the inverse already 460 | if(!(freq in inverse)) 461 | inverse[freq] = x; 462 | else { 463 | var e = inverse[freq]; 464 | //if it's already an array then just add it 465 | if(isArray(e)) 466 | e.push(x); 467 | //convert it to and array 468 | else 469 | inverse[freq] = [x, inverse[freq]]; 470 | } 471 | } 472 | //the keys now represent the maxes. We want the max of those keys 473 | var max = inverse[Math.max.apply(null, core.Utils.keys(inverse))]; 474 | //check it's an array. If it is then map over the results and convert 475 | //them to Symbol 476 | if(isArray(max)) { 477 | retval = _.symfunction('mode', max.sort()); 478 | } 479 | else 480 | retval = _.parse(max); 481 | } 482 | 483 | return retval; 484 | }, 485 | gVariance: function (k, args) { 486 | var x_ = __.Statistics.mean.apply(__.Statistics, args), 487 | sum = __.Statistics.sum(args, x_); 488 | return _.multiply(k, sum); 489 | }, 490 | variance: function () { 491 | var args = [].slice.call(arguments); 492 | //handle arrays 493 | if(isVector(args[0])) 494 | return __.Statistics.variance.apply(this, args[0].elements); 495 | var k = _.divide(new Symbol(1), __.Statistics.count(args)); 496 | return __.Statistics.gVariance(k, args); 497 | }, 498 | sampleVariance: function () { 499 | var args = [].slice.call(arguments); 500 | //handle arrays 501 | if(isVector(args[0])) 502 | return __.Statistics.sampleVariance.apply(this, args[0].elements); 503 | 504 | var k = _.divide(new Symbol(1), _.subtract(__.Statistics.count(args), new Symbol(1))); 505 | return __.Statistics.gVariance(k, args); 506 | }, 507 | standardDeviation: function () { 508 | var args = [].slice.call(arguments); 509 | //handle arrays 510 | if(isVector(args[0])) 511 | return __.Statistics.standardDeviation.apply(this, args[0].elements); 512 | return _.pow(__.Statistics.variance.apply(__.Statistics, args), new Symbol(1 / 2)); 513 | }, 514 | sampleStandardDeviation: function () { 515 | var args = [].slice.call(arguments); 516 | //handle arrays 517 | if(isVector(args[0])) 518 | return __.Statistics.sampleStandardDeviation.apply(this, args[0].elements); 519 | return _.pow(__.Statistics.sampleVariance.apply(__.Statistics, args), new Symbol(1 / 2)); 520 | }, 521 | zScore: function (x, mean, stdev) { 522 | return _.divide(_.subtract(x, mean), stdev); 523 | } 524 | }, 525 | Units: { 526 | table: { 527 | foot: '12 inch', 528 | meter: '100 cm', 529 | decimeter: '10 cm', 530 | 531 | } 532 | } 533 | }; 534 | 535 | nerdamer.register([ 536 | { 537 | name: 'laplace', 538 | visible: true, 539 | numargs: 3, 540 | build: function () { 541 | return __.LaPlace.transform; 542 | } 543 | }, 544 | { 545 | name: 'ilt', 546 | visible: true, 547 | numargs: 3, 548 | build: function () { 549 | return __.LaPlace.inverse; 550 | } 551 | }, 552 | //statistical 553 | { 554 | name: 'mean', 555 | visible: true, 556 | numargs: -1, 557 | build: function () { 558 | return __.Statistics.mean; 559 | } 560 | }, 561 | { 562 | name: 'median', 563 | visible: true, 564 | numargs: -1, 565 | build: function () { 566 | return __.Statistics.median; 567 | } 568 | }, 569 | { 570 | name: 'mode', 571 | visible: true, 572 | numargs: -1, 573 | build: function () { 574 | return __.Statistics.mode; 575 | } 576 | }, 577 | { 578 | name: 'smpvar', 579 | visible: true, 580 | numargs: -1, 581 | build: function () { 582 | return __.Statistics.sampleVariance; 583 | } 584 | }, 585 | { 586 | name: 'variance', 587 | visible: true, 588 | numargs: -1, 589 | build: function () { 590 | return __.Statistics.variance; 591 | } 592 | }, 593 | { 594 | name: 'smpstdev', 595 | visible: true, 596 | numargs: -1, 597 | build: function () { 598 | return __.Statistics.sampleStandardDeviation; 599 | } 600 | }, 601 | { 602 | name: 'stdev', 603 | visible: true, 604 | numargs: -1, 605 | build: function () { 606 | return __.Statistics.standardDeviation; 607 | } 608 | }, 609 | { 610 | name: 'zscore', 611 | visible: true, 612 | numargs: 3, 613 | build: function () { 614 | return __.Statistics.zScore; 615 | } 616 | } 617 | ]); 618 | 619 | //link registered functions externally 620 | nerdamer.updateAPI(); 621 | }()); 622 | 623 | // Added for all.min.js 624 | if((typeof module) !== 'undefined') { 625 | module.exports = nerdamer; 626 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 together-science 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Nerdamer-prime 2 | ============== 3 | 4 | This is a continuation of Martin Donk's (jiggzson) [Nerdamer project](https://nerdamer.com/). We forked this a while ago, 5 | but are up to speed with everything Martin did before he archived the original repo. 6 | 7 | The license is unchanged, everything is free under MIT terms. 8 | 9 | The npm js installation point has moved: `npm i nerdamer-prime`. 10 | 11 | NOTICE: Starting with version 1.2.0, vector and matrix semantics are changing to be more useful for linear algebra. If this causes you problems, please stick with 1.1.26 and let us know so we can see how we could make compatible changes. 12 | 13 | Our intentions: 14 | 15 | To keep Nerdamer in good repair, and make improvements where we need them and where we can. Mostly bug fixes. If you investigate 16 | some of the bugs in the old repo, you will see that people ask for "things it should be able to do", mostly to do with simplifications. 17 | This kind of stuff is difficult, and it is more difficult in someone else's codebase. Nerdamer wasn't meant to be a complete symbolic 18 | algebra system. Its wealth of features can fool you, though. Its features and the fact that it works fast, in the browser and NodeJS, 19 | makes it still worthwhile. But please understand that it will not achieve a whole lot more than it can do right now. Consider using 20 | e.g. SymPy in a WASM webworker if you need more. See here for a demo: [SymPy live](https://live.sympy.org/) 21 | 22 | We have made some improvements - simplification of logs and squareroots, and bug fixes related to those areas and factoring. Unit tests 23 | are fixed except for a couple of known flaws. There will be further work in this area. We will also work on vectors, which are not 24 | useful for our product [together.math](https://www.together.science/) today. Those will be breaking changes 25 | (*THESE ARE NOW IN PROGRESS* for versions >= 1.2.0, you are welcome to fork an earlier version). 26 | 27 | If you have a clear bug, file an issue with the code to repro. If you want a new feature - and that includes many things that you will 28 | think of as "obvious flaws" - file the issue, but we probably won't do it. But someone else can! We will absolutely consider compatible 29 | PRs, but best to [talk to us](mailto:info@together.science) before you start. 30 | 31 | Below follows the original README. 32 | 33 | 34 | Nerdamer 35 | ======== 36 | 37 | As of version 0.5.0, the library is split into the core and optional add-ons which can be loaded after the core has been loaded. 38 | 39 | Getting started with Nerdamer 40 | 41 | Load the library in your html page 42 | 43 | ```html 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | ``` 54 | Or import everything 55 | ```html 56 | 57 | ``` 58 | If you're using node.js install it using `npm i nerdamer-prime` (GM: originally `npm i nerdamer`)and then 59 | 60 | ```javascript 61 | // const cannot be used since nerdamer gets modified when other modules are loaded 62 | var nerdamer = require('nerdamer'); 63 | // Load additional modules. These are not required. 64 | require('nerdamer/Algebra'); 65 | require('nerdamer/Calculus'); 66 | require('nerdamer/Solve'); 67 | require('nerdamer/Extra'); 68 | ``` 69 | Or do a single import to import everything 70 | ```javascript 71 | const nerdamer = require("nerdamer/all.min") 72 | ``` 73 | 74 | Some functions have dependencies from other add-ons. 75 | 76 | You can see nerdamer in action at http://nerdamer.com/demo 77 | 78 | For full documentation go to http://nerdamer.com/documentation 79 | 80 | All operations are done using the 'nerdamer' object. 81 | 82 | To add an expression just add it to the nerdamer object which will return a `Expression` object. 83 | 84 | ```javascript 85 | var e = nerdamer('x^2+2*(cos(x)+x*x)'); 86 | console.log(e.text()); 87 | 88 | //result: 89 | //2*cos(x)+3*x^2 90 | ``` 91 | It is also possible to use `nerdamer` functions directly within the need for string manipulation of the input. The input will be parsed and the output will of type `Expression`. For example: 92 | ```javascript 93 | var ans = nerdamer.expand('(x-1)^5'); 94 | console.log(ans.text()); 95 | // -1-10*x^2-5*x^4+10*x^3+5*x+x^5 96 | 97 | var sol = nerdamer.solve('x^2-4', 'x'); 98 | console.log(sol.text()) 99 | // [2,-2] 100 | ``` 101 | 102 | You can also pass in an object with known values as the second parameter. 103 | 104 | ```javascript 105 | var e = nerdamer('x^2+2*(cos(x)+x*x)',{x:6}); 106 | console.log(e.text()); 107 | 108 | //result: 109 | //108+2*cos(6) 110 | ``` 111 | 112 | 113 | As you can see only the substitution is performed. To evaluate the result just call evaluate. 114 | Note that evaluate returns a text string or a number not an object. 115 | 116 | ```javascript 117 | var e = nerdamer('x^2+2*(cos(x)+x*x)',{x:6}).evaluate(); 118 | console.log(e.text()); 119 | 120 | //result: 121 | //109.9203405733006 122 | ``` 123 | To get back the text as a fraction, call the text method and pass in the string 'fractions'. 124 | 125 | ```javascript 126 | var e = nerdamer('x^2+2*(cos(x)+x*x)',{x:6}).evaluate(); 127 | console.log(e.text('fractions')); 128 | 129 | //result: 130 | //429607273/3908351 131 | ``` 132 | You can get your expression back as LaTeX by calling the toTeX method 133 | ```javascript 134 | var LaTeX = nerdamer('x^2+2*(cos(x)+x*x)',{x:0.25}).toTeX(); 135 | console.log(LaTeX); 136 | 137 | //result: 138 | //2 \cdot \mathrm{cos}\left(\frac{1}{4}\right)+\frac{3}{16} 139 | ``` 140 | 141 | To have numbers returned as decimals pass in the string 'decimals' to the toTeX method 142 | 143 | ```javascript 144 | var LaTeX = nerdamer('x^2+2*(cos(x)+x*x)',{x:0.25}).toTeX('decimal'); 145 | console.log(LaTeX); 146 | 147 | //result: 148 | //2 \cdot \mathrm{cos}\left(0.25\right)+0.1875 149 | ``` 150 | 151 | Alternatively you can pass an object containing known values into evaluate method instead. The values passed in don't have to be number they can be another expression if needed. 152 | 153 | ```javascript 154 | var e = nerdamer('x^2+2*(cos(x)+x*x)',{x:'x^2+1'}); 155 | console.log(e.text()); 156 | 157 | //result: 158 | //2*cos(1+x^2)+3*(1+x^2)^2 159 | ``` 160 | 161 | Every time you parse an expression it's stored in nerdamer. To get a list of all the expressions you just call 162 | nerdamer.expressions(). 163 | 164 | ```javascript 165 | var knownValues = {x:'x^2+1'}; 166 | nerdamer('x^2+2*(cos(x)+x*x)').evaluate(knownValues); 167 | nerdamer('sin(x)^2+cos(x)^2').evaluate(knownValues); 168 | 169 | console.log(nerdamer.expressions()); 170 | 171 | //result: 172 | //[ 46.692712758272776, 1 ] 173 | ``` 174 | 175 | You can request it as an object as well by passing in true. This can be convenient in some 176 | situations as the numbering starts at 1; 177 | 178 | ```javascript 179 | var knownValues = {x:'x^2+1'}; 180 | nerdamer('x^2+2*(cos(x)+x*x)', knownValues ); 181 | nerdamer('sin(x)^2+cos(x)^2', knownValues ); 182 | 183 | console.log(nerdamer.expressions(true)); 184 | 185 | //{ '1': '2*cos(1+x^(2))+3*(1+x^(2))^(2)', 186 | //'2': 'cos(1+x^(2))^(2)+sin(1+x^(2))^(2)' } 187 | ``` 188 | 189 | Functions aren't always immediately parsed to numbers. For example 190 | 191 | ```javascript 192 | var result = nerdamer('cos(x)',{x:6}); 193 | console.log(result.text()); 194 | //cos(6) 195 | ``` 196 | will only subsitute out the variable name. To change this behaviour numer should be passed in as the 3rd argument. 197 | 198 | ```javascript 199 | var result = nerdamer('cos(x)',{x:6}, 'numer'); 200 | console.log(result.text()); 201 | //0.960170286650366 202 | ``` 203 | or alternatively 204 | 205 | ```javascript 206 | var result = nerdamer('cos(x)').evaluate({x:6}); 207 | console.log(result.text()); 208 | //0.960170286650366 209 | ``` 210 | The difference however is that the first option directly substitutes the variables while the second first evaluates 211 | the expression and then makes the substitutions. This library utilizes native javascript functions as much as possible. As a result it inherits whatever rounding errors they possess. One major change with version 0.6.0 however, is dealing with floating point issues. 212 | 213 | ```javascript 214 | var result = nerdamer('sqrt(x)*sqrt(x)-2', {x: 2}); 215 | console.log(result.text()); 216 | //0 217 | ``` 218 | The above expample now returns zero whereas in previous version the result would be 4.440892098500626e-16. Same goes for 0.1+0.2. 219 | 220 | An expression can be replaced directly by passing in the index of which expression to override. For example 221 | 222 | ```javascript 223 | nerdamer('cos(x)',{x:6}, 'numer'); 224 | nerdamer('sin(x)+y',{x:6}, null, 1); 225 | console.log(nerdamer.expressions()); 226 | //[ 'sin(6)+y' ] 227 | ``` 228 | 229 | If multiple modifier options need to be passed into nerdamer you can do so using an array. For example ... 230 | 231 | ```javascript 232 | var e = nerdamer('cos(x)+(y-x)^2', {x:7}, ['expand', 'numer']); 233 | console.log(e.text()); 234 | //-14*y+y^2+49.7539022543433 235 | ``` 236 | 237 | If you need the code as LaTeX you can pass in true as the second parameter when requesting the expressions. 238 | 239 | ```javascript 240 | nerdamer('x^2+2*(cos(x)+x*x)'); 241 | nerdamer('sin(x)^0.25+cos(x)^0.5' ); 242 | var asObject = true; 243 | var asLaTeX = true; 244 | console.log(nerdamer.expressions(asObject, asLaTeX)); 245 | 246 | /*{ '1': '2 \\cdot \\mathrm{cos}\\left(x\\right)+3 \\cdot x^{2}', 247 | '2': '\\sqrt{\\mathrm{cos}\\left(x\\right)}+\\mathrm{sin}\\left(x\\right)^{\\frac{1}{4}}' }*/ 248 | ``` 249 | 250 | 251 | You can specify a particular location when adding an expression, which is specified with the third parameter. 252 | 253 | ```javascript 254 | nerdamer('x^2+2*(cos(x)+x*x)'); 255 | nerdamer('sin(x)^0.25+cos(x)^0.5' ); 256 | nerdamer('expr-override', undefined, 2 ); 257 | var asObject = false; 258 | var asLaTeX = true; 259 | console.log(nerdamer.expressions(asObject, asLaTeX)); 260 | 261 | /* [ '2 \\cdot \\mathrm{cos}\\left(x\\right)+3 \\cdot x^{2}', 262 | '\\sqrt{\\mathrm{cos}\\left(x\\right)}+\\mathrm{sin}\\left(x\\right)^{\\frac{1}{4}}', 263 | 'expr-override' ] 264 | */ 265 | ``` 266 | 267 | Here's an example of reserved variable and function names. 268 | 269 | ```javascript 270 | var reserved = nerdamer.reserved(); 271 | console.log(reserved); 272 | //result: 273 | /* csc, sec, cot, erf, fact, mod, GCD, QGCD, LCM, pow, PI, E, cos, sin, tan, acos, asin, atan, sinh, cosh, tanh, asinh, acosh, atanh, exp, min, max, floor, ceil, round, vector, matrix, parens, sqrt, log, expand, abs, invert, transpose, dot */ 274 | 275 | //or as an array 276 | 277 | var reserved = nerdamer.reserved(true); 278 | console.log(reserved); 279 | //result: 280 | /* [ 'csc', 'sec', 'cot', 'erf', 'fact', 'mod', 'GCD', 'QGCD', 'LCM', 'pow', 'PI', 'E', 'cos', 'sin', 'tan', 'acos', 'asin', 'atan', 'sinh', 'cosh', 'tanh', 'asinh', 'acosh', 'atanh', 'exp', 'min', 'max', 'floor', 'ceil', 'round', 'vector', 'matrix', 281 | 'parens', 'sqrt', 'log', 'expand', 'abs', 'invert', 'transpose', 'dot' ] */ 282 | ``` 283 | 284 | Most math functions are passed in as part of the expression. If you want to differentiate for instance you just use the function diff which is located in the Calculus add-on as of version 0.5.0 285 | 286 | ```javascript 287 | var e = nerdamer('diff(x^2+2*(cos(x)+x*x),x)'); 288 | 289 | console.log(e.text()); 290 | 291 | //result: 292 | //-2*sin(x)+6*x 293 | ``` 294 | 295 | 296 | Nerdamer can also handle runtime functions. To do this use the method setFunction. 297 | The runtime functions do have symbolic capabilities and support for imaginary numbers. 298 | The setFunction method is used as follows: 299 | Mode 1a: 300 | nerdamer.setFunction( functionDefinition ) 301 | 302 | ```javascript 303 | nerdamer.setFunction('interpolate(y0,x0,y1,x1,x)=y0+(y1-y0)*((x-x0)/(x1-x0))') 304 | var answer = nerdamer('interpolate(4,1,34,7,2)').evaluate(); 305 | 306 | console.log(answer); 307 | 308 | //result: 9 309 | ``` 310 | 311 | Mode 1b: 312 | nerdamer.setFunction( functionName, functionParameters , functionBody ) 313 | 314 | ```javascript 315 | //generate some points 316 | var f = function(x) { return 5*x-1; } 317 | console.log(f(1)); //4 318 | console.log(f(2)); //9 - value to be found 319 | console.log(f(7)); //34 320 | 321 | nerdamer.setFunction('interpolate', ['y0','x0','y1','x1','x'], 'y0+(y1-y0)*((x-x0)/(x1-x0))') 322 | 323 | var answer = nerdamer('interpolate(4,1,34,7,2)').evaluate(); 324 | 325 | console.log(answer); 326 | 327 | //result: 9 328 | ``` 329 | 330 | Mode 2: 331 | Custom functions alternatively be set in following manner. 332 | 333 | ```javascript 334 | nerdamer('hyp(a, b) = sqrt(a^2 + b^2) '); 335 | 336 | var result = nerdamer('hyp(3, 4)').evaluate().text(); 337 | console.log(result); 338 | //result: 5 339 | ``` 340 | 341 | Mode 3: 342 | Custom JavaScript functions can also be set. For example 343 | 344 | ```javascript 345 | function hyp(a, b) { 346 | return Math.sqrt(a*a + b*b); 347 | } 348 | 349 | nerdamer.setFunction(hyp); 350 | 351 | var result = nerdamer('hyp(3, 4)').evaluate().text(); 352 | console.log(result); 353 | //result: 5 354 | ``` 355 | Also 356 | ```javascript 357 | function hyp(a, b) { 358 | return Math.sqrt(a*a + b*b); 359 | } 360 | 361 | nerdamer(hyp); 362 | 363 | var result = nerdamer('hyp(3, 4)').evaluate().text(); 364 | console.log(result); 365 | //result: 5 366 | ``` 367 | 368 | If you need to add a constant use the setConstant method 369 | 370 | ```javascript 371 | nerdamer.setConstant( 'g', 9.81); 372 | var weight = nerdamer('100*g').text(); 373 | console.log(weight); 374 | //result: 375 | //981 376 | ``` 377 | 378 | To delete just set it to delete 379 | 380 | ```javascript 381 | nerdamer.setConstant( 'g', 9.81); 382 | var weight = nerdamer('100*g').text(); 383 | console.log(weight); 384 | //981 385 | nerdamer.setConstant( 'g', 'delete'); 386 | var weight = nerdamer('100*g').text(); 387 | console.log(weight); 388 | //100*g 389 | ``` 390 | 391 | You also have the option of exporting your function to a javascript function which can be useful if you need some 392 | filtering from user input. Do keep in mind that the parameters are sorted alphabetically for more than one 393 | parameter. To use it add the expression to nerdamer and use the buildFunction method. 394 | 395 | ```javascript 396 | var f = nerdamer('x^2+5').buildFunction(); 397 | console.log(f(9)); 398 | 399 | //result: 400 | //86 401 | ``` 402 | If you have a particular order in which you need the parameters to be set, then you pass in an array with the variables in the order in which you want them for instance: 403 | 404 | ```javascript 405 | var f = nerdamer('z+x^2+y').buildFunction(['y', 'x', 'z']); 406 | console.log(f(9,2,1)); 407 | //result 408 | //14 409 | ``` 410 | 411 | Every time you add an expression to nerdamer it's stored. To list the expressions currently in nerdamer call 412 | the 'expressions' method. To delete an expression use the 'clear' method and pass in the expression you want to delete. 413 | To clear everything pass in the string 'all'. 414 | 415 | ```javascript 416 | nerdamer('n*R*T/v'); 417 | nerdamer('mc^2'); 418 | nerdamer('G*m1*m2/d^2'); 419 | 420 | nerdamer.clear(2); 421 | 422 | console.log(nerdamer.expressions(true)); 423 | 424 | //result: 425 | //{ '1': 'R*T*n*v^(-1)', '2': 'G*d^(-2)*m1*m2' } 426 | 427 | nerdamer.clear('all'); 428 | console.log(nerdamer.expressions(true)); 429 | //result: 430 | //{} 431 | ``` 432 | 433 | If you need go get the variables of an expression use the variables method. This method can be called after 434 | nerdamer was provided an expression. For example 435 | 436 | ```javascript 437 | var variables = nerdamer('csc(x*cos(y))-no_boring_x').variables(); 438 | console.log(variables); 439 | //result: 440 | //[ 'no_boring_x', 'x', 'y' ] 441 | ``` 442 | 443 | The order in which the variables appear require a little bit of knowledge of how nerdamer organizes symbols. For the 444 | sake of simplicity we'll just assume that there is no particular order 445 | 446 | ---------------------------------------------------------------------------------------------------------------------- 447 | 448 | Using the solver 449 | =============== 450 | To solve equations first load Solve.js. Just remember that Solve also required Algebra.js and Calculus.js to be loaded. You can then solve equations using nerdamer. Important: State the variable for which you are trying to solve. 451 | ```javascript 452 | var sol = nerdamer.solveEquations('x^3+8=x^2+6','x'); 453 | console.log(sol.toString()); 454 | //1+i,-i+1,-1 455 | ``` 456 | 457 | Notice that we use toString rather than text as this returns a javascript array. 458 | 459 | You can also solve an expression 460 | ```javascript 461 | var e = nerdamer.solveEquations('x^2+4-y', 'y'); 462 | console.log(e[0].text()); 463 | //4+x^2 464 | ``` 465 | 466 | You can also solve multivariate equations 467 | ```javascript 468 | var sol = nerdamer.solveEquations('x^2+8+y=x+6','x'); 469 | console.log(sol.toString()); 470 | //0.5*((-4*y-7)^0.5+1),0.5*(-(-4*y-7)^0.5+1) 471 | ``` 472 | You can do up to 3rd order polynomials for multivariate polynomials 473 | 474 | Additionally you can try for equations containing functions. This is more of a hit or miss approach unlike single variable polynomials (which uses Mr. David Binner's Jenkins-Traub port - http://www.akiti.ca/PolyRootRe.html) but it's there if you want to give it a try. 475 | 476 | ```javascript 477 | var sol = nerdamer.solveEquations('cos(x)+cos(3*x)=1','x'); 478 | console.log(sol.toString()); 479 | //5.7981235959208695,0.4850617112587174 480 | ``` 481 | To solve a system of linear equations pass them in as an array. For example 482 | 483 | ```javascript 484 | var sol = nerdamer.solveEquations(['x+y=1', '2*x=6', '4*z+y=6']); 485 | console.log(sol); 486 | //[ [ 'x', 3 ], [ 'y', -2 ], [ 'z', 2 ] ] 487 | ``` 488 | In version 0.7.2 and up the solver can additionally be used in the following way 489 | ```javascript 490 | //first parse the equation 491 | var x = nerdamer('x^2+2=y-7*a'); 492 | //You can make substitutions to the equation 493 | x = x.evaluate({a: 'x^2-3'}); 494 | console.log(x.toString()); //2+x^2=-7*x^2+21+y 495 | var solutions = x.solveFor('x'); 496 | console.log(solutions.toString()); //(1/16)*sqrt(32*y+608),(-1/16)*sqrt(32*y+608) 497 | ``` 498 | -------------------------------------------------------------------------------- /all.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Author : Martin Donk 3 | * Website : http://www.nerdamer.com 4 | * Email : martin.r.donk@gmail.com 5 | * Source : https://github.com/jiggzson/nerdamer 6 | * Can be used to load all add-ons with one require 7 | */ 8 | 9 | var nerdamer = require('./nerdamer.core.js'); 10 | require('./Algebra.js'); 11 | require('./Calculus.js'); 12 | require('./Solve.js'); 13 | require('./Extra.js'); 14 | 15 | //export nerdamer 16 | module.exports = nerdamer; 17 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var concat = require('gulp-concat'); 3 | var uglify = require('gulp-uglify'); 4 | 5 | var devFiles = ['./nerdamer.core.js','./Algebra.js', './Calculus.js', './Solve.js', './Extra.js']; 6 | 7 | gulp.task('concat_all', function() { 8 | return gulp.src(devFiles) 9 | .pipe(concat('all.min.js')) 10 | .pipe(uglify()) 11 | .pipe(gulp.dest('./')); 12 | }); 13 | 14 | gulp.task('watch', function() { 15 | gulp.watch(devFiles, gulp.series('concat_all')); 16 | }); -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | export as namespace nerdamer 2 | export = nerdamer 3 | declare function nerdamer( 4 | expression: nerdamer.ExpressionParam, 5 | subs?: { [name: string]: string }, 6 | option?: keyof nerdamer.Options | (keyof nerdamer.Options)[], 7 | location?: nerdamer.int): nerdamer.Expression 8 | declare namespace nerdamer { 9 | export type ExpressionParam = Expression | string 10 | export interface Options { 11 | numer: never, expand: never 12 | } 13 | type int = number 14 | /** 15 | * Returns the current version of nerdamer. 16 | */ 17 | export function version(): string 18 | 19 | /** 20 | * Sets a constant value which nerdamer will automatically substitute when parsing expression/equation 21 | * @param name The variable to be set as the constant. 22 | * @param value The value for the expression to be set to. 23 | */ 24 | export function setConstant(name: string, value: number | string): typeof nerdamer 25 | 26 | /** 27 | * Sets a function which can then be called using nerdamer. 28 | * @param fnName The function name 29 | * @param fnParams The parameter array in the order in which the arguments are to be passed 30 | * @param fnBody The body of the function 31 | * @example 1 32 | * nerdamer.setFunction('f', ['x', 'y'], 'x^2+y') 33 | * var x = nerdamer('f(4, 7)').toString() 34 | * console.log(x.toString()) 35 | * nerdamer.setFunction('g', ['z', 'x', 'y'], '2*x+3*y+4*z') 36 | * x = nerdamer('g(3, 1, 2)') 37 | * console.log(x.toString()) 38 | * @example 2 39 | * nerdamer.setFunction('f(x, y) = x^2+y') //OR 'f(x, y) := x^2+y' 40 | * var x = nerdamer('f(4, 7)').toString() 41 | * console.log(x.toString()) 42 | * nerdamer.setFunction('g', ['z', 'x', 'y'], '2*x+3*y+4*z') 43 | * x = nerdamer('g(3, 1, 2)') 44 | * console.log(x.toString()) 45 | * @example 3 46 | * function custom(x , y) { 47 | * return x + y; 48 | * } 49 | * nerdamer.setFunction(custom); 50 | * var x = nerdamer('custom(4, 7)').toString(); 51 | * console.log(x.toString()) 52 | * console.log(x.valueOf()) 53 | * OR just nerdamer.setFunction(function custom(x , y) { return x + y; }); 54 | */ 55 | export function setFunction(fnName: string | Function, fnParams?: string[], fnBody?: string): typeof nerdamer 56 | 57 | /** 58 | * Returns the nerdamer core object. This object contains all the core functions of nerdamer and houses the parser. 59 | * @example 60 | * Object.keys(nerdamer.getCore()) 61 | */ 62 | export function getCore(): any 63 | 64 | /** 65 | * Gets the list of reserved names. This is a list of names already in use by nerdamer excluding variable names. This is not a static list. 66 | * @param asArray Pass in true to get the list back as an array instead of as an object. 67 | */ 68 | export function reserved(asArray: true): string[] 69 | export function reserved(asArray?: false): any 70 | 71 | /** 72 | * Clears all stored expressions. 73 | * @example 74 | * var x = nerdamer('x*x') 75 | console.log(nerdamer.expressions()) 76 | nerdamer.flush() //clear all expressions 77 | console.log(nerdamer.expressions()) 78 | */ 79 | export function flush(): typeof nerdamer 80 | 81 | /** 82 | * Converts and expression to LaTeX without evaluating expression. 83 | * @param expression The expression being converted. 84 | */ 85 | export function convertToLaTeX(expression: string): string 86 | 87 | /** 88 | * Attempts to import a LaTeX string. 89 | * @param TeX The expression being converted. 90 | */ 91 | export function convertFromLaTeX(TeX: string): Expression 92 | 93 | /** 94 | * Each time an expression is parsed nerdamer stores the result. Use this method to get back stored expressions. 95 | * @param asObject Pass in true to get expressions as numbered object with 1 as starting index 96 | * @param asLatex Pass in the string "LaTeX" to get the expression to LaTeX, otherwise expressions come back as strings 97 | */ 98 | export function expressions(asObject?: boolean, asLatex?: 'LaTeX'): string[] 99 | 100 | /** 101 | * Registers a module function with nerdamer. The object needs to contain at a minimum, a name property (text), a numargs property (int), this is -1 for variable arguments or an array containing the min and max arguments, the visible property (bool) which allows use of this function through nerdamer, defaults to true, and a build property containing a function which returns the function to be used. This function is also handy for creating aliases to functions. See below how the alias D was created for the diff function). 102 | * @param o 103 | */ 104 | export interface ModuleFunction { 105 | /** 106 | * Name of function. 107 | */ 108 | name: string, 109 | 110 | /** 111 | * Number of function arguments, -1 for variable arguments or an tuple containing minimum and maximum number of arguments. 112 | */ 113 | numargs: int | [int, int], 114 | 115 | /** 116 | * Allows use of this function through nerdamer. Defaults to true. 117 | */ 118 | visible?: boolean, 119 | 120 | /** 121 | * Return the function to be used. 122 | */ 123 | build(): (...args: number[]) => number 124 | } 125 | 126 | /** 127 | * Registers one or more module functions with nerdamer. 128 | * @param f 129 | * @example 130 | * var core = nerdamer.getCore() 131 | var _ = core.PARSER 132 | function f(a, b) { 133 | //use clone for safety since a or b might be returned 134 | var sum = _.add(a.clone(), b.clone()) 135 | var product = _.multiply(a.clone(), b.clone()) 136 | return _.multiply(sum, product) 137 | } 138 | //register the function with nerdamer 139 | nerdamer.register({ 140 | name: 'myFunction', 141 | numargs: 2, 142 | visible: true, 143 | build: function(){ return f } 144 | }) 145 | 146 | //create an alias for the diff function 147 | var core = nerdamer.getCore() 148 | nerdamer.register({ 149 | name: 'D', 150 | visible: true, 151 | numargs: [1, 3], 152 | build: function(){ return core.Calculus.diff } 153 | }) 154 | */ 155 | export function register(f: ModuleFunction | ModuleFunction[]): typeof nerdamer 156 | 157 | /** 158 | * This method can be used to check that the variable meets variable name requirements for nerdamer. Variable names Must start with a letter or underscore and may contains any combination of numbers, letters, and underscores after that. 159 | * @param variable_name The variable name being validated. 160 | * @example 161 | * nerdamer.validVarName('cos') // false 162 | * nerdamer.validVarName('chicken1') // true 163 | * nerdamer.validVarName('1chicken') // false 164 | * nerdamer.validVarName('_') // true 165 | */ 166 | export function validVarName(variable_name: string): boolean 167 | 168 | /** 169 | * Sets a known value in nerdamer. This differs from setConstant as the value can be overridden trough the scope. See example. 170 | * @param name The known value to be set. 171 | * @param value The value for the expression to be set to 172 | * @example 173 | * nerdamer.setVar('x', '11') 174 | * nerdamer('x*x') // == 121 175 | * // nerdamer will use 13 instead of 11: 176 | * nerdamer('x*x', {x: 13}) // == 169 177 | * // the value will be 121 again since the known value isn't being overridden: 178 | * nerdamer('x*x') // == 121 179 | * nerdamer.setVar('x', 'delete') 180 | * // since there no longer is a known value it will just be evaluated symbolically: 181 | * nerdamer('x*x') // == x^2 182 | */ 183 | export function setVar(name: string, value: number | string | 'delete'): void 184 | 185 | /** 186 | * Clears all previously set variables. 187 | */ 188 | export function clearVars(): typeof nerdamer 189 | 190 | /** 191 | * Gets all previously set variables. 192 | * @param option Use "LaTeX" to get as LaTeX. Defaults to text. 193 | */ 194 | export function getVars(option: 'LaTeX' | 'text'): { [name: string]: string } 195 | 196 | /** 197 | * Sets the value of a nerdamer setting. Currently PARSE2NUMBER and IMAGINARY. Setting PARSE2NUMBER to true will let nerdamer always try to return a number whenenver possible. IMAGINARY allows you to change the variable used for imaginary to j for instance. 198 | * @param setting The setting to be changed 199 | * @param value The value to set the setting to. 200 | * @example 201 | * nerdamer.set('PARSE2NUMBER', true) 202 | * nerdamer('cos(9)+1') // == 14846499/167059106 203 | * nerdamer.set('IMAGINARY', 'j') 204 | * nerdamer('sqrt(-1)') // == j 205 | */ 206 | export function set(setting: 'PARSE2NUMBER', value: boolean): typeof nerdamer 207 | export function set(setting: 'IMAGINARY', value: string | 'i'): typeof nerdamer 208 | export function set(setting: 'POWER_OPERATOR', value: '**' | '^'): typeof nerdamer 209 | 210 | export interface Expression { 211 | /** 212 | * Generates a JavaScript function given the expression. This is perfect for plotting and filtering user input. Plotting for the demo is accomplished using this. The order of the parameters is in alphabetical order by default but an argument array can be provided with the desired order. 213 | * @param args_array The argument array with the order in which they are preferred. 214 | */ 215 | buildFunction(args_array?: string[]): (...args: number[]) => number 216 | 217 | /** 218 | * Forces evaluation of the expression. 219 | * @example 220 | * const x = nerdamer('sin(9+5)') 221 | * //the expression is simplified but the functions aren't called: 222 | * x.toString() // == sin(14) 223 | * // force function calls with evaluate: 224 | * x.evaluate().toString() // == 127690464/128901187 225 | */ 226 | evaluate(): Expression 227 | 228 | /** 229 | * Substitutes a given value for another given value 230 | * @param value The value being substituted. 231 | * @param for_value The value to substitute for. 232 | */ 233 | sub(value: string, for_value: string): Expression 234 | 235 | /** 236 | * Get a list of the variables contained within the expression. 237 | */ 238 | variables(): string[] 239 | 240 | /** 241 | * Gets expression as LaTeX 242 | */ 243 | toTeX(): string 244 | 245 | /** 246 | * Returns the value of the expression as a string or a number 247 | */ 248 | valueOf(): string | number 249 | 250 | /** 251 | * Gets the list of reserved names. This is a list of names already in use by nerdamer excluding variable names. This is not a static list. 252 | * @param outputType Pass in the string 'decimals' to always get back numers as decimals. Pass in the string 'fractions' to always get back number as fractions. Defaults to decimals. 253 | */ 254 | text(outputType?: 'decimals' | 'fractions'): string 255 | 256 | /** 257 | * This method requires that the Solve, Calculus, and Algebra add-ons are loaded. It will attempt to solve an equation. If no solutions are found then an empty array is returned. It can solve for multivariate polynomials up to the third degree. After which it can solve numerically for polynomials up to the 100th degree. If it's a univariate equation it will attempt to solve it using numerical methods. 258 | * @param variable The variable to solve for. 259 | * @example 260 | * nerdamer('a*x^2+b*x=y').evaluate({y: 'x-7'}) // == ?? 261 | * eq.solveFor('x') // ?? TODO 262 | */ 263 | solveFor(variable: string): Expression | Expression[] 264 | 265 | /** 266 | * Forces the expression to displayed with decimals 267 | */ 268 | toDecimal(prec?: number): string 269 | 270 | /** 271 | * Checks to see if the expression's value equals a number. Compares the direct value returned. 272 | * The function will not check for all possible cases. To avoid this call evaluate. 273 | * @example 274 | * nerdamer('sqrt(5)').isNumber() 275 | * // false 276 | * nerdamer('sqrt(5)').evaluate().isNumber() 277 | * // true 278 | */ 279 | isNumber(): boolean 280 | 281 | /** 282 | * Checks if a number evaluates to an imaginary number 283 | * @example 284 | * nerdamer('sqrt(-5)+8').isImaginary() 285 | * // true 286 | * nerdamer('sqrt(5)+8').isImaginary() 287 | * // false 288 | */ 289 | isImaginary(): boolean 290 | 291 | /** 292 | * Adds a value to an expression 293 | * @example 294 | * nerdamer('x').add(3) 295 | */ 296 | add(symbol: number | string | Expression): Expression 297 | 298 | /** 299 | * Subtracts a value from an expression 300 | * @example 301 | * nerdamer('x').subtract(3) 302 | */ 303 | subtract(symbol: number | string | Expression): Expression 304 | 305 | /** 306 | * Multiplies an expression by a value 307 | * @example 308 | * nerdamer('x').multiply(3) 309 | */ 310 | multiply(symbol: number | string | Expression): Expression 311 | 312 | /** 313 | * Divides an expression by a valule 314 | * @example 315 | * nerdamer('9*x').divide(3) 316 | */ 317 | divide(symbol: number | string | Expression): Expression 318 | 319 | /** 320 | * Raises an expression to a power 321 | * @example 322 | * nerdamer('x').pow(3) 323 | */ 324 | pow(symbol: number | string | Expression): Expression 325 | 326 | /** 327 | * Checks if two values are equal 328 | * @param value The value being tested 329 | * @example 330 | * nerdamer('sqrt(9)').eq(3) 331 | * // true 332 | * nerdamer('x').eq('y') 333 | * // false 334 | */ 335 | eq(value: number | string | Expression): boolean 336 | 337 | /** 338 | * Checks if a value is less than another 339 | * @param value The value being tested 340 | * @example 341 | * nerdamer('sqrt(9)').lt(3) 342 | * // false 343 | * nerdamer('8').lt(100) 344 | * // true 345 | */ 346 | lt(value: number | string | Expression): boolean 347 | 348 | /** 349 | * Checks if a value is less than or equal to another 350 | * @param value The value being tested 351 | * @example 352 | * nerdamer('sqrt(9)').lte(3) 353 | * // true 354 | * nerdamer('x').lte(100) 355 | * // false 356 | */ 357 | lte(value: number | string | Expression): boolean 358 | 359 | /** 360 | * Checks if a value is greater than another 361 | * @param value The value being tested 362 | * @example 363 | * nerdamer('sqrt(9)').gt(3) 364 | * // false 365 | * nerdamer('800').gt(100) 366 | * // true 367 | */ 368 | gt(value: number | string | Expression): boolean 369 | 370 | /** 371 | * Checks if a value is greater than or equal to another 372 | * @param value The value being tested 373 | * @example 374 | * nerdamer('sqrt(9)').gte(3) 375 | * // true 376 | * nerdamer('x').gte(100) 377 | * // false 378 | */ 379 | gte(value: number | string | Expression): boolean 380 | 381 | /** 382 | * Expands a function or expression. 383 | * @example 384 | * nerdamer('x*(x+1)').expand(); 385 | * // x+x^2 386 | * nerdamer('(x+y)*(x-5)*x').expand(); 387 | * // -5*x*y-5*x^2+x^3+x^2*y 388 | */ 389 | expand(): Expression 390 | } 391 | 392 | ////////// CALCULUS 393 | 394 | /** 395 | * Gets the GCD of 2 polynomials 396 | * @param expression Returns the appropriate value if possible otherwise it returns the function with the simplified expression. 397 | * @param index The index of summation. 398 | * @param lower Starting index. 399 | * @param upper Ending index. 400 | */ 401 | export function sum(expression: ExpressionParam, 402 | index: string, 403 | lower: ExpressionParam, 404 | upper: ExpressionParam): Expression 405 | 406 | /** 407 | * 408 | * @param expression Returns the appropriate value if possible otherwise it returns the function with the simplified expression. 409 | * @param variable The variable with respect to which to integrate. 410 | */ 411 | export function integrate(expression: ExpressionParam, variable: string): Expression 412 | 413 | /** 414 | * 415 | * @param expression Returns the appropriate value if possible otherwise it returns the function with the simplified expression. 416 | * @param variable The variable with respect to which to differentiate. 417 | * @param n Calculate the nth derivative. 418 | */ 419 | export function diff(expression: ExpressionParam, variable: string, n?: int): Expression 420 | 421 | ////////// ALGEBRA 422 | 423 | /** 424 | * Divides 2 polynominals. 425 | * @param expression Returns the appropriate value if possible otherwise it returns the function with the simplified expression. 426 | */ 427 | export function divide(expression: ExpressionParam): Expression 428 | 429 | /** 430 | * Factor an expression. 431 | * @param expression Returns the appropriate value if possible otherwise it returns the function with the simplified expression. 432 | */ 433 | export function factor(expression: ExpressionParam): Expression 434 | 435 | /** 436 | * Gets the GCD of 2 polynomials. 437 | * @param expression Returns the appropriate value if possible otherwise it returns the function with the simplified expression. 438 | */ 439 | export function gcd(expression: ExpressionParam): Expression 440 | 441 | /** 442 | * Finds the roots of a univariate polynomial. 443 | * @param expression 444 | */ 445 | export function factor(expression: ExpressionParam): Expression 446 | } 447 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Nerdamer Tree Visualizer 5 | 6 | 7 | 8 | 136 | 137 | 138 | 139 |
140 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Martin Donk 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": { 3 | "name": "Gunnar Mein taking over from Martin Donk", 4 | "email": "info@together.science" 5 | }, 6 | "description": "javascript light-weight symbolic math library", 7 | "name": "nerdamer-prime", 8 | "license": "MIT", 9 | "version": "1.2.5", 10 | "homepage": "https://github.com/together-science/nerdamer-prime", 11 | "directory": { 12 | "lib": "./" 13 | }, 14 | "publishConfig": { 15 | "access": "public" 16 | }, 17 | "main": "nerdamer.core.js", 18 | "typings": "index.d.ts", 19 | "keywords": [ 20 | "math", 21 | "matrix", 22 | "complex", 23 | "number", 24 | "parser", 25 | "expression", 26 | "functions", 27 | "numeric", 28 | "equation solver", 29 | "linear algebra", 30 | "vectors", 31 | "symbolic", 32 | "integration", 33 | "differentiation", 34 | "derivative", 35 | "algebra", 36 | "calculus", 37 | "mathematics", 38 | "constants", 39 | "symbolic math" 40 | ], 41 | "repository": { 42 | "type": "git", 43 | "url": "git+https://github.com/together-science/nerdamer-prime.git" 44 | }, 45 | "bugs": { 46 | "url": "https://github.com/together-science/nerdamer-prime/issues" 47 | }, 48 | "engines": { 49 | "node": ">=0.10.0" 50 | }, 51 | "scripts": { 52 | "test": "jasmine", 53 | "gulp": "npx gulp concat_all" 54 | }, 55 | "devDependencies": { 56 | "gulp": "^4.0.2", 57 | "gulp-cli": "^2.3.0", 58 | "gulp-concat": "^2.6.1", 59 | "gulp-uglify": "^3.0.2", 60 | "gulp-watch": "^4.0.1", 61 | "jasmine": "^2.99.0" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /spec/LaTeX.spec.js: -------------------------------------------------------------------------------- 1 | /* global expect */ 2 | 3 | 'use strict'; 4 | 5 | var nerdamer = require('../nerdamer.core.js'); 6 | 7 | describe('TeX features', function () { 8 | it('should render TeX output correctly', function () { 9 | var testCases = [ 10 | { 11 | given: '2', 12 | TeX: '2', 13 | decimalTeX: '2' 14 | }, { 15 | given: '1/2', 16 | TeX: '\\frac{1}{2}', 17 | decimalTeX: '0.5' 18 | }, { 19 | given: '2*x', 20 | TeX: '2 \\cdot x', 21 | decimalTeX: '2 \\cdot x' 22 | }, { 23 | given: '2/5*x', 24 | TeX: '\\frac{2 \\cdot x}{5}', 25 | decimalTeX: '0.4 \\cdot x' 26 | }, { 27 | given: '2/5*x^2', 28 | TeX: '\\frac{2 \\cdot x^{2}}{5}', 29 | decimalTeX: '0.4 \\cdot x^{2}' 30 | }, { 31 | given: '1/2*x', 32 | TeX: '\\frac{x}{2}', 33 | decimalTeX: '0.5 \\cdot x' 34 | }, { 35 | given: '1/2*x^2', 36 | TeX: '\\frac{x^{2}}{2}', 37 | decimalTeX: '0.5 \\cdot x^{2}' 38 | }, { 39 | given: '1/2*2^(2/3)', 40 | TeX: '\\frac{1}{2^{\\frac{1}{3}}}', 41 | decimalTeX: '0.7937005259840998' 42 | }, { 43 | given: '2^(2/3)', 44 | TeX: '2^{\\frac{2}{3}}', 45 | decimalTeX: '1.5874010519681994' 46 | }, { 47 | given: '5/8*2^(2/3)*4', 48 | TeX: '\\frac{5}{2^{\\frac{1}{3}}}', 49 | decimalTeX: '3.968502629920499' 50 | }, 51 | { 52 | given: '3*x^(2/3)/4', 53 | TeX: '\\frac{3 \\cdot x^{\\frac{2}{3}}}{4}', 54 | decimalTeX: '0.75 \\cdot x^{0.66666666666666666667}' 55 | }, 56 | { 57 | given: '4*cos(x)', 58 | TeX: '4 \\cdot \\mathrm{cos}\\left(x\\right)', 59 | decimalTeX: '4 \\cdot \\mathrm{cos}\\left(x\\right)' 60 | }, { 61 | given: '(1/4)*cos(x)', 62 | TeX: '\\frac{\\mathrm{cos}\\left(x\\right)}{4}', 63 | decimalTeX: '0.25 \\cdot \\mathrm{cos}\\left(x\\right)' 64 | }, { 65 | given: '(5/4)*cos(x)', 66 | TeX: '\\frac{5 \\cdot \\mathrm{cos}\\left(x\\right)}{4}', 67 | decimalTeX: '1.25 \\cdot \\mathrm{cos}\\left(x\\right)' 68 | }, { 69 | given: '7/8*sqrt(x)', 70 | TeX: '\\frac{7 \\cdot \\sqrt{x}}{8}', 71 | decimalTeX: '0.875 \\cdot \\sqrt{x}' 72 | }, { 73 | given: '1/8*sqrt(x+8)', 74 | TeX: '\\frac{\\sqrt{x+8}}{8}', 75 | decimalTeX: '0.125 \\cdot \\sqrt{x+8}' 76 | }, { 77 | given: 'x/(x+y)', 78 | TeX: '\\frac{x}{x+y}', 79 | decimalTeX: '\\frac{x}{x+y}' 80 | }, { 81 | given: 'x/(x+y)^3', 82 | TeX: '\\frac{x}{\\left(x+y\\right)^{3}}', 83 | decimalTeX: '\\frac{x}{\\left(x+y\\right)^{3}}' 84 | }, { 85 | given: '(x+y)^3/x', 86 | TeX: '\\frac{\\left(x+y\\right)^{3}}{x}', 87 | decimalTeX: '\\frac{\\left(x+y\\right)^{3}}{x}' 88 | }, { 89 | given: '((x+1)*(x+2))/(x+5)', 90 | TeX: '\\frac{\\left(x+1\\right) \\cdot \\left(x+2\\right)}{x+5}', 91 | decimalTeX: '\\frac{\\left(x+1\\right) \\cdot \\left(x+2\\right)}{x+5}' 92 | }, { 93 | given: '((x+1)*(x+2)*u)/((x+5)*z)', 94 | TeX: '\\frac{\\left(x+1\\right) \\cdot \\left(x+2\\right) \\cdot u}{\\left(x+5\\right) \\cdot z}', 95 | decimalTeX: '\\frac{\\left(x+1\\right) \\cdot \\left(x+2\\right) \\cdot u}{\\left(x+5\\right) \\cdot z}' 96 | }, { 97 | given: '2/(x+y)^3', 98 | TeX: '\\frac{2}{\\left(x+y\\right)^{3}}', 99 | decimalTeX: '\\frac{2}{\\left(x+y\\right)^{3}}' 100 | }, { 101 | given: '2*(x+1)/(x+y)^3', 102 | TeX: '\\frac{2\\left(x+1\\right)}{\\left(x+y\\right)^{3}}', 103 | decimalTeX: '\\frac{2\\left(x+1\\right)}{\\left(x+y\\right)^{3}}' 104 | }, { 105 | given: '2*(x+1)^2/(x+y)^3', 106 | TeX: '\\frac{2 \\cdot \\left(x+1\\right)^{2}}{\\left(x+y\\right)^{3}}', 107 | decimalTeX: '\\frac{2 \\cdot \\left(x+1\\right)^{2}}{\\left(x+y\\right)^{3}}' 108 | }, { 109 | given: '(x)^x/(x+1)', 110 | TeX: '\\frac{x^{x}}{x+1}', 111 | decimalTeX: '\\frac{x^{x}}{x+1}' 112 | }, { 113 | given: '(3/4)*(x+y)^x/(x+5)^2', 114 | TeX: '\\frac{3 \\cdot \\left(x+y\\right)^{x}}{4 \\cdot \\left(x+5\\right)^{2}}', 115 | decimalTeX: '\\frac{0.75 \\cdot \\left(x+y\\right)^{x}}{\\left(x+5\\right)^{2}}' 116 | }, { 117 | given: '(3/4)*abs(x+y)^x/(x+5)^2', 118 | TeX: '\\frac{3 \\cdot \\left|x+y\\right|^{x}}{4 \\cdot \\left(x+5\\right)^{2}}', 119 | decimalTeX: '\\frac{0.75 \\cdot \\left|x+y\\right|^{x}}{\\left(x+5\\right)^{2}}' 120 | }, { 121 | given: 'x^(1/4)+3/4*x^2+cos(1/4)', 122 | TeX: '\\frac{3 \\cdot x^{2}}{4}+x^{\\frac{1}{4}}+\\mathrm{cos}\\left(\\frac{1}{4}\\right)', 123 | decimalTeX: '0.75 \\cdot x^{2}+x^{0.25}+\\mathrm{cos}\\left(0.25\\right)' 124 | }, { 125 | given: '-(x^wtf+1)^6-(t+1)/(x+3)^2', 126 | TeX: '-\\left(x^{wtf}+1\\right)^{6}-\\frac{t+1}{\\left(x+3\\right)^{2}}', 127 | decimalTeX: '-\\left(x^{wtf}+1\\right)^{6}-\\frac{t+1}{\\left(x+3\\right)^{2}}' 128 | }, { 129 | given: '2*(-log(x)*sin(x)+cos(x)*x^(-1))', 130 | TeX: '2\\left(-\\mathrm{log}\\left(x\\right) \\cdot \\mathrm{sin}\\left(x\\right)+\\frac{\\mathrm{cos}\\left(x\\right)}{x}\\right)', 131 | decimalTeX: '2\\left(-\\mathrm{log}\\left(x\\right) \\cdot \\mathrm{sin}\\left(x\\right)+\\frac{\\mathrm{cos}\\left(x\\right)}{x}\\right)' 132 | }, { 133 | given: '(x*(x+1))/(x^2+2*x+1)', 134 | TeX: '\\frac{x \\cdot \\left(x+1\\right)}{x^{2}+2 \\cdot x+1}', 135 | decimalTeX: '\\frac{x \\cdot \\left(x+1\\right)}{x^{2}+2 \\cdot x+1}' 136 | }, { 137 | given: 'x^2+2*x+y^2+y+6', 138 | TeX: 'x^{2}+2 \\cdot x+y^{2}+y+6', 139 | decimalTeX: 'x^{2}+2 \\cdot x+y^{2}+y+6' 140 | }, { 141 | given: '(-1*(x-1))', 142 | TeX: '-x+1', 143 | decimalTeX: '-x+1' 144 | }, { 145 | given: 'x!', 146 | TeX: 'x!', 147 | decimalTeX: 'x!' 148 | }, { 149 | given: '(x+1)!', 150 | TeX: '\\left(x+1\\right)!', 151 | decimalTeX: '\\left(x+1\\right)!' 152 | }, 153 | { 154 | given: 'x!+(x+1)!', 155 | TeX: '\\left(x+1\\right)!+x!', 156 | decimalTeX: '\\left(x+1\\right)!+x!' 157 | }, 158 | { 159 | given: '(x/(x+y))^n', 160 | TeX: '\\frac{x^{n}}{x+y}', 161 | decimalTeX: '\\frac{x^{n}}{x+y}' 162 | }, 163 | { 164 | given: '1/(a*b)', 165 | TeX: '\\frac{1}{a \\cdot b}', 166 | decimalTeX: '\\frac{1}{a \\cdot b}' 167 | }, 168 | { 169 | given: 'sum(a,b,c,Infinity)', 170 | TeX: '\\sum\\limits_{{b}={c}}^{\\infty} {a}', 171 | decimalTeX: '\\sum\\limits_{{b}={c}}^{\\infty} {a}' 172 | }, 173 | { 174 | given: 'product(a,b,c,Infinity)', 175 | TeX: '\\prod\\limits_{{b}={c}}^{\\infty} {a}', 176 | decimalTeX: '\\prod\\limits_{{b}={c}}^{\\infty} {a}' 177 | }, 178 | { 179 | given: 'mod(a,b)', 180 | TeX: 'a \\bmod b', 181 | decimalTeX: 'a \\bmod b' 182 | }, 183 | { 184 | given: 'nthroot(a,b)', 185 | TeX: '\\sqrt[b]{a}', 186 | decimalTeX: '\\sqrt[b]{a}' 187 | }, 188 | { 189 | given: 'x_aa_bb+y_cc_dd', 190 | TeX: 'x_{aa_{bb}}+y_{cc_{dd}}', 191 | decimalTeX: 'x_{aa_{bb}}+y_{cc_{dd}}' 192 | } 193 | ]; 194 | 195 | for(var i = 0; i < testCases.length; ++i) { 196 | // when 197 | var teX = nerdamer(testCases[i].given).toTeX(); 198 | var decimalTex = nerdamer(testCases[i].given).toTeX('decimal'); 199 | 200 | // then 201 | expect(teX).toEqual(testCases[i].TeX, 'for formula ' + testCases[i].given); 202 | expect(decimalTex).toEqual(testCases[i].decimalTeX, 'for formula ' + testCases[i].given); 203 | } 204 | }); 205 | 206 | /** #36: Weird results with sqrt */ 207 | it('should render square roots properly', function () { 208 | // given 209 | var formula = '2*sqrt(x)'; 210 | 211 | // when 212 | var teX = nerdamer(formula).toTeX(); 213 | 214 | // then 215 | expect(teX).toEqual('2 \\cdot \\sqrt{x}'); 216 | }); 217 | 218 | /** #39: Terms multiplied in brackets not rendered correctly */ 219 | it('should render parentheses', function () { 220 | // given 221 | var formula = '(x+1)*(x+2)'; 222 | 223 | // when 224 | var teX = nerdamer(formula).toTeX(); 225 | 226 | // then 227 | expect(teX).toEqual('\\left(x+1\\right) \\cdot \\left(x+2\\right)'); 228 | }); 229 | 230 | /** #41: Latex output should use descending order */ 231 | it('should use descending order of polynomials', function () { 232 | // given 233 | var formula = 'x^2+x+1'; 234 | 235 | // when 236 | var teX = nerdamer(formula).toTeX(); 237 | 238 | // then 239 | expect(teX).toEqual('x^{2}+x+1'); 240 | }); 241 | 242 | it('should support Greek letters', function () { 243 | // given 244 | var testCases = [ 245 | { 246 | given: 'alpha + beta', 247 | expected: '\\alpha+\\beta' 248 | }, 249 | { 250 | given: '5 * 3 / psi', 251 | expected: '\\frac{15}{\\psi}' 252 | }, 253 | { 254 | given: 'Xi ^ tau - 8*nu', 255 | expected: '\\Xi^{\\tau}-8 \\cdot \\nu' 256 | } 257 | ]; 258 | 259 | for(var i = 0; i < testCases.length; ++i) { 260 | // when 261 | var teX = nerdamer(testCases[i].given).toTeX(); 262 | 263 | // then 264 | expect(teX).toEqual(testCases[i].expected); 265 | } 266 | }); 267 | 268 | it('should explicitly convert to LaTeX', function () { 269 | expect(nerdamer.convertToLaTeX('realpart(a)')).toEqual('\\operatorname{Re}\\left(a\\right)'); 270 | expect(nerdamer.convertToLaTeX('imagpart(a)')).toEqual('\\operatorname{Im}\\left(a\\right)'); 271 | expect(nerdamer.convertToLaTeX('diff(cos(x),x)')).toEqual('\\frac{d}{d x}\\left({\\mathrm{cos}\\left(x\\right)}\\right)'); 272 | expect(nerdamer.convertToLaTeX('integrate(cos(x),x)')).toEqual('\\int {\\mathrm{cos}\\left(x\\right)}\\, dx'); 273 | expect(nerdamer.convertToLaTeX('2*(sqrt(3)+sqrt(2))')).toEqual('2 \\cdot \\left(\\sqrt{3} + \\sqrt{2}\\right)'); 274 | expect(nerdamer.convertToLaTeX('(a+1)(x+a)^(-5)+1')).toEqual('\\frac{a + 1}{{\\left(x + a\\right)}^{5}} + 1'); 275 | expect(nerdamer.convertToLaTeX('a*x^-3+1/a')).toEqual('\\frac{a}{{x}^{3}} + \\frac{1}{a}'); 276 | expect(nerdamer.convertToLaTeX('a*x^+3+1/a')).toEqual('a \\cdot {x}^{3} + \\frac{1}{a}'); 277 | expect(nerdamer.convertToLaTeX('x^2/y-x')).toEqual('\\frac{{x}^{2}}{y} - x'); 278 | }); 279 | 280 | it('should display integrals', function () { 281 | // given 282 | var testCases = [ 283 | { 284 | given: 'defint(log(2cos(x/2)),-π,π,x)', 285 | expected: '\\int\\limits_{-\\pi}^{\\pi} \\mathrm{log}\\left(2 \\cdot \\mathrm{cos}\\left(\\frac{x}{2}\\right)\\right) dx' 286 | }, 287 | { 288 | given: 'integrate(sin(x^x),x)', 289 | expected: '\\int{\\mathrm{sin}\\left(x^{x}\\right)}{dx}' 290 | }, 291 | ]; 292 | 293 | for(var i = 0; i < testCases.length; ++i) { 294 | // when 295 | var teX = nerdamer(testCases[i].given).toTeX(); 296 | 297 | // then 298 | expect(teX).toEqual(testCases[i].expected); 299 | } 300 | }); 301 | 302 | }); -------------------------------------------------------------------------------- /spec/TeXConvert.spec.js: -------------------------------------------------------------------------------- 1 | var nerdamer = require('../nerdamer.core.js'); 2 | it('Should convert from TeX', function () { 3 | expect(nerdamer.convertFromLaTeX('x^6 \\cdot cos(\\frac{1}{2})').toString()).toEqual('cos(1/2)*x^6'); 4 | expect(nerdamer.convertFromLaTeX('\\sqrt[3]{1+a}+1').toString()).toEqual('(1+a)^(1/3)+1'); 5 | expect(nerdamer.convertFromLaTeX('\\begin{matrix} \\begin{matrix}1 & 2 \\\\ 7 & 8\\end{matrix} & 2+9 \\\\ 7 & 8\\end{matrix}').toString()) 6 | .toEqual('matrix([matrix([1,2],[7,8]),11],[7,8])'); 7 | expect(nerdamer.convertFromLaTeX('\\int_{0}^{\\pi}(\\sin(y)),y').toString()).toEqual('defint(sin(y),0,pi,y)'); 8 | expect(nerdamer.convertFromLaTeX('\\frac12').toString()).toEqual('1/2'); 9 | expect(nerdamer.convertFromLaTeX('x_2a').toString()).toEqual('a*x_2'); 10 | expect(nerdamer.convertFromLaTeX('x^2a').toString()).toEqual('a*x^2'); 11 | }); 12 | -------------------------------------------------------------------------------- /spec/algebra.spec.js: -------------------------------------------------------------------------------- 1 | /* global expect */ 2 | 3 | 'use strict'; 4 | 5 | var nerdamer = require('../nerdamer.core.js'); 6 | require('../Algebra.js'); 7 | require('../Calculus.js'); 8 | require('../Solve.js'); 9 | 10 | describe('Algebra', function () { 11 | it('should perform gcd operations correctly', function () { 12 | expect(nerdamer('gcd(5*x^6+5*x^5+27*x^4+27*x^3+28*x^2+28*x, 5*x^3+7*x)').toString()).toEqual('5*x^3+7*x'); 13 | expect(nerdamer('gcd(-20+16*i,-10+8*i)').toString()).toEqual('-10+8*i'); 14 | expect(nerdamer('gcd(2*x^2+2*x+1,x+1)').toString()).toEqual('1'); 15 | expect(nerdamer('gcd(x^2+2*x+1,x+1)').toString()).toEqual('1+x'); 16 | expect(nerdamer('gcd(6*x^9+24*x^8+15*x^7+6*x^2+24*x+15, (2*x^2+8*x+5))').toString()).toEqual('2*x^2+8*x+5'); 17 | expect(nerdamer('gcd(x^8+4*x^7+4*x^6+3*x^5+12*x^4+12*x^3, (x^3+3))').toString()).toEqual('3+x^3'); 18 | expect(nerdamer('gcd(6*x^9+24*x^8+15*x^7+6*x^2+24*x+15, x^7+1)').toString()).toEqual('1+x^7'); 19 | expect(nerdamer('gcd(1+x^2,2*x)').toString()).toEqual('1'); 20 | expect(nerdamer('gcd(84*x^4+147*x^3+16*x^2+28*x, 44*x^5+77*x^4+16*x^3+28*x^2+12*x+21)').toString()).toEqual('4*x+7'); 21 | expect(nerdamer('gcd(5*x^11+90*x^9+361*x^7+473*x^5+72*x^3+91*x, 7150*x^12+9360*x^10+1375*x^9+1430*x^8+37550*x^7+1872*x^6+47075*x^5+7510*x^3+9360*x)').toString()).toEqual('5*x^5+x'); 22 | expect(nerdamer('gcd(7*x^4+7*x^3+4*x^2+5*x+1, 21*x^6+47*x^4+80*x^3+20*x^2+49*x+11)').toString()).toEqual('1+4*x+7*x^3'); 23 | expect(nerdamer('gcd(5*x^11+90*x^9+361*x^7+473*x^5+72*x^3+91*x, 7150*x^12+9360*x^10+1375*x^9+1430*x^8+37550*x^7+1872*x^6+47075*x^5+7510*x^3+9360*x,x)').toString()).toEqual('x'); 24 | expect(nerdamer('gcd(x^8+4*x^7+4*x^6+3*x^5+12*x^4+12*x^3, (x^3+3), 3+x^3)').toString()).toEqual('3+x^3'); 25 | expect(nerdamer('gcd(a, b, c)').toString()).toEqual('gcd(a,b,c)'); 26 | expect(nerdamer('gcd(18,12, 6)').toString()).toEqual('6'); 27 | expect(nerdamer('gcd(3, 5, 7)').toString()).toEqual('1'); 28 | expect(nerdamer('gcd(1/2, 1/3, 1/4)').toString()).toEqual('1/12'); 29 | expect(nerdamer('gcd(5%, 15%, 25%)').toString()).toEqual('1/20'); 30 | expect(nerdamer('gcd(1/a, 1/b, 1/c)').toString()).toEqual('gcd(a^(-1),b^(-1),c^(-1))'); 31 | expect(nerdamer('gcd(2^x, 6^x)').toString()).toEqual('2^x'); 32 | expect(nerdamer('gcd(a, b, c, gcd(x, y, z, gcd(f,gcd(g,h))))').toString()).toEqual('gcd(a,b,c,x,y,z,f,g,h)'); 33 | expect(nerdamer('gcd(2^x, 6^x)').toString()).toEqual('2^x'); 34 | expect(nerdamer('gcd(a,a,b,b,gcd(c,c))').toString()).toEqual('1'); 35 | expect(nerdamer('gcd(a,a)').toString()).toEqual('a'); 36 | expect(nerdamer('gcd(a^b,a^c)').toString()).toEqual('a'); 37 | expect(nerdamer('gcd(a^c,b^c)').toString()).toEqual('1'); 38 | expect(nerdamer('gcd(a^a,a^a)').toString()).toEqual('a^a'); 39 | }); 40 | 41 | it('should perform lcm operations correctly', function () { 42 | expect(nerdamer('lcm(5*x^6+5*x^5+27*x^4+27*x^3+28*x^2+28*x, 5*x^3+7*x)').toString()).toEqual('27*x^3+27*x^4+28*x+28*x^2+5*x^5+5*x^6'); 43 | expect(nerdamer('lcm(-20+16*i,-10+8*i)').toString()).toEqual('-10+8*i'); 44 | expect(nerdamer('lcm(2*x^2+2*x+1,x+1)').toString()).toEqual('(1+2*x+2*x^2)*(1+x)'); 45 | expect(nerdamer('lcm(x^2+2*x+1,x+1)').toString()).toEqual('1+2*x+x^2'); 46 | expect(nerdamer('lcm(6*x^9+24*x^8+15*x^7+6*x^2+24*x+15, (2*x^2+8*x+5))').toString()).toEqual('15+15*x^7+24*x+24*x^8+6*x^2+6*x^9'); 47 | expect(nerdamer('lcm(x^8+4*x^7+4*x^6+3*x^5+12*x^4+12*x^3, (x^3+3))').toString()).toEqual('12*x^3+12*x^4+3*x^5+4*x^6+4*x^7+x^8'); 48 | expect(nerdamer('lcm(6*x^9+24*x^8+15*x^7+6*x^2+24*x+15, x^7+1)').toString()).toEqual('15+15*x^7+24*x+24*x^8+6*x^2+6*x^9'); 49 | expect(nerdamer('lcm(1+x^2,2*x)').toString()).toEqual('2*(1+x^2)*x'); 50 | expect(nerdamer('lcm(84*x^4+147*x^3+16*x^2+28*x, 44*x^5+77*x^4+16*x^3+28*x^2+12*x+21)').toString()).toEqual('(12*x+16*x^3+28*x^2+44*x^5+77*x^4+21)*(147*x^3+16*x^2+28*x+84*x^4)*(4*x+7)^(-1)'); 51 | expect(nerdamer('lcm(5*x^11+90*x^9+361*x^7+473*x^5+72*x^3+91*x, 7150*x^12+9360*x^10+1375*x^9+1430*x^8+37550*x^7+1872*x^6+47075*x^5+7510*x^3+9360*x)').toString()).toEqual('(1375*x^9+1430*x^8+1872*x^6+37550*x^7+47075*x^5+7150*x^12+7510*x^3+9360*x+9360*x^10)*(361*x^7+473*x^5+5*x^11+72*x^3+90*x^9+91*x)*(5*x^5+x)^(-1)'); 52 | expect(nerdamer('lcm(7*x^4+7*x^3+4*x^2+5*x+1, 21*x^6+47*x^4+80*x^3+20*x^2+49*x+11)').toString()).toEqual('(1+4*x+7*x^3)^(-1)*(1+4*x^2+5*x+7*x^3+7*x^4)*(11+20*x^2+21*x^6+47*x^4+49*x+80*x^3)'); 53 | expect(nerdamer('lcm(5*x^11+90*x^9+361*x^7+473*x^5+72*x^3+91*x, 7150*x^12+9360*x^10+1375*x^9+1430*x^8+37550*x^7+1872*x^6+47075*x^5+7510*x^3+9360*x,x)').toString()).toEqual('(1375*x^9+1430*x^8+1872*x^6+37550*x^7+47075*x^5+7150*x^12+7510*x^3+9360*x+9360*x^10)*(361*x^7+473*x^5+5*x^11+72*x^3+90*x^9+91*x)*(5*x^6+x^2)^(-1)*x'); 54 | expect(nerdamer('lcm(x^8+4*x^7+4*x^6+3*x^5+12*x^4+12*x^3, (x^3+3), 3+x^3)').toString()).toEqual('(12*x^3+12*x^4+3*x^5+4*x^6+4*x^7+x^8)*(3+x^3)^2*(6*x^3+x^6+9)^(-1)'); 55 | expect(nerdamer('lcm(a, b, c)').toString()).toEqual('a*b*c*gcd(a*b,a*c,b*c)^(-1)'); 56 | expect(nerdamer('lcm(18,12, 6)').toString()).toEqual('36'); 57 | expect(nerdamer('lcm(3, 5, 7)').toString()).toEqual('105'); 58 | expect(nerdamer('lcm(1/2, 1/3, 1/4)').toString()).toEqual('1'); 59 | expect(nerdamer('lcm(5%, 15%, 25%)').toString()).toEqual('3/4'); 60 | expect(nerdamer('lcm(1/a, 1/b, 1/c)').toString()).toEqual('1'); 61 | expect(nerdamer('lcm(2^x, 6^x)').toString()).toEqual('6^x'); 62 | expect(nerdamer('lcm(a, b, c, gcd(x, y, z, gcd(f,gcd(g,h))))').toString()).toEqual('a*b*c*gcd(x,y,z,f,g,h)'); 63 | expect(nerdamer('lcm(2^x, 6^x)').toString()).toEqual('6^x'); 64 | expect(nerdamer('lcm(a,a,b,b,gcd(c,c))').toString()).toEqual('a^2*b^2*c*gcd(a^2*b^2,a^2*b*c,a^2*b*c,a*b^2*c,a*b^2*c)^(-1)'); 65 | expect(nerdamer('lcm(a,a)').toString()).toEqual('a^2*gcd(a,a)^(-1)'); 66 | expect(nerdamer('lcm(a^b,a^c)').toString()).toEqual('a^(-1+b+c)'); 67 | expect(nerdamer('lcm(a^c,b^c)').toString()).toEqual('a^c*b^c'); 68 | expect(nerdamer('lcm(a^a,a^a)').toString()).toEqual('a^a'); 69 | }); 70 | 71 | describe('isPoly', function () { 72 | it('should detect polynomials', function () { 73 | expect(nerdamer('51').symbol.isPoly(true)).toEqual(true); 74 | expect(nerdamer('x^2+1').symbol.isPoly(true)).toEqual(true); 75 | expect(nerdamer('51/x').symbol.isPoly(true)).toEqual(false); 76 | expect(nerdamer('x^2+1/x').symbol.isPoly(true)).toEqual(false); 77 | expect(nerdamer('y*x^2+1/x').symbol.isPoly(true)).toEqual(false); 78 | expect(nerdamer('y*x^2+x').symbol.isPoly(true)).toEqual(true); 79 | expect(nerdamer('7*y*x^2+z*x+4').symbol.isPoly(true)).toEqual(true); 80 | expect(nerdamer('7*y*x^2+z*x^-1+4').symbol.isPoly(true)).toEqual(false); 81 | expect(nerdamer('sqrt(5*x)+7').symbol.isPoly(true)).toEqual(false); 82 | expect(nerdamer('abs(5*x^3)-x+7').symbol.isPoly(true)).toEqual(false); 83 | expect(nerdamer('cos(x)^2+cos(x)+1').symbol.isPoly(true)).toEqual(false); 84 | expect(nerdamer('sqrt(97)x^2-sqrt(13)x+sqrt(14)x+sqrt(43)x^2+sqrt(3)*sqrt(101)').symbol.isPoly(true)).toEqual(true); 85 | expect(nerdamer('-5 sqrt(14)x-14x^2 sqrt(83)').symbol.isPoly(true)).toEqual(true); 86 | }); 87 | 88 | it('should evaluate abs() directly', function () { 89 | // given 90 | var formula = 'abs(5*x^2)-x+11'; 91 | 92 | // when 93 | var isPoly = nerdamer(formula).symbol.isPoly(); 94 | 95 | // then 96 | expect(isPoly).toBeTruthy(); 97 | }); 98 | }); 99 | 100 | it('should perform divisions', function () { 101 | expect(nerdamer('div(x^2*y^3+b*y^2+3*a*x^2*y+3*a*b, y^2+3*a)').toString()).toEqual('[b+x^2*y,0]'); 102 | expect(nerdamer('div(x^2, x^3)').toString()).toEqual('[0,x^2]'); 103 | expect(nerdamer('div(cos(x^2)^2+2*cos(x^2)+1, cos(x^2)+1)').toString()).toEqual('[1+cos(x^2),0]'); 104 | expect(nerdamer('div(2*x^2+2*x+1, x+1)').toString()).toEqual('[2*x,1]'); 105 | expect(nerdamer('div(7*x,2*x)').toString()).toEqual('[7/2,0]'); 106 | expect(nerdamer('div(7*b*z^2+14*y*z+14*a*x^2*z-b*t*z-2*t*y-2*a*t*x^2, 7*z-t)').toString()).toEqual('[2*a*x^2+2*y+b*z,0]'); 107 | expect(nerdamer('div(x^2+5, y-1)').toString()).toEqual('[0,5+x^2]'); 108 | expect(nerdamer('div(4*a*x^2*y^2+4*a*y^2+b*x^2+a*x^2+b+a, x^2+1)').toString()).toEqual('[4*a*y^2+a+b,0]'); 109 | expect(nerdamer('div(4*a*x^2*y^2+4*a*y^2+b*x^2+a*x^2+b+a+u^6+1, x^2+1)').toString()).toEqual('[4*a*y^2+a+b,1+u^6]'); 110 | expect(nerdamer('div(15*x^9-25*x^7-35*x^6+6*x^5+3*x^4-10*x^3-19*x^2-7*x, 3*x^3-5*x-7)').toString()).toEqual('[2*x^2+5*x^6+x,0]'); 111 | expect(nerdamer('div(sin(x)^2*tan(x)-4*cos(x)*tan(x)+cos(x)*sin(x)^2-4*cos(x)^2, sin(x)^2-4*cos(x)^2)').toString()).toEqual('[cos(x)+tan(x),-4*cos(x)*tan(x)-4*cos(x)^2+4*cos(x)^3+4*cos(x)^2*tan(x)]'); 112 | expect(nerdamer('div(y^2*z-4*x*z+x*y^2-4*x^2, y^2-4*x^2)').toString()).toEqual('[x+z,-4*x*z-4*x^2+4*x^3+4*x^2*z]'); 113 | expect(nerdamer('div(-5*y^2+16*a*y+5*x^4+14*a*x^2-3*a^2, 3*a-y+x^2)').toString()).toEqual('[-a+5*x^2+5*y,0]'); 114 | expect(nerdamer('div(y^2+2*x*y+x^2,x+y)').toString()).toEqual('[x+y,0]'); 115 | expect(nerdamer('div(x*y^2+x^2*y-y-x, x*y-1)').toString()).toEqual('[x+y,0]'); 116 | expect(nerdamer('div(y^2*z-4*x*z+x*y^2-4*x^2+x^2, y^2-4*x^2)').toString()).toEqual('[x+z,-3*x^2+4*x^3-4*x*z+4*x^2*z]'); 117 | expect(nerdamer('div(7*x^6*z-a*x*z+28*a*x^6*y^3-4*a^2*x*y^3+7*b*x^6-a*b*x, 4*y^3*a+z+b)').toString()).toEqual('[-a*x+7*x^6,0]'); 118 | expect(nerdamer('div(x^2+5, cos(x)-1)').toString()).toEqual('[0,5+x^2]'); 119 | expect(nerdamer('div((1+z), t*x+7)').toString()).toEqual('[0,1+z]'); 120 | expect(nerdamer('div(-x^2*y-y+4*a*x^2+t+4*a+6*b, x^2+1)').toString()).toEqual('[-y+4*a,6*b+t]'); 121 | expect(nerdamer('div(15*x^9-25*x^7-35*x^6+6*x^5+3*x^4-10*x^3-19*x^2-7*x+y, 3*x^3-5*x-7)').toString()).toEqual('[2*x^2+5*x^6+x,y]'); 122 | expect(nerdamer('div(x^2+2*x+1+u, x+1)').toString()).toEqual('[1+x,u]'); 123 | expect(nerdamer('div(y^3+x*y^2+x^2*y+x^3+x, x+y)').toString()).toEqual('[x^2+y^2,x]'); 124 | expect(nerdamer('div(b*y*z+7*x^6*z-a*x*z-7*z+4*a*b*y^4+28*a*x^6*y^3-4*a^2*x*y^3-28*a*y^3+b^2*y+7*b*x^6-a*b*x-7*b, 4*y^3*a+z+b)').toString()).toEqual('[-7-a*x+7*x^6+b*y,0]'); 125 | expect(nerdamer('div(b*y*z-a*x*z+4*a*b*y^4-4*a^2*x*y^3+b^2*y-a*b*x, 4*y^3*a+z+b)').toString()).toEqual('[-a*x+b*y,0]'); 126 | expect(nerdamer('div(17*x^3*y+3*x^2*y+34*x+6, x^2*y+2)').toString()).toEqual('[17*x+3,0]'); 127 | expect(nerdamer('div(b^2*y^2+2*a*b*y^2+a^2*y^2+2*b^2*x*y+4*a*b*x*y+2*a^2*x*y+b^2*x^2+2*a*b*x^2+a^2*x^2, 2*b*y^2+2*a*y^2+4*b*x*y+4*a*x*y+2*b*x^2+2*a*x^2)').toString()).toEqual('[(1/2)*a+(1/2)*b,0]'); 128 | expect(nerdamer('div(2*a*b*x+2*a*b*y+a^2*x+a^2*y+b^2*x+b^2*y, x+y)').toString()).toEqual('[2*a*b+a^2+b^2,0]'); 129 | expect(nerdamer('div((2x-1)(3x^2+5x-2)-7x-14,x^2+1)').toString()).toEqual('[6*x+7,-19-22*x]'); 130 | expect(nerdamer('div(2(x+1)^5+1,x+2)').toString()).toEqual('[2+2*x^4+4*x+6*x^3+8*x^2,-1]'); 131 | expect(nerdamer('divide(a*b^(-1)+b^(-1)*c,a+c)').toString()).toEqual('b^(-1)'); 132 | expect(nerdamer('divide(-20+16*i,-10+8*i)').toString()).toEqual('2'); 133 | }); 134 | /** #3: "(a-b)^2 - (b-a)^2" not simplifying. */ 135 | it('should simplify to 0', function () { 136 | // given 137 | var formula = '(a-b)^2-(b-a)^2'; 138 | 139 | // when 140 | var result = nerdamer(formula, null, ['numer', 'expand']).toString(); 141 | 142 | // then 143 | expect(result).toBe('0'); 144 | }); 145 | /** #40: Expected more simple solution for factoring. */ 146 | it('should use simple factor result', function () { 147 | // given 148 | var formula = 'factor(x^2+x+1/4)'; 149 | 150 | // when 151 | var result = nerdamer(formula).toString(); 152 | 153 | // then 154 | expect(result).toBe('(1/4)*(1+2*x)^2'); 155 | }); 156 | 157 | /** #43: Formula not expanded. */ 158 | it('should expand formula', function () { 159 | // given 160 | var formula = 'expand((x+5)(x-3)-x^2)'; 161 | 162 | // when 163 | var result = nerdamer(formula).toString(); 164 | 165 | // then 166 | expect(result).toBe('-15+2*x'); 167 | }); 168 | it('should factor correctly', function () { 169 | expect(nerdamer('factor(x^2+2*x+1)').toString()).toEqual('(1+x)^2'); 170 | expect(nerdamer('factor(x^2-y^2)').toString()).toEqual('(-y+x)*(x+y)'); 171 | expect(nerdamer('factor(a^2*x^2-b^2*y^2)').toString()).toEqual('(-b*y+a*x)*(a*x+b*y)'); 172 | expect(nerdamer('factor(b^6+3*a^2*b^4+3*a^4*b^2+a^6)').toString()).toEqual('(a^2+b^2)^3'); 173 | expect(nerdamer('factor(b^6+12*a^2*b^4+48*a^4*b^2+64*a^6)').toString()).toEqual('((9007199254740996/2251799813685249)*a^2+b^2)^3'); 174 | expect(nerdamer('factor(c^6+3*b^2*c^4+3*a^2*c^4+3*b^4*c^2+6*a^2*b^2*c^2+3*a^4*c^2+b^6+3*a^2*b^4+3*a^4*b^2+a^6)').toString()).toEqual('(a^2+b^2+c^2)^3'); 175 | expect(nerdamer('factor(x^4+25*x^3+234*x^2+972*x+1512)').toString()).toEqual('(6+x)^3*(7+x)'); 176 | expect(nerdamer('factor(x^5+32*x^4+288*x^3-418*x^2-16577*x-55902)').toString()).toEqual('(-7+x)*(11+x)^3*(6+x)'); 177 | expect(nerdamer('factor(x^2*y*z+x*z+t*x^2*y+t*x)').toString()).toEqual('(1+x*y)*(t+z)*x'); 178 | expect(nerdamer('factor(x^2*y+x^2)').toString()).toEqual('(1+y)*x^2'); 179 | expect(nerdamer('factor(sqrt(4*x^2*y+4*x^2))').toString()).toEqual('2*abs(x)*sqrt(1+y)'); 180 | expect(nerdamer('factor(x^3-1/2x^2-13/2x-3)').toString()).toEqual('(1/2)*(-3+x)*(1+2*x)*(2+x)'); 181 | expect(nerdamer('factor(x^16-1)').toString()).toEqual('(-1+x)*(1+x)*(1+x^2)*(1+x^4)*(1+x^8)'); 182 | expect(nerdamer('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)').toString()).toEqual('10*(-1+x)*(1+x)*(3*x+4)*(6+x)^6'); 183 | expect(nerdamer('factor((7x^3+4x^2+x)/(12x^3+6x^2-2x))').toString()).toEqual('(1/2)*(-1+3*x+6*x^2)^(-1)*(1+4*x+7*x^2)'); 184 | expect(nerdamer('factor((-2x-2x^2-2))').toString()).toEqual('2*(-1-x-x^2)'); 185 | expect(nerdamer('factor(1331*x^3*y^3+216*z^6)').toString()).toEqual('(-66*x*y*z^2+121*x^2*y^2+36*z^4)*(11*x*y+6*z^2)'); 186 | expect(nerdamer('factor(1331*x^3*y^3-216*z^6)').toString()).toEqual('(-6*z^2+11*x*y)*(121*x^2*y^2+36*z^4+66*x*y*z^2)'); 187 | expect(nerdamer('factor(64a^3-27b^3)').toString()).toEqual('(-3*b+4*a)*(12*a*b+16*a^2+9*b^2)'); 188 | expect(nerdamer('factor(64*x^3+125)').toString()).toEqual('(-20*x+16*x^2+25)*(4*x+5)'); 189 | expect(nerdamer('factor((-5*K+32)^2)').toString()).toEqual('(-32+5*K)^2'); 190 | expect(nerdamer('factor(100)').toString()).toEqual('2^2*5^2'); 191 | expect(nerdamer('factor(100*x)').toString()).toEqual('100*x'); 192 | expect(nerdamer('(2*y+p)^2').toString()).toEqual('(2*y+p)^2'); 193 | expect(nerdamer('factor((-1+x)*(y+1))').toString()).toEqual('(-1+x)*(1+y)'); 194 | expect(nerdamer('factor(x^2-6*x+9-4*y^2)').toString()).toEqual('(-2*y-3+x)*(-3+2*y+x)'); 195 | expect(nerdamer('factor((x^2+4x+4)-y^2)').toString()).toEqual('(-y+2+x)*(2+x+y)'); 196 | expect(nerdamer('factor(baseunit_m^2*sin(alpha)+baseunit_m^2*cos(alpha))').toString()).toEqual('(cos(alpha)+sin(alpha))*baseunit_m^2'); 197 | expect(nerdamer('factor(baseunit_m^2*sin(alpha)+baseunit_m^2)').toString()).toEqual('(1+sin(alpha))*baseunit_m^2'); 198 | expect(nerdamer('factor(2*baseunit_m^2*sin(3*alpha)+(4)*baseunit_m^2*cos(5*alpha)^2+6*baseunit_m^2*sin(7*alpha)^2+8*baseunit_m^2)').toString()).toEqual('2*(2*cos(5*alpha)^2+3*sin(7*alpha)^2+4+sin(3*alpha))*baseunit_m^2'); 199 | expect(nerdamer('factor(81-(16a^2-56a+49))').toString()).toEqual('8*(-a+4)*(1+2*a)'); 200 | expect(nerdamer('factor((9x^2-12x+4)-25)').toString()).toEqual('3*(-7+3*x)*(1+x)'); 201 | expect(nerdamer('factor((x^2+4x+4)+x*y+2y)').toString()).toEqual('(2+x)*(2+x+y)'); 202 | expect(nerdamer('factor((4x^2+24x+36)-14x*y-42y)').toString()).toEqual('2*(-7*y+2*x+6)*(3+x)'); 203 | expect(nerdamer('factor(35a*b-15b+(49a^2-42a+9))').toString()).toEqual('(-3+5*b+7*a)*(-3+7*a)'); 204 | expect(nerdamer('factor(1-6a^2+9a^4)').toString()).toEqual('(-1+3*a^2)^2'); 205 | expect(nerdamer('factor(1-6a^2+9a^4-49b^2)').toString()).toEqual('(-1+3*a^2+7*b)*(-1-7*b+3*a^2)'); 206 | expect(nerdamer("factor((-b*c+5*a)*(b*c)^(-1))").toString()).toEqual('(-b*c+5*a)*(b*c)^(-1)'); 207 | }); 208 | it('should not have any regression to factor', function() { 209 | //this test will absolutely break as factor improves enough to factor this expression. For now it just serves as a safeguard 210 | expect(nerdamer('factor(x^a+2x^(a-1)+1x^(a-2))').toString()).toEqual('2*x^(-1+a)+x^(-2+a)+x^a'); 211 | }); 212 | it('should correctly determine the polynomial degree', function () { 213 | expect(nerdamer('deg(x^2+2*x+x^5)').toString()).toEqual('5'); 214 | expect(nerdamer('deg(x^2+2*x+x^x)').toString()).toEqual('max(2,x)'); 215 | expect(nerdamer('deg(x^2+2*x+cos(x))').toString()).toEqual('2'); 216 | expect(nerdamer('deg(x^a+x^b+x^c,x)').toString()).toEqual('max(a,b,c)'); 217 | expect(nerdamer('deg(a*x^2+b*x+c,x)').toString()).toEqual('2'); 218 | }); 219 | it('should correctly peform partial fraction decomposition', function () { 220 | expect(nerdamer('partfrac((3*x+2)/(x^2+x), x)').toString()).toEqual('(1+x)^(-1)+2*x^(-1)'); 221 | expect(nerdamer('partfrac((17*x-53)/(x^2-2*x-15), x)').toString()).toEqual('13*(3+x)^(-1)+4*(-5+x)^(-1)'); 222 | expect(nerdamer('partfrac((x^3+2)/(x+1)^2,x)').toString()).toEqual('(1+x)^(-2)+3*(1+x)^(-1)-2+x'); 223 | expect(nerdamer('partfrac(x/(x-1)^2, x)').toString()).toEqual('(-1+x)^(-1)+(-1+x)^(-2)'); 224 | expect(nerdamer('partfrac((x^2+1)/(x*(x-1)^3), x)').toString()).toEqual('(-1+x)^(-1)+2*(-1+x)^(-3)-x^(-1)'); 225 | expect(nerdamer('partfrac((17-53)/(x^2-2*x-15), x)').toString()).toEqual('(-9/2)*(-5+x)^(-1)+(9/2)*(3+x)^(-1)'); 226 | expect(nerdamer('partfrac(1/(x^6-1),x)').toString()).toEqual('(-1/3)*(-x+x^2+1)^(-1)+(-1/3)*(1+x+x^2)^(-1)+(-1/6)*(1+x)^(-1)+(-1/6)*(1+x+x^2)^(-1)*x+(1/6)*(-1+x)^(-1)+(1/6)*(-x+x^2+1)^(-1)*x'); 227 | expect(nerdamer('partfrac((3*x^2-3*x-8)/((x-5)*(x^2+x-4)),x)').toString()).toEqual('(-4+x+x^2)^(-1)*x+2*(-5+x)^(-1)'); 228 | expect(nerdamer('partfrac(15*(9+s^2)^(-1)*cos(1)+5*(9+s^2)^(-1)*s*sin(1),s)').toString()).toEqual('(15*cos(1)+5*s*sin(1))*(9+s^2)^(-1)'); 229 | }); 230 | it('should prime factor correctly', function () { 231 | expect(nerdamer('pfactor(100!)').toString()).toEqual('(11^9)*(13^7)*(17^5)*(19^5)*(23^4)*(29^3)*(2^97)*(31^3)*(37^2)*(3^48)*(41^2)*(43^2)*(47^2)*(53)*(59)*(5^24)*(61)*(67)*(71)*(73)*(79)*(7^16)*(83)*(89)*(97)'); 232 | expect(nerdamer('pfactor(100)').toString()).toEqual('(2^2)*(5^2)'); 233 | expect(nerdamer('pfactor(8)').toString()).toEqual('(2^3)'); 234 | expect(nerdamer('pfactor(999999999999)').toString()).toEqual('(101)*(11)*(13)*(37)*(3^3)*(7)*(9901)'); 235 | expect(nerdamer('pfactor(1000000005721)').toString()).toEqual('(1000000005721)'); 236 | expect(nerdamer('pfactor(1000000005721092)').toString()).toEqual('(131)*(212044106387)*(2^2)*(3^2)'); 237 | expect(nerdamer('pfactor(-10000000114421840327308)').toString()).toEqual('(-2^2)*(480827)*(7)*(8345706745687)*(89)'); 238 | expect(nerdamer('pfactor(-7877474663)').toString()).toEqual('(-97)*(180871)*(449)'); 239 | expect(nerdamer('pfactor(15!+1)').toString()).toEqual('(46271341)*(479)*(59)'); 240 | expect(nerdamer('pfactor(15!+11!)').toString()).toEqual('(11)*(181^2)*(2^8)*(3^4)*(5^2)*(7)'); 241 | expect(nerdamer('pfactor(product(n!,n,1,10))').toString()).toEqual('(2^38)*(3^17)*(5^7)*(7^4)'); 242 | expect(nerdamer('pfactor(4677271)').toString()).toEqual('(2089)*(2239)'); 243 | }); 244 | it('should get coeffs', function () { 245 | expect(nerdamer('coeffs(x^2+2*x+1, x)').toString()).toEqual('[1,2,1]'); 246 | expect(nerdamer('coeffs(a*b*x^2+c*x+d, x)').toString()).toEqual('[d,c,a*b]'); 247 | expect(nerdamer('coeffs(t*x, x)').toString()).toEqual('[0,t]'); 248 | expect(nerdamer('coeffs(b*(t*x-5), x)').toString()).toEqual('[-5*b,b*t]'); 249 | expect(nerdamer('coeffs(a*x^2+b*x+c+x, x)').toString()).toEqual('[c,1+b,a]'); 250 | }); 251 | it('should get all coeffs', function () { 252 | expect(nerdamer('coeffs(x+A+1,x)').toString()).toEqual('[1+A,1]'); 253 | expect(nerdamer.coeffs('2x+i*x+5', 'x').toString()).toEqual('[5,2+i]'); 254 | }); 255 | it('should calculate the line function', function () { 256 | expect(nerdamer('line([1,2], [3,4])').toString()).toEqual('1+x'); 257 | expect(nerdamer('line([a1,b1], [a2,b2], t)').toString()).toEqual('(-a1+a2)^(-1)*(-b1+b2)*t-(-a1+a2)^(-1)*(-b1+b2)*a1+b1'); 258 | }); 259 | it('should simplify correctly', function () { 260 | expect(nerdamer('simplify(sin(x)^2+cos(x)^2)').toString()).toEqual('1'); 261 | expect(nerdamer('simplify(1/2*sin(x^2)^2+cos(x^2)^2)').toString()).toEqual('(1/4)*(3+cos(2*x^2))'); 262 | expect(nerdamer('simplify(0.75*sin(x^2)^2+cos(x^2)^2)').toString()).toEqual('(1/8)*(7+cos(2*x^2))'); 263 | expect(nerdamer('simplify((x^2+4*x-45)/(x^2+x-30))').toString()).toEqual('(6+x)^(-1)*(9+x)'); 264 | expect(nerdamer('simplify(1/(x-1)+1/(1-x))').toString()).toEqual('0'); 265 | expect(nerdamer('simplify((x-1)/(1-x))').toString()).toEqual('-1'); 266 | expect(nerdamer('simplify((x^2+2*x+1)/(x+1))').toString()).toEqual('1+x'); 267 | expect(nerdamer('simplify((- x + x^2 + 1)/(x - x^2 - 1))').toString()).toEqual('-1'); 268 | expect(nerdamer('simplify(n!/(n+1)!)').toString()).toEqual('(1+n)^(-1)'); 269 | expect(nerdamer('simplify((17/2)*(-10+8*i)^(-1)-5*(-10+8*i)^(-1)*i)').toString()).toEqual('(-9/82)*i-125/164'); 270 | expect(nerdamer('simplify((-2*i+7)^(-1)*(3*i+4))').toString()).toEqual('(29/53)*i+22/53'); 271 | expect(nerdamer('simplify(((a+b)^2)/c)').toString()).toEqual('(a+b)^2*c^(-1)'); 272 | expect(nerdamer('simplify(-(-5*x - 9 + 2*y))').toString()).toEqual('-2*y+5*x+9'); 273 | expect(nerdamer('simplify(a/b+b/a)').toString()).toEqual('(a^2+b^2)*a^(-1)*b^(-1)'); 274 | expect(nerdamer('simplify(((2*e^t)/(e^t))+(1/(e^t)))').toString()).toEqual('(1+2*e^t)*e^(-t)'); 275 | expect(nerdamer('simplify(0.5sqrt(4a+4y))').toString()).toEqual('sqrt(a+y)'); 276 | expect(nerdamer('simplify(log(3*(a/b)^2))').toString()).toEqual('-2*log(b)+2*log(a)+30312094/27591257'); 277 | expect(nerdamer('simplify(log(e*(a/b)^2))').toString()).toEqual('-2*log(b)+1+2*log(a)'); 278 | expect(nerdamer('simplify(log(a/b))').toString()).toEqual('-log(b)+log(a)'); 279 | expect(nerdamer('simplify(cos(a/b))').toString()).toEqual('cos(a*b^(-1))'); 280 | expect(nerdamer('simplify(cos(x)^2+sin(x)^2+cos(x)-tan(x)-1+sin(x^2)^2+cos(x^2)^2)').toString()).toEqual('-tan(x)+1+cos(x)'); 281 | expect(nerdamer('simplify((1/2)*(-x+2*y))').toString()).toEqual('(1/2)*(-x+2*y)'); 282 | expect(nerdamer('simplify((-1/2)x+y)').toString()).toEqual('(1/2)*(-x+2*y)'); 283 | expect(nerdamer('simplify((1/6)*(-3*x+12+2*y))').toString()).toEqual('(1/6)*(-3*x+12+2*y)'); 284 | expect(nerdamer('simplify((-1/2)x+(1/3)y+2)').toString()).toEqual('(1/6)*(-3*x+12+2*y)'); 285 | expect(nerdamer('simplify(((17/2)*(-5*K+32)^(-1)*K^2+(5/2)*K-125*(-5*K+32)^(-1)*K-16+400*(-5*K+32)^(-1))*(-17*(-5*K+32)^(-1)*K+80*(-5*K+32)^(-1))^(-1))').toString()).toEqual('(-35*K+4*K^2+112)*(-80+17*K)^(-1)'); 286 | expect(nerdamer('simplify(sqrt(baseunit_m^2*sin(alpha)+baseunit_m^2))').toString()).toEqual('baseunit_m*sqrt(1+sin(alpha))'); 287 | expect(nerdamer('simplify(sqrt(2*baseunit_m^2*sin(3*alpha)+(4)*baseunit_m^2*cos(5*alpha)^2+6*baseunit_m^2*sin(7*alpha)^2+8*baseunit_m^2))').toString()).toEqual('baseunit_m*sqrt(2)*sqrt(2*cos(5*alpha)^2+3*sin(7*alpha)^2+4+sin(3*alpha))'); 288 | expect(nerdamer('simplify(sqrt(baseunit_m^2*sin(alpha)+baseunit_m^2*cos(alpha)))').toString()).toEqual('baseunit_m*sqrt(cos(alpha)+sin(alpha))'); 289 | expect(nerdamer('simplify(sqrt(2*baseunit_m^2*sin(alpha)+(4)*baseunit_m^2*cos(alpha)^2))').toString()).toEqual('baseunit_m*sqrt(2)*sqrt(2*cos(alpha)^2+sin(alpha))'); 290 | expect(nerdamer('simplify(sqrt(2*baseunit_m^2*sin(3*alpha)+(4)*baseunit_m^2*cos(5*alpha)^2))').toString()).toEqual('baseunit_m*sqrt(2)*sqrt(2*cos(5*alpha)^2+sin(3*alpha))'); 291 | expect(nerdamer('simplify(sqrt(4+4*x))').toString()).toBe('2*sqrt(1+x)'); 292 | expect(nerdamer('simplify(0.5sqrt(4a+4y))').toString()).toEqual('sqrt(a+y)'); 293 | expect(nerdamer('simplify((1/2)*sqrt(-4*y+36))').toString() ).toEqual('sqrt(-y+9)'); 294 | expect(nerdamer('simplify(sqrt(x)^(-1))').toString()).toEqual('sqrt(x)^(-1)'); 295 | expect(nerdamer("simplify(cos(122925461/78256779))").toString()).toEqual('0'); 296 | expect(nerdamer("simplify(cos(pi/2))").toString()).toEqual('0'); 297 | expect(nerdamer('simplify(sin(2)+1)').toString()).toEqual('180783971/94686123'); 298 | expect(nerdamer("simplify(sqrt((sin(1/6)+1)^2+3*cos(1/6)^2))").toString()).toEqual('sqrt(1+2*sin(1/6)+sin(1/6)^2+3*cos(1/6)^2)'); 299 | // also test the non-string API 300 | expect(nerdamer('(log((s))+log(2)) - (log((s*2)))').simplify().toString()).toEqual('0'); 301 | expect(nerdamer('(log((s))-log(2)) - (log((s/2)))').simplify().toString()).toEqual('0'); 302 | expect(nerdamer('1+sin(x)+1').simplify().toString()).toEqual('2+sin(x)'); 303 | expect(nerdamer("sqrt(55225+64)").simplify().evaluate().text()).toEqual('235.136130783850405119'); 304 | expect(nerdamer("(1/5)*sqrt(55225*sin(0)^2+55225*cos(0)^2+64)").simplify().evaluate().text()).toEqual('47.027226156770083846'); 305 | expect(nerdamer('log(2*((a+b)^7))').simplify().toString()).toEqual('49180508/70952475+7*log(a+b)'); 306 | // should simplify twice in a row and not lose the minus! 307 | expect(nerdamer("-sqrt(h)*sqrt(m)^(-1)").simplify().simplify().text()).toEqual('-sqrt(h)*sqrt(m)^(-1)') 308 | expect(nerdamer("(530678210/1662132951)*s*(-sqrt(h))*sqrt(m)^(-1)").simplify().simplify().text("fractions")).toEqual('(-530678210/1662132951)*s*sqrt(h)*sqrt(m)^(-1)'); 309 | // simplify nested fractions correctly 310 | expect(nerdamer("((1/b)/((a/b)+(1/b)))").simplify().text()).toEqual('(1+a)^(-1)') 311 | expect(nerdamer("((a/b)+(1/b))").simplify().text()).toEqual('(1+a)*b^(-1)') 312 | // simplify must preserve signs of certain terms 313 | expect(nerdamer("(1-y)^2").simplify().text()).toBe('(-y+1)^2'); 314 | expect(nerdamer("48-48*(x/100)").simplify().text()).toBe('0.48*(-x+100)'); 315 | expect(nerdamer("-sqrt(12/5)").simplify().evaluate().text()).toBe("-1.549193338482966855"); 316 | }); 317 | it('simplify should be pure', function() { 318 | const a = nerdamer('100*2^((1/2)*m)'); 319 | a.simplify(); 320 | expect(a.toString()).toEqual('100*2^((1/2)*m)'); 321 | }); 322 | it('should also simplify squareroots', function() { 323 | expect(nerdamer('baseunit_m*sqrt(1/baseunit_m^2)').toString()).toEqual('1'); 324 | expect(nerdamer('sqrt(2*baseunit_m^2+2*baseunit_m^2)').toString()).toEqual('2*baseunit_m'); 325 | expect(nerdamer('sqrt(2*baseunit_m^2*sin(3*alpha))').toString()).toEqual('baseunit_m*sqrt(2)*sqrt(sin(3*alpha))'); 326 | }); 327 | it('should calculate nth roots correctly', function() { 328 | expect(nerdamer('roots((-1)^(1/5))').evaluate().text()).toEqual('[(181485532/308761629)*i+260449120/321932817,(55918065/58795733)*i-39088169/126491972,-1,(-8795017271979437/9247628423009475)*i-24157817/78176338,(-8677593/14763203)*i+102334155/126491972]'); 329 | expect(nerdamer('roots((2)^(1/3))').evaluate().text()).toEqual('[131293077/116968834,-131293077/116968834]'); 330 | }); 331 | it('should complete the square', function() { 332 | expect(nerdamer('sqcomp(a*x^2+b*x-11*c, x)').toString()).toEqual('((1/2)*abs(b)*sqrt(a)^(-1)+sqrt(a)*x)^2+(-1/4)*(abs(b)*sqrt(a)^(-1))^2-11*c'); 333 | expect(nerdamer('sqcomp(9*x^2-18*x+17)').toString()).toEqual('(-3+3*x)^2+8'); 334 | expect(nerdamer('sqcomp(s^2+s+1)').toString()).toEqual('(1/2+s)^2+3/4'); 335 | }); 336 | it('should simplify equations', function() { 337 | expect(nerdamer('cos(x)^2+sin(x)^2+cos(x)-tan(x)-1+sin(x^2)^2+cos(x^2)^2=3+b+4').simplify().toString()).toEqual('-tan(x)+1+cos(x)=7+b'); 338 | }); 339 | 340 | it('should simplify trig identities', function() { 341 | expect(nerdamer('simplify(cos(3/2*pi*x+pi)-sin(3/2*pi*x+3/2*pi))').toString()).toEqual('0') 342 | 343 | expect(nerdamer('simplify(sin(-x)+sin(x))').toString()).toEqual('0') 344 | expect(nerdamer('simplify(cos(-x)-cos(x))').toString()).toEqual('0') 345 | expect(nerdamer('simplify(sin(pi-x)-sin(x))').toString()).toEqual('0') 346 | expect(nerdamer('simplify(cos(pi-x)+cos(x))').toString()).toEqual('0') 347 | expect(nerdamer('simplify(sin(pi/2-x)-cos(x))').toString()).toEqual('0') 348 | expect(nerdamer('simplify(cos(pi/2-x)-sin(x))').toString()).toEqual('0') 349 | 350 | expect(nerdamer('simplify(sin(0)-1/2*sqrt(0))').toString()).toEqual('0') 351 | expect(nerdamer('simplify(sin(pi/6)-1/2*sqrt(1))').toString()).toEqual('0') 352 | expect(nerdamer('simplify(sin(pi/4)-1/2*sqrt(2))').toString()).toEqual('0') 353 | expect(nerdamer('simplify(sin(pi/3)-1/2*sqrt(3))').toString()).toEqual('0') 354 | expect(nerdamer('simplify(sin(pi/2)-1/2*sqrt(4))').toString()).toEqual('0') 355 | 356 | expect(nerdamer('simplify(cos(0)-1/2*sqrt(4))').toString()).toEqual('0') 357 | expect(nerdamer('simplify(cos(pi/6)-1/2*sqrt(3))').toString()).toEqual('0') 358 | expect(nerdamer('simplify(cos(pi/4)-1/2*sqrt(2))').toString()).toEqual('0') 359 | expect(nerdamer('simplify(cos(pi/3)-1/2*sqrt(1))').toString()).toEqual('0') 360 | expect(nerdamer('simplify(cos(pi/2)-1/2*sqrt(0))').toString()).toEqual('0') 361 | 362 | expect(nerdamer('simplify(tan(0)-0)').toString()).toEqual('0') 363 | expect(nerdamer('simplify(tan(pi/6)-1/sqrt(3))').toString()).toEqual('0') 364 | expect(nerdamer('simplify(tan(pi/4)-1)').toString()).toEqual('0') 365 | expect(nerdamer('simplify(tan(pi/3)-sqrt(3))').toString()).toEqual('0') 366 | 367 | expect(nerdamer('simplify(sin(2*x)-2*sin(x)*cos(x))').toString()).toEqual('0') 368 | expect(nerdamer('simplify(cos(2*x)-(2*cos(x)*cos(x)-1))').toString()).toEqual('0') 369 | 370 | expect(nerdamer('simplify(tan(x)-sin(x)/cos(x))').toString()).toEqual('0') 371 | 372 | expect(nerdamer('simplify(sin(x+y)-(sin(x)*cos(y)+cos(x)*sin(y)))').toString()).toEqual('0') 373 | expect(nerdamer('simplify(cos(x+y)-(cos(x)*cos(y)-sin(x)*sin(y)))').toString()).toEqual('0') 374 | }); 375 | 376 | it('regression tests', function() { 377 | // issue #7 - everything becomes 1 378 | expect(nerdamer("log(0.01s)/(-239263565+51955423log(s))-1") 379 | .simplify().text()) 380 | .not.toEqual('1'); 381 | // simplify issue unlogged 12/10/2023 382 | expect(nerdamer("(-1/2)*sqrt(5)+1/2").simplify().text()).toBe('-0.618033988749894960'); 383 | }); 384 | }); 385 | -------------------------------------------------------------------------------- /spec/basic_parser.spec.js: -------------------------------------------------------------------------------- 1 | /* global expect */ 2 | 3 | 'use strict'; 4 | 5 | var utils = require('./support/utils'); 6 | var parse = utils.parse; 7 | var _$ = utils.toFixed; 8 | 9 | describe('Basic operations', function() { 10 | it('should add correctly', function(){expect(parse('1+1')).toEqual(2);}); 11 | it('should subtract correctly', function(){expect(parse('3-1')).toEqual(2);}); 12 | it('should divide correctly', function(){expect(parse('6/3')).toEqual(2);}); 13 | it('should multiply correctly', function(){expect(parse('2*6')).toEqual(12);}); 14 | it('should raise to power correctly', function(){expect(parse('2^3')).toEqual(8);}); 15 | it('should parse scientific notation correctly', function(){expect(parse('1.234e+1')).toEqual(12.34);}); 16 | it('should parse non-normalized scientific notation correctly', function(){expect(parse('12.3e-1')).toEqual(1.23);}); 17 | }); 18 | 19 | describe('Order of precedence', function() { 20 | it('should recognize * > +', function(){expect(parse('3*5+3')).toEqual(18);}); 21 | it('should recognize + = -', function(){expect(parse('4+5-11')).toEqual(-2);}); 22 | it('should recognize ^ = +', function(){expect(parse('2^3-3')).toEqual(5);}); 23 | it('should recognize ^ > *', function(){expect(parse('2^3*3')).toEqual(24);}); 24 | it('should recognize / = *', function(){expect(parse('6/3*5')).toEqual(10);}); 25 | it('should recognize / = +', function(){expect(_$(parse('2/3+2/3'))).toEqual(_$(1.3333333333333333));}); 26 | it('should recognize ^ is right associative', function(){expect(parse('2^3^2')).toEqual(512);}); 27 | }); 28 | 29 | describe('Percentages', function() { 30 | it('should calculate percentages', function(){expect(parse('5%')).toEqual(0.05);}); 31 | it('should add percentages', function(){expect(parse('5%+5%')).toEqual(0.1);}); 32 | it('should multiply percentages', function(){expect(parseFloat(parse('10%*10%').toFixed(2))).toEqual(0.01);}); 33 | it('should multiply percentages by numbers', function(){expect(parse('100*10%')).toEqual(10);}); 34 | }); 35 | 36 | describe('Functions', function() { 37 | it('should calculate functions', function(){expect(_$(parse('sin(1)'))).toEqual(_$(0.8414709848078965));}); 38 | it('should calculate functions', function(){expect(_$(parse('sin(sin(2))'))).toEqual(_$(0.7890723435728884));}); 39 | it('should add with functions', function(){expect(_$(parse('sin(sin(2))+4'))).toEqual(_$(4.789072343572888));}); 40 | it('should handle function with arguments', function(){expect(parse('max(4,6,3)')).toEqual(6);}); 41 | it('should handle function with arguments with operations', function(){expect(parse('max(4,5+7,3)')).toEqual(12);}); 42 | it('should handle function with arguments with operations and subsequent operations', 43 | function(){expect(parse('max(4,5+7,3)+11')).toEqual(23);} 44 | ); 45 | }); 46 | 47 | describe('Modulus', function() { 48 | it('should calculate modulus', function(){expect(parse('10%4')).toEqual(2);}); 49 | it('should add with modulus', function(){expect(parse('10%4+6')).toEqual(8);}); 50 | it('should multiply with modulus', function(){expect(parse('10%4*8')).toEqual(16);}); 51 | it('should add and multiply with modulus', function(){expect(parse('2+10%4*8')).toEqual(18);}); 52 | it('should respect modulus in functions', function(){expect(parse('max(3,2+10%4*8,5)')).toEqual(18);}); 53 | it('should respect modulus with percentages', function(){expect(parse('8000%%8')).toEqual(0);}); 54 | it('should correctly handle modulus left assoc', function(){expect(parse('3*3%9')).toEqual(0);}); 55 | }); 56 | 57 | describe('Brackets', function() { 58 | it('should respect parentheses', function(){expect(parse('4*(2+1)')).toEqual(12);}); 59 | it('should respect parentheses within parentheses', function(){expect(parse('3*(4+(2+1))')).toEqual(21);}); 60 | it('should recognize vectors', function(){expect(parse('[1,2,[3,4]]').toString()).toEqual('[1,2,[3,4]]');}); 61 | it('should handle multiple levels of brackets', function() {expect(parse('((((((((9))))))))+1')).toEqual(10);}); 62 | it('should handle multiple levels of brackets', function() {expect(parse('((((((1+1))+4)))+3)')).toEqual(9);}); 63 | }); 64 | 65 | describe('Prefixes', function() { 66 | it('should correctly parse prefixes', function(){ 67 | expect(parse('-(-3*-(4))')).toEqual(-12); 68 | expect(_$(parse('3^-1^-1'))).toEqual(_$(0.333333333333333)); 69 | expect(parse('-(-1-+1)^2')).toEqual(-4); 70 | expect(parse('-(-1-1+1)')).toEqual(1); 71 | expect(parse('-(1)--(1-1--1)')).toEqual(0); 72 | expect(parse('-(-(1))-(--1)')).toEqual(0); 73 | expect(_$(parse('5^-2^-4'))).toEqual(_$(0.9043038394024115)); 74 | expect(parse('5^---3')).toEqual(0.008); 75 | expect(parse('5^-(++1+--+2)')).toEqual(0.008); 76 | expect(parse('(5^-(++1+--+2))^-2')).toEqual(15625); 77 | expect(parse('(5^-3^2)')).toEqual(5.12e-7); 78 | expect(parse('-(--5*--7)')).toEqual(-35); 79 | }); 80 | }); 81 | 82 | /* 83 | //Future TODO. The skeleton is there but currently too many specs fail 84 | describe('Spaces', function() { 85 | it('should respect spaces when parsing', function(){ 86 | expect(parse('sin 9')).toEqual(0.4121184852417566); 87 | }); 88 | it('should ingore other spaces when parsing', function(){ 89 | expect(parse('sin 9 + 11')).toEqual(11.4121184852417566); 90 | expect(parse('sin 9+ 11')).toEqual(11.4121184852417566); 91 | expect(parse('sin 9+11')).toEqual(11.4121184852417566); 92 | expect(parse('2* sin 9+11')).toEqual(11.824236970483513); 93 | expect(parse('2 * sin 9+11')).toEqual(11.824236970483513); 94 | expect(parse(' 2 * sin 9+11 ')).toEqual(11.824236970483513); 95 | }); 96 | it('should correctly identify spaces with arguments', function(){ 97 | expect(parse(' max( 5 , 2 , 17 ) ')).toEqual(17); 98 | }); 99 | it('should correctly identify matrices with spaces', function(){ 100 | expect(parse('[ 1, [ 3, 5, 7 ] , [1 , [2, [ 1, 2] ]] ]').toString()).toEqual('[1,[3,5,7],[1,[2,[1,2]]]]'); 101 | }); 102 | }); 103 | */ 104 | 105 | // describe('Accessing vectors', function(){ 106 | // it('should correctly access vectors', function(){ 107 | // expect(parse('[1,[3,5,7],[1,[2,[1,2]]]][2]').toString()).toEqual('[1,[2,[1,2]]]'); 108 | // expect(parse('[1,[3,5,7],[1,[2,[1,2]]]][2][1]').toString()).toEqual('[2,[1,2]]'); 109 | // expect(parse('[1,[3,5,7],[1,[2,[1,2]]]][2][1][1]').toString()).toEqual('[1,2]'); 110 | // expect(parse('[1,[3,5,7],[1,[2,[1,2]]]][2][1][1][0]').toString()).toEqual('1'); 111 | // expect(parse('5*[1,[3,5,7],[1,[2,[1,2]]]][2][1][1][0]').toString()).toEqual('5'); 112 | // expect(parse('5*[1,[3,5,7],[1,[2,[1,2]]]][2][1][1][0]+8').toString()).toEqual('13'); 113 | // }); 114 | // it('should access ranges', function(){ 115 | // expect(parse('[1,2,3,4,5][1:4]').toString()).toEqual('[2,3,4]'); 116 | // }); 117 | // it('should access using negative indices', function(){ 118 | // expect(parse('[1,2,3,4,5][-2]').toString()).toEqual('4'); 119 | // }); 120 | // it('should not confuse vector wit accessor', function(){ 121 | // expect(parse('[[1,2],[3,4],[5,6]]').toString()).toEqual('[[1,2],[3,4],[5,6]]'); 122 | // }); 123 | // }); 124 | 125 | // describe('Setting vector values', function() { 126 | // it('should set values of vectors with the assign operator', function() { 127 | // expect(parse('[1,2][1]:x').toString()).toEqual('[1,x]'); 128 | // }); 129 | // }); 130 | 131 | describe('Substitutions', function(){ 132 | it('should substitute x', function() {expect(parse('x+1', {x: 4})).toEqual(5);}); 133 | it('should substitute x', function() {expect(parse('2*x+1', {x: 4})).toEqual(9);}); 134 | }); 135 | -------------------------------------------------------------------------------- /spec/build.spec.js: -------------------------------------------------------------------------------- 1 | /* global expect */ 2 | 3 | 'use strict'; 4 | 5 | var nerdamer = require('../nerdamer.core.js'); 6 | 7 | describe('build', function () { 8 | 9 | var values = [ 10 | 2.1, 3.3, 1 11 | ]; 12 | 13 | it('should honor argument order', function () { 14 | // given 15 | var testCases = [ 16 | { 17 | given: '-x^2+1', 18 | params: ['x'], 19 | one_expected: -3.41, 20 | two_expected: -3.41 21 | }, 22 | { 23 | given: '-x^2+y', 24 | params: ['y', 'x'], 25 | one_expected: -1.1100000000000003, 26 | two_expected: -8.79 27 | }, 28 | { 29 | given: '5*(cos(x^2)+y)', 30 | params: ['y', 'x'], 31 | one_expected: 15.010991793381834, 32 | two_expected: 9.973108618869924 33 | }, 34 | { 35 | given: 'sec(z)+5*(cos(x^2)+y)', 36 | params: ['z', 'y', 'x'], 37 | one_expected: 16.86180751106276, 38 | two_expected: 17.220709873373476 39 | }, 40 | { 41 | given: '(sec(z)+5*(cos(x^2)+y))^2-x', 42 | params: ['z', 'y', 'x'], 43 | one_expected: 282.22055254013253, 44 | two_expected: 295.5528485429027 45 | }, 46 | { 47 | given: '(x+1)+(y+2)+(z+3)', 48 | params: ['z', 'y', 'x'], 49 | one_expected: 12.399999999999999, 50 | two_expected: 12.4 51 | }, 52 | { 53 | given: '(((x+1)+(y+2))^2+(z+3))^x', 54 | params: ['z', 'y', 'x'], 55 | one_expected: 8555.83476461652, 56 | two_expected: 58.39 57 | }, 58 | { 59 | given: 'sqrt(x)*sqrt(5)', 60 | params: ['x'], 61 | one_expected: 3.2403703492039306, 62 | two_expected: 3.2403703492039306 63 | }, 64 | { 65 | given: 'abs(-x)-x', 66 | params: ['x'], 67 | one_expected: 0, 68 | two_expected: 0 69 | }, 70 | { 71 | given: 'sec(x)+tan(z)^2+ceil(y)', 72 | params: ['z', 'x', 'y'], 73 | one_expected: 4.444717164847537, 74 | two_expected: 2.910896226522566 75 | }, 76 | { 77 | given: 'mod(y,2)', 78 | params: ['y'], 79 | one_expected: 0.10000000000000009, 80 | two_expected: 0.10000000000000009 81 | }, 82 | { 83 | given: 'fact(6)*min(x, y)*z', 84 | params: ['x','z','y'], 85 | one_expected: 1512, 86 | two_expected: 2376 87 | }, 88 | { 89 | given: '((x+y)^2+(y+2)+(z+3))^(x+y)', 90 | params: ['x', 'z', 'y'], 91 | one_expected: 362284798.5739877, 92 | two_expected: 9072.850179772464 93 | }, 94 | { 95 | given: 'asin(0.2)+atan(0.1)+acosh(y)', 96 | params: ['y'], 97 | one_expected: 1.6738857175240727, 98 | two_expected: 1.6738857175240727 99 | }, 100 | { 101 | given: '(sin(z)*cos(y))^(tan(2)+1)', 102 | params: ['y', 'z'], 103 | one_expected: 20.054910364061293, 104 | two_expected: 20.054910364061293 105 | }, 106 | { 107 | given: 'cos(x+tan(x+sin(x)))^2', 108 | params: ['x'], 109 | one_expected: 0.1168736998021759, 110 | two_expected: 0.1168736998021759 111 | }, 112 | { 113 | given: '4*(s^2+t^2)*s', 114 | params: ['s', 't'], 115 | one_expected: 128.52, 116 | two_expected: 128.52 117 | } 118 | ]; 119 | 120 | for (var i = 0; i < testCases.length; ++i) { 121 | // when 122 | var parsed = nerdamer(testCases[i].given); 123 | var f1 = parsed.buildFunction(); 124 | var f2 = parsed.buildFunction(testCases[i].params); 125 | 126 | // then 127 | expect(f1.apply(null, values)).toEqual(testCases[i].one_expected); 128 | expect(f2.apply(null, values)).toEqual(testCases[i].two_expected); 129 | } 130 | }); 131 | }); -------------------------------------------------------------------------------- /spec/calculus.spec.js: -------------------------------------------------------------------------------- 1 | /* global expect */ 2 | 3 | 'use strict'; 4 | 5 | var nerdamer = require('../nerdamer.core.js'); 6 | var round = nerdamer.getCore().Utils.round; 7 | 8 | describe('Calculus', function () { 9 | 10 | it('should differentiate correctly', function () { 11 | expect(nerdamer('diff(cos(x),x)').toString()).toEqual('-sin(x)'); 12 | expect(nerdamer('diff(log(x),x)').toString()).toEqual('x^(-1)'); 13 | expect(nerdamer('diff(tan(x),x)').toString()).toEqual('sec(x)^2'); 14 | expect(nerdamer('diff(4*tan(x)*sec(x),x)').toString()).toEqual('4*(sec(x)*tan(x)^2+sec(x)^3)'); 15 | expect(nerdamer('diff(sqrt(7),x)').toString()).toEqual('0'); 16 | expect(nerdamer('diff(4,x)').toString()).toEqual('0'); 17 | expect(nerdamer('diff(x^2,x)').toString()).toEqual('2*x'); 18 | expect(nerdamer('diff(2*x^2+4,x)').toString()).toEqual('4*x'); 19 | expect(nerdamer('diff(sqrt(x)*x,x)').toString()).toEqual('(3/2)*x^(1/2)'); 20 | expect(nerdamer('diff(sqrt(x)-1/sqrt(x),x)').toString()).toEqual('(1/2)*x^(-1/2)+(1/2)*x^(-3/2)'); 21 | expect(nerdamer('diff(x^2/3-3/x^2,x)').toString()).toEqual('(2/3)*x+6*x^(-3)'); 22 | expect(nerdamer('diff(sqrt(x)*(x^2+1),x)').toString()).toEqual('(1/2)*(1+x^2)*x^(-1/2)+2*x^(3/2)'); 23 | expect(nerdamer('diff(e^x/(e^x-1),x)').toString()).toEqual('(-1+e^x)^(-1)*e^x-(-1+e^x)^(-2)*e^(2*x)'); 24 | expect(nerdamer('diff(e^x,x)').toString()).toEqual('e^x'); 25 | expect(nerdamer('diff(e^x/x,x)').toString()).toEqual('-e^x*x^(-2)+e^x*x^(-1)'); 26 | expect(nerdamer('diff(tan(x)*log(1/cos(x)),x)').toString()).toEqual('-(-cos(x)^(-1)*sin(x)*tan(x)+log(cos(x))*sec(x)^2)'); 27 | expect(nerdamer('diff((2*x)^(e),x)').toString()).toEqual('2^e*e*x^(-1+e)'); 28 | expect(nerdamer('diff(2*cos(x)*log(x),x)').toString()).toEqual('2*(-log(x)*sin(x)+cos(x)*x^(-1))'); 29 | expect(nerdamer('diff(cos(5*x)*log(sec(sqrt(cos(x^(4/5))^2))/y^2)*y,x)').toString()).toEqual('(-4/5)*abs(cos(x^(4/5)))^(-1)*cos(x^(4/5))*sec(abs(cos(x^(4/5))))*sin(x^(4/5))*tan(abs(cos(x^(4/5))))*x^(-1/5)*y^(-2)*cos(5*x)*sec(abs(cos(x^(4/5))))^(-1)*y^3-5*log(sec(abs(cos(x^(4/5))))*y^(-2))*sin(5*x)*y'); 30 | expect(nerdamer('diff(x*cos(x)^log(x),x)').toString()).toEqual('(-cos(x)^(-1)*log(x)*sin(x)+log(cos(x))*x^(-1))*cos(x)^log(x)*x+cos(x)^log(x)'); 31 | expect(nerdamer('diff(cos(2*x),x)').toString()).toEqual('-2*sin(2*x)'); 32 | expect(nerdamer('diff(cos(x)*tan(x),x)').toString()).toEqual('-sin(x)*tan(x)+cos(x)*sec(x)^2'); 33 | expect(nerdamer('diff(sec(sqrt(cos(x^(4/5))^2)),x)').toString()).toEqual('(-4/5)*abs(cos(x^(4/5)))^(-1)*cos(x^(4/5))*sec(abs(cos(x^(4/5))))*sin(x^(4/5))*tan(abs(cos(x^(4/5))))*x^(-1/5)'); 34 | expect(nerdamer('diff(log(log(log(cos(t*t)^z))),t)').toString()).toEqual('-2*cos(t^2)^(-1)*sin(t^2)*t*z*log(cos(t^2))^(-1)*log(log(cos(t^2))*z)^(-1)*z^(-1)'); 35 | expect(nerdamer('diff(6*log(x)^(3*log(x^2)),x)').toString()).toEqual('36*(log(log(x))*x^(-1)+x^(-1))*log(x)^(6*log(x))'); 36 | expect(nerdamer('diff(sinh(x^2)^cos(x),x)').toString()).toEqual('(-log(sinh(x^2))*sin(x)+2*cos(x)*cosh(x^2)*sinh(x^2)^(-1)*x)*sinh(x^2)^cos(x)'); 37 | expect(nerdamer('diff(tan(x)*tanh(x),x)').toString()).toEqual('sec(x)^2*tanh(x)+sech(x)^2*tan(x)'); 38 | expect(nerdamer('diff(4*x*tan(x)*7*tanh(x),x)').toString()).toEqual('28*(sec(x)^2*tanh(x)*x+sech(x)^2*tan(x)*x+tan(x)*tanh(x))'); 39 | expect(nerdamer('diff(y*tan(y)*7*tanh(y),x)').toString()).toEqual('0'); 40 | expect(nerdamer('diff(yx*tan(y)*7*tanh(y),x)').toString()).toEqual('0'); 41 | expect(nerdamer('diff(y,x)').toString()).toEqual('0'); 42 | expect(nerdamer('diff(x*y,x)').toString()).toEqual('y'); 43 | expect(nerdamer('diff([sin(x), x^2, x],x)').toString()).toEqual('[cos(x),2*x,1]'); 44 | expect(nerdamer('diff(sinc(a*x^3+b),x)').toString()).toEqual('3*((a*x^3+b)*cos(a*x^3+b)-sin(a*x^3+b))*(a*x^3+b)^(-2)*a*x^2'); 45 | expect(nerdamer('diff(sqrt(e^x + a),x)').toString()).toEqual('(1/2)*(a+e^x)^(-1/2)*e^x'); 46 | 47 | // issue # 48 | expect(nerdamer('diff(3asin(x),x)').toString()).toEqual('3*sqrt(-x^2+1)^(-1)'); 49 | expect(nerdamer('diff(5acos(x),x)').toString()).toEqual('-5*sqrt(-x^2+1)^(-1)'); 50 | expect(nerdamer('diff(7atan(x),x)').toString()).toEqual('7*(1+x^2)^(-1)'); 51 | 52 | // equations 53 | expect(nerdamer('diff(3asin(x)=x,x)').toString()).toEqual('3*sqrt(-x^2+1)^(-1)=1'); 54 | }); 55 | 56 | /* 57 | todo: cover all function differentiation, with scalars: 58 | case LOG: 59 | case COS: 60 | case SIN: 61 | case TAN: 62 | case SEC: 63 | case CSC: 64 | case COT: 65 | case ASIN: 66 | case ACOS: 67 | case ATAN: 68 | case ABS: 69 | case 'cosh': 70 | case 'sinh': 71 | case TANH: 72 | case SECH: 73 | case CSCH: 74 | case COTH: 75 | case 'asinh': 76 | case 'acosh': 77 | case 'atanh': 78 | case ASECH: 79 | case ACOTH: 80 | case ACSCH: 81 | case ASEC: 82 | case ACSC: 83 | case ACOT: 84 | case 'S': 85 | case 'C': 86 | case 'Si': 87 | case 'Shi': 88 | case 'Ci': 89 | case 'Chi': 90 | case 'Ei': 91 | case 'Li': 92 | case 'erf': 93 | case 'atan2': 94 | case 'sign': 95 | case 'sinc': 96 | case Settings.LOG10: 97 | */ 98 | 99 | it('should calculate sums correctly', function () { 100 | expect(nerdamer('sum(x+y, x, 0, 3)').evaluate().toString()).toEqual('4*y+6'); 101 | expect(nerdamer('sum(x^2+x, x, 0, 10)').evaluate().toString()).toEqual('440'); 102 | expect(nerdamer('sum(x^2*z^2+x*y-z+1, x, 0, 10)').evaluate().toString()).toEqual('-11*z+385*z^2+11+55*y'); 103 | expect(nerdamer('sum(x^2*z^2+x*y-z+1, z, 0, 10)').evaluate().toString()).toEqual('-44+11*x*y+385*x^2'); 104 | expect(nerdamer('sum(sqrt(x)*sin(x), x, 0, 10)').evaluate().toString()).toEqual('775334583/372372283'); 105 | expect(nerdamer('sum(e^(-x^2*π/9),x,1,100)').evaluate().toString()).toEqual('633863423979/633863423978'); 106 | }); 107 | 108 | it('should calculate the definite integral correctly', function () { 109 | expect(round(nerdamer('defint(cos(x),1,2,x)').evaluate(), 14)).toEqual(round(0.067826442018, 14)); 110 | expect(round(nerdamer('defint(cos(x)^3*x^2-1,-1,9)').evaluate(), 14)).toEqual(round(8.543016466395, 14)); 111 | expect(round(nerdamer('defint(cos(x^x),1,2,x)').evaluate(), 14)).toEqual(round(-0.27113666621, 14)); 112 | expect(round(nerdamer('defint(cos(x^log(sin(x))),2,3,x)').evaluate(), 14)).toEqual(round(0.805604089074, 14)); 113 | expect(round(nerdamer('defint(log(2*cos(x/2)),-π,π,x)').evaluate(), 14)).toEqual(round(-0, 14)); 114 | expect(round(nerdamer('defint(log(cos(x/2)),-π,π,x)').evaluate(), 14)).toEqual(round(-4.355172180607, 14)); 115 | expect(round(nerdamer('defint(log(x+1), -1, 1, x)').evaluate(), 14)).toEqual(round(-0.6137056388801095, 14)); 116 | expect(round(nerdamer('defint(log(x), 0, 1, x)').evaluate(), 14)).toEqual(round(-1, 14)); 117 | expect(round(nerdamer('defint((x^2-3)/(-x^3+9x+1), 1, 3, x)').evaluate(), 14)).toEqual(round(0.732408192445406585, 14)); 118 | expect(round(nerdamer('defint(x*(x-5)^(1/2),5,8)').evaluate(), 14)).toEqual(round(23.555890982936999348, 14)); 119 | expect(round(nerdamer('defint(sqrt(4(x^2)+4), 0, 3)').evaluate(), 14)).toEqual(round(11.305279439735999908, 14)); 120 | }); 121 | 122 | it('should calculate limits correctly', function () { 123 | expect(nerdamer('limit((2-2*x^2)/(x-1), x, 1)').toString()).toEqual('-4'); 124 | expect(nerdamer('limit(1/2*(x^2 - 1)/(x^2 + 1), x, 3)').toString()).toEqual('2/5'); 125 | expect(nerdamer('limit(tan(3*x)/tan(x), x, pi/2)').toString()).toEqual('1/3'); 126 | expect(nerdamer('limit(x/(3*abs(4*x)),x, 0)').toString()).toEqual('[-1/12,1/12]'); 127 | expect(nerdamer('limit((4x^2-x)/(3x^2+x),x,∞)').toString()).toEqual('4/3'); 128 | expect(nerdamer('limit((x^(1/2)+x^(-1/2))/(x^(1/2)-x^(-1/2)),x,Infinity)').toString()).toEqual('1'); 129 | expect(nerdamer('limit((2*x+log(x))/(x*log(x)),x,Infinity)').toString()).toEqual('0'); 130 | expect(nerdamer('limit(e^(-x)+2,x,Infinity)').toString()).toEqual('2'); 131 | expect(nerdamer('limit((x+1)^(1+1/x)-x^(1+x),x, Infinity)').toString()).toEqual('-Infinity'); 132 | expect(nerdamer('limit(x/(x+1)^2, x, -1)').toString()).toEqual('-Infinity'); 133 | expect(nerdamer('limit(log(x),x, 0)').toString()).toEqual('-Infinity'); 134 | expect(nerdamer('limit((3*sin(x)-sin(2*x))/(x-sin(x)),x,0)').toString()).toEqual('Infinity'); 135 | // issue #12 136 | expect(nerdamer('limit((2sin(x)-sin(2x))/(x-sin(x)),x,0)').toString()).toEqual('6'); 137 | }); 138 | 139 | it('should integrate properly', function () { 140 | expect(nerdamer('integrate(sin(x), x)').toString()).toEqual('-cos(x)'); 141 | expect(nerdamer('integrate((22/7)^x,x)').toString()).toEqual('(log(1/7)+log(22))^(-1)*22^x*7^(-x)'); 142 | expect(nerdamer('integrate(cos(x), x)').toString()).toEqual('sin(x)'); 143 | expect(nerdamer('integrate(2*x^2+x, x)').toString()).toEqual('(1/2)*x^2+(2/3)*x^3'); 144 | expect(nerdamer('integrate(log(x), x)').toString()).toEqual('-x+log(x)*x'); 145 | expect(nerdamer('integrate(sqrt(x), x)').toString()).toEqual('(2/3)*x^(3/2)'); 146 | expect(nerdamer('integrate(asin(a*x), x)').toString()).toEqual('a^(-1)*sqrt(-a^2*x^2+1)+asin(a*x)*x'); 147 | expect(nerdamer('integrate(a/x, x)').toString()).toEqual('a*log(x)'); 148 | expect(nerdamer('integrate(x*e^x, x)').toString()).toEqual('-e^x+e^x*x'); 149 | expect(nerdamer('integrate(x^3*log(x), x)').toString()).toEqual('(-1/16)*x^4+(1/4)*log(x)*x^4'); 150 | expect(nerdamer('integrate(x^2*sin(x), x)').toString()).toEqual('-cos(x)*x^2+2*cos(x)+2*sin(x)*x'); 151 | expect(nerdamer('integrate(sin(x)*log(cos(x)), x)').toString()).toEqual('-cos(x)*log(cos(x))+cos(x)'); 152 | expect(nerdamer('integrate(x*asin(x), x)').toString()).toEqual('(-1/4)*asin(x)+(1/2)*asin(x)*x^2+(1/4)*cos(asin(x))*sin(asin(x))'); 153 | expect(nerdamer('integrate(q/((2-3*x^2)^(1/2)), x)').toString()).toEqual('asin(3*sqrt(6)^(-1)*x)*q*sqrt(3)^(-1)'); 154 | expect(nerdamer('integrate(1/(a^2+x^2), x)').toString()).toEqual('a^(-1)*atan(a^(-1)*x)'); 155 | expect(nerdamer('integrate(11/(a+5*r*x)^2,x)').toString()).toEqual('(-11/5)*(5*r*x+a)^(-1)*r^(-1)'); 156 | expect(nerdamer('integrate(cos(x)*sin(x), x)').toString()).toEqual('(-1/2)*cos(x)^2'); 157 | expect(nerdamer('integrate(x*cos(x)*sin(x), x)').toString()).toEqual('(-1/2)*cos(x)^2*x+(1/4)*cos(x)*sin(x)+(1/4)*x'); 158 | expect(nerdamer('integrate(t/(a*x+b), x)').toString()).toEqual('a^(-1)*log(a*x+b)*t'); 159 | expect(nerdamer('integrate(x*(x+a)^3, x)').toString()).toEqual('(1/2)*a^3*x^2+(1/5)*x^5+(3/4)*a*x^4+a^2*x^3'); 160 | expect(nerdamer('integrate(4*x/(x^2+a^2), x)').toString()).toEqual('2*log(a^2+x^2)'); 161 | expect(nerdamer('integrate(1/(x^2+3*a^2), x)').toString()).toEqual('a^(-1)*atan(a^(-1)*sqrt(3)^(-1)*x)*sqrt(3)^(-1)'); 162 | expect(nerdamer('integrate(8*x^3/(6*x^2+3*a^2), x)').toString()).toEqual('8*((-1/24)*a^2*log(2*x^2+a^2)+(1/12)*x^2)'); 163 | expect(nerdamer('integrate(10*q/(4*x^2+24*x+20), x)').toString()).toEqual('10*((-1/16)*log(5+x)+(1/16)*log(1+x))*q'); 164 | expect(nerdamer('integrate(x/(x+a)^2, x)').toString()).toEqual('(a+x)^(-1)*a+log(a+x)'); 165 | expect(nerdamer('integrate(sqrt(x-a), x)').toString()).toEqual('(2/3)*(-a+x)^(3/2)'); 166 | expect(nerdamer('integrate(x^n*log(x), x)').toString()).toEqual('(1+n)^(-1)*log(x)*x^(1+n)-(1+n)^(-2)*x^(1+n)'); 167 | expect(nerdamer('integrate(3*a*sec(x)^2, x)').toString()).toEqual('3*a*tan(x)'); 168 | expect(nerdamer('integrate(a/(x^2+b*x+a*x+a*b),x)').toString()).toEqual('(((-a^(-1)*b+1)^(-1)*a^(-1)*b+1)*a^(-1)*log(b+x)-(-a^(-1)*b+1)^(-1)*a^(-1)*log(a+x))*a'); 169 | expect(nerdamer('integrate(log(a*x+b),x)').toString()).toEqual('((a*x+b)*log(a*x+b)-a*x-b)*a'); 170 | expect(nerdamer('integrate(x*log(x),x)').toString()).toEqual('(-1/4)*x^2+(1/2)*log(x)*x^2'); 171 | expect(nerdamer('integrate(log(a*x)/x,x)').toString()).toEqual('(1/2)*log(a*x)^2'); 172 | expect(nerdamer('integrate(log(x)^2,x)').toString()).toEqual('-2*log(x)*x+2*x+log(x)^2*x'); 173 | expect(nerdamer('integrate(t*log(x)^3,x)').toString()).toEqual('(-3*log(x)^2*x-6*x+6*log(x)*x+log(x)^3*x)*t'); 174 | expect(nerdamer('integrate(e^x*sin(x),x)').toString()).toEqual('(1/2)*(-cos(x)*e^x+e^x*sin(x))'); 175 | expect(nerdamer('integrate(e^x*sin(x),x)').toString()).toEqual('(1/2)*(-cos(x)*e^x+e^x*sin(x))'); 176 | expect(nerdamer('integrate(e^(2*x)*sin(x),x)').toString()).toEqual('(4/5)*((-1/4)*cos(x)*e^(2*x)+(1/2)*e^(2*x)*sin(x))'); 177 | expect(nerdamer('integrate(e^(2*x)*sin(x)*x,x)').toString()).toEqual('(-3/25)*e^(2*x)*sin(x)+(4/25)*cos(x)*e^(2*x)+(4/5)*((-1/4)*cos(x)*e^(2*x)+(1/2)*e^(2*x)*sin(x))*x'); 178 | expect(nerdamer('integrate(x*log(x)^2,x)').toString()).toEqual('(-1/2)*log(x)*x^2+(1/2)*log(x)^2*x^2+(1/4)*x^2'); 179 | expect(nerdamer('integrate(x^2*log(x)^2,x)').toString()).toEqual('(-2/9)*log(x)*x^3+(1/3)*log(x)^2*x^3+(2/27)*x^3'); 180 | expect(nerdamer('integrate(x^2*e^(a*x),x)').toString()).toEqual('-2*(-a^(-2)*e^(a*x)+a^(-1)*e^(a*x)*x)*a^(-1)+a^(-1)*e^(a*x)*x^2'); 181 | expect(nerdamer('integrate(8*e^(a*x^2),x)').toString()).toEqual('4*erf(sqrt(-a)*x)*sqrt(-a)^(-1)*sqrt(pi)'); 182 | expect(nerdamer('integrate(5*x*e^(-8*a*x^2),x)').toString()).toEqual('(-5/16)*a^(-1)*e^(-8*a*x^2)'); 183 | expect(nerdamer('integrate(x^2*sin(x),x)').toString()).toEqual('-cos(x)*x^2+2*cos(x)+2*sin(x)*x'); 184 | expect(nerdamer('integrate(8*tan(b*x)^2,x)').toString()).toEqual('8*(-x+b^(-1)*tan(b*x))'); 185 | expect(nerdamer('integrate(sec(a*x)^3,x)').toString()).toEqual('(1/2)*a^(-1)*log(sec(a*x)+tan(a*x))+(1/2)*a^(-1)*sec(a*x)*tan(a*x)'); 186 | expect(nerdamer('integrate(sec(a*x)*tan(a*x),x)').toString()).toEqual('a^(-1)*sec(a*x)'); 187 | expect(nerdamer('integrate(3*a*cot(a*x)^4, x)').toString()).toEqual('3*((-1/3)*a^(-1)*cot(a*x)^3+a^(-1)*cot(a*x)+x)*a'); 188 | expect(nerdamer('integrate(3*a*csc(a*x)^4, x)').toString()).toEqual('3*((-1/3)*a^(-1)*cot(a*x)*csc(a*x)^2+(-2/3)*a^(-1)*cot(a*x))*a'); 189 | expect(nerdamer('integrate(1/8*a*2/(x^3+13*x^2+47*x+35),x)').toString()).toEqual('(1/4)*((-1/8)*log(5+x)+(1/12)*log(7+x)+(1/24)*log(1+x))*a'); 190 | expect(nerdamer('integrate(a*2/(x^2+x),x)').toString()).toEqual('2*(-log(1+x)+log(x))*a'); 191 | expect(nerdamer('integrate((x+7)/(x+1)^3,x)').toString()).toEqual('(-1/2)*(1+x)^(-1)+(-7/2)*(1+x)^(-2)+(-1/2)*(1+x)^(-2)*x'); 192 | expect(nerdamer('integrate((3*x+2)/(x^2+x),x)').toString()).toEqual('2*log(x)+log(1+x)'); 193 | expect(nerdamer('integrate([sin(x), x^2, x],x)').toString()).toEqual('[-cos(x),(1/3)*x^3,(1/2)*x^2]'); 194 | expect(nerdamer('integrate(sinh(x),x)').toString()).toEqual('cosh(x)'); 195 | expect(nerdamer('integrate(cosh(x),x)').toString()).toEqual('sinh(x)'); 196 | expect(nerdamer('integrate(tanh(x),x)').toString()).toEqual('log(cosh(x))'); 197 | expect(nerdamer('integrate(sinh(x)*x,x)').toString()).toEqual('-sinh(x)+cosh(x)*x'); 198 | expect(nerdamer('integrate((x^6+x^2-7)/(x^2+11), x)').toString()).toEqual('(-11/3)*x^3+(1/5)*x^5+122*x-1349*atan(sqrt(11)^(-1)*x)*sqrt(11)^(-1)'); 199 | expect(nerdamer('integrate(x^6/(x^2+11), x)').toString()).toEqual('(-11/3)*x^3+(1/5)*x^5+121*x-1331*atan(sqrt(11)^(-1)*x)*sqrt(11)^(-1)'); 200 | expect(nerdamer('integrate(x^2/(x^2+11))').toString()).toEqual('-11*atan(sqrt(11)^(-1)*x)*sqrt(11)^(-1)+x'); 201 | expect(nerdamer('integrate(tan(x)*csc(x), x)').toString()).toEqual('log(sec(x)+tan(x))'); 202 | expect(nerdamer('integrate(sinh(x)*e^x, x)').toString()).toEqual('(-1/2)*x+(1/4)*e^(2*x)'); 203 | expect(nerdamer('integrate(sinh(x)*cos(x), x)').toString()).toEqual('(-1/4)*e^(-x)*sin(x)+(1/4)*cos(x)*e^(-x)+(1/4)*cos(x)*e^x+(1/4)*e^x*sin(x)'); 204 | expect(nerdamer('integrate(cos(x^2), x)').toString()).toEqual('integrate(cos(x^2),x)'); 205 | expect(nerdamer('integrate(sqrt(a-x^2)*x^2, x)').toString()).toEqual('((-1/16)*cos(2*asin(sqrt(a)^(-1)*x))*sin(2*asin(sqrt(a)^(-1)*x))+(1/8)*asin(sqrt(a)^(-1)*x))*a^2'); 206 | expect(nerdamer('integrate((1-x^2)^(3/2), x)').toString()).toEqual('(-3/16)*cos(2*asin(x))*sin(2*asin(x))+(-x^2+1)^(3/2)*x+(3/8)*asin(x)'); 207 | expect(nerdamer('integrate((1-x^2)^(3/2)*x^2, x)').toString()).toEqual('(-1/32)*cos(2*asin(x))*sin(2*asin(x))+(-1/48)*cos(2*asin(x))^2*sin(2*asin(x))+(1/16)*asin(x)+(1/48)*sin(2*asin(x))'); 208 | expect(nerdamer('integrate(cos(x)^2*sin(x)^4, x)').toString()).toEqual('(-1/32)*cos(2*x)*sin(2*x)+(-1/48)*sin(2*x)+(1/16)*x+(1/48)*cos(2*x)^2*sin(2*x)'); 209 | expect(nerdamer('integrate(log(a*x+1)/x^2, x)').toString()).toEqual('(-log(1+a*x)+log(x))*a-log(1+a*x)*x^(-1)'); 210 | expect(nerdamer('integrate(x^2*(1-x^2)^(5/2), x)').toString()).toEqual('(-1/128)*cos(2*asin(x))^3*sin(2*asin(x))+(-1/48)*cos(2*asin(x))^2*sin(2*asin(x))+(-3/256)*cos(2*asin(x))*sin(2*asin(x))+(1/48)*sin(2*asin(x))+(5/128)*asin(x)'); 211 | expect(nerdamer('integrate(1/tan(a*x)^n, x)').toString()).toEqual('integrate(tan(a*x)^(-n),x)'); 212 | expect(nerdamer('integrate(sin(x)^2*cos(x)*tan(x), x)').toString()).toEqual('(-3/4)*cos(x)+(1/12)*cos(3*x)'); 213 | expect(nerdamer('integrate(cos(x)^2/sin(x),x)').toString()).toEqual('-log(cot(x)+csc(x))+cos(x)'); 214 | expect(nerdamer('integrate(cos(x)/x,x)').toString()).toEqual('Ci(x)'); 215 | expect(nerdamer('integrate(sin(x)/x,x)').toString()).toEqual('Si(x)'); 216 | expect(nerdamer('integrate(log(x)^3/x,x)').toString()).toEqual('(1/4)*log(x)^4'); 217 | expect(nerdamer('integrate(tan(x)^2*sec(x), x)').toString()).toEqual('(-1/2)*log(sec(x)+tan(x))+(1/2)*sec(x)*tan(x)'); 218 | expect(nerdamer('integrate(tan(x)/cos(x),x)').toString()).toEqual('cos(x)^(-1)'); 219 | expect(nerdamer('integrate(sin(x)^3/x,x)').toString()).toEqual('(-1/4)*Si(3*x)+(3/4)*Si(x)'); 220 | expect(nerdamer('integrate(tan(x)/sec(x)*sin(x)/tan(x),x)').toString()).toEqual('(-1/2)*cos(x)^2'); 221 | expect(nerdamer('integrate(log(x)^n/x,x)').toString()).toEqual('(1+n)^(-1)*log(x)^(1+n)'); 222 | expect(nerdamer('integrate(1/(x^2+9)^3,x)').toString()).toEqual('(1/729)*((1/4)*cos(atan((1/3)*x))^3*sin(atan((1/3)*x))+(3/8)*atan((1/3)*x)+(3/8)*cos(atan((1/3)*x))*sin(atan((1/3)*x)))'); 223 | expect(nerdamer('integrate(asin(x)/sqrt(2-2x^2),x)').toString()).toEqual('(1/2)*asin(x)^2*sqrt(2)^(-1)'); 224 | expect(nerdamer('integrate(atan(x)/(2+2*x^2),x)').toString()).toEqual('(1/4)*atan(x)^2'); 225 | expect(nerdamer('integrate(1/(sqrt(1-1/x^2)*x^2), x)').toString()).toEqual('asin(sqrt(-x^(-2)+1))'); 226 | expect(nerdamer('integrate(1/(sqrt(1-1/x^2)*x), x)').toString()).toEqual('(-1/2)*log(1+sqrt(-x^(-2)+1))+(1/2)*log(-1+sqrt(-x^(-2)+1))'); 227 | expect(nerdamer('integrate(exp(2*log(x)),x)').toString()).toEqual('(1/3)*x^3'); 228 | }); 229 | }); 230 | -------------------------------------------------------------------------------- /spec/extra.spec.js: -------------------------------------------------------------------------------- 1 | /* global expect */ 2 | 3 | 'use strict'; 4 | 5 | var nerdamer = require('../nerdamer.core.js'); 6 | require('../Extra'); 7 | 8 | describe('Extra Calculus', function () { 9 | 10 | it('should transform Laplace correctly', function () { 11 | expect(nerdamer('laplace(5, t, s)').toString()).toEqual('5*s^(-1)'); 12 | expect(nerdamer('laplace(a*t, t, s)').toString()).toEqual('a*s^(-2)'); 13 | expect(nerdamer('laplace(cos(a*t), t, s)').toString()).toEqual('(a^2+s^2)^(-1)*s'); 14 | expect(nerdamer('laplace(cos(x), t, s)').toString()).toEqual('cos(x)*s^(-1)'); 15 | expect(nerdamer('laplace(sinh(a*t), t, s)').toString()).toEqual('(-a^2+s^2)^(-1)*a'); 16 | expect(nerdamer('laplace(a*t^2, t, s)').toString()).toEqual('2*a*s^(-3)'); 17 | expect(nerdamer('laplace(2*sqrt(t), t, s)').toString()).toEqual('s^(-3/2)*sqrt(pi)'); 18 | expect(nerdamer('laplace(x*e^(a*t), t, s)').toString()).toEqual('(-a+s)^(-1)*x'); 19 | expect(nerdamer('laplace(x*(sin(a*t)-a*t*cos(a*t)), t, s)').toString()).toEqual('((a^2+s^2)^(-1)*a-((1+a^2*s^(-2))^(-2)*s^(-2)-(1+a^2*s^(-2))^(-2)*a^2*s^(-4))*a)*x'); 20 | expect(nerdamer('laplace(sin(a*t), t, s)').toString()).toEqual('(a^2+s^2)^(-1)*a'); 21 | expect(nerdamer('laplace(t*sin(a*t), t, s)').toString()).toEqual('2*(1+a^2*s^(-2))^(-2)*a*s^(-3)'); 22 | expect(nerdamer('laplace(sin(a*t+b), t, s)').toString()).toEqual('(1+a^2*s^(-2))^(-1)*a*cos(b)*s^(-2)+(1+a^2*s^(-2))^(-1)*s^(-1)*sin(b)'); 23 | expect(nerdamer('laplace(t^2*e^(a*t), t, s)').toString()).toEqual('-2*(-s+a)^(-3)'); 24 | expect(nerdamer('laplace(6*t*e^(-9*t)*sin(6*t), t, s)').toString()).toEqual('-72*(-9-s)^(-3)*(1+36*(-9-s)^(-2))^(-2)'); 25 | expect(nerdamer('laplace(sinh(t)*e^t, t, s)').toString()).toEqual('(-1/2)*(-s+2)^(-1)+(-1/2)*s^(-1)'); 26 | }); 27 | 28 | it('should invert a Laplace transform correctly', function () { 29 | expect(nerdamer('ilt(a/(b*x), x, t)').toString()).toEqual('a*b^(-1)'); 30 | expect(nerdamer('ilt(a*6/(b*s^6),s,t)').toString()).toEqual('(1/20)*a*b^(-1)*t^5'); 31 | expect(nerdamer('ilt(3*s/(4*s^2+5),s,t)').toString()).toEqual('(3/4)*cos((1/2)*sqrt(5)*t)'); 32 | expect(nerdamer('ilt(2/(3*s^2+1),s,t)').toString()).toEqual('2*sin((1/3)*sqrt(3)*t)*sqrt(3)^(-1)'); 33 | expect(nerdamer('ilt(5*sqrt(pi)/(3*s^(3/2)),s,t)').toString()).toEqual('(10/3)*sqrt(t)'); 34 | expect(nerdamer('ilt(3/(7*s^2+1)^2, s, t)').toString()).toEqual('(-3/14)*cos((1/7)*sqrt(7)*t)*t+(3/2)*sin((1/7)*sqrt(7)*t)*sqrt(7)^(-1)'); 35 | expect(nerdamer('ilt(5*s/(s^2+4)^2, s, t)').toString()).toEqual('(5/4)*sin(2*t)*t'); 36 | expect(nerdamer('ilt(8*s^2/(2*s^2+3)^2, s, t)').toString()).toEqual('2*sin((1/2)*sqrt(6)*t)*sqrt(6)^(-1)+cos((1/2)*sqrt(6)*t)*t'); 37 | expect(nerdamer('ilt((6*s^2-1)/(4*s^2+1)^2, s, t)').toString()).toEqual('(1/8)*sin((1/2)*t)+(5/16)*cos((1/2)*t)*t'); 38 | expect(nerdamer('ilt((5*(sin(1)*s+3*cos(1)))/(s^2+9),s, t)').toString()).toEqual('5*cos(1)*sin(3*t)+5*cos(3*t)*sin(1)'); 39 | expect(nerdamer('ilt(((s+1)*(s+2)*(s+3))^(-1), s, t)').toString()).toEqual('(1/2)*e^(-3*t)+(1/2)*e^(-t)-e^(-2*t)'); 40 | expect(nerdamer('ilt(1/(s^2+s+1),s,t)').toString()).toEqual('2*e^((-1/2)*t)*sin((1/2)*sqrt(3)*t)*sqrt(3)^(-1)'); 41 | expect(nerdamer('ilt(1/(s^2+2s+1),s,t)').toString()).toEqual('e^(-t)*t'); 42 | // expect(nerdamer('ilt(1/((s)(s^2+4s+1)),s,t)').toString()).toEqual('1+e^(-2*t)*i^(-1)*sin(i*sqrt(3)*t)*sqrt(3)^(-1)'); 43 | }); 44 | 45 | it('should calculate mode correctly', function () { 46 | expect(nerdamer('mode(r,r,r,r)').toString()).toEqual('r'); 47 | expect(nerdamer('mode(1,2)').toString()).toEqual('mode(1,2)'); 48 | expect(nerdamer('mode(1,2,3,1,2)').toString()).toEqual('mode(1,2)'); 49 | expect(nerdamer('mode(1,1,2)').toString()).toEqual('1'); 50 | expect(nerdamer('mode(a,a,b,c,a,b,d)').toString()).toEqual('a'); 51 | expect(nerdamer('mode(x, r+1, 21, tan(x), r+1)').toString()).toEqual('1+r'); 52 | expect(nerdamer('mode(x, r+1, 21, tan(x), r+1, x)').toString()).toEqual('mode(1+r,x)'); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /spec/solve.spec.js: -------------------------------------------------------------------------------- 1 | /* global expect */ 2 | 3 | 'use strict'; 4 | 5 | var nerdamer = require('../nerdamer.core.js'); 6 | require('../Solve'); 7 | 8 | // describe("profiler", () => { 9 | // it("start", async function() { 10 | // console.profile(); 11 | // }); 12 | // }); 13 | 14 | describe('Solve', function () { 15 | it('debug problem of the day', function () { 16 | // expect(nerdamer('x^3+x^2-4x-4=y').solveFor('x').toString()).toEqual(''); 17 | }); 18 | it('should solve correctly', function () { 19 | expect(nerdamer('solve(x=y/3416.3333333333344, y)').toString()).toEqual('[(1073228064103962/314146179365)*x]'); 20 | expect(nerdamer('solve(x, x)').toString()).toEqual('[0]'); 21 | expect(nerdamer('solve(5*y^x=8, x)').toString()).toEqual('[log(8/5)*log(y)^(-1)]'); 22 | expect(nerdamer('solve(x^y+8=a*b, x)').toString()).toEqual('[(-8+a*b)^y^(-1)]'); 23 | expect(nerdamer('solve(x^2, x)').toString()).toEqual('[0]'); 24 | expect(nerdamer('solve(x^3, x)').toString()).toEqual('[0]'); 25 | expect(nerdamer('solve(x+1, x)').toString()).toEqual('[-1]'); 26 | expect(nerdamer('solve(x^2+1, x)').toString()).toEqual('[i,-i]'); 27 | expect(nerdamer('solve(2*x^2+1, x)').toString()).toEqual('[(1/2)*i*sqrt(2),(-1/2)*i*sqrt(2)]'); 28 | expect(nerdamer('solve(3*(x+5)*(x-4), x)').toString()).toEqual('[-5,4]'); 29 | expect(nerdamer('solve(3*(x+a)*(x-b), x)').toString()).toEqual('[-a,b]'); 30 | expect(nerdamer('solve(a*x^2+b, x)').toString()).toEqual('[a^(-1)*i*sqrt(a)*sqrt(b),-a^(-1)*i*sqrt(a)*sqrt(b)]'); 31 | expect(nerdamer('solve(x^2+2*x+1, x)').toString()).toEqual('[-1]'); 32 | expect(nerdamer('solve(-5 sqrt(14)x-14x^2 sqrt(83)-10=0,x)').toString()).toEqual('[(-1/28)*sqrt(-560*sqrt(83)+350)*sqrt(83)^(-1)+(-5/28)*sqrt(14)*sqrt(83)^(-1),(-5/28)*sqrt(14)*sqrt(83)^(-1)+(1/28)*sqrt(-560*sqrt(83)+350)*sqrt(83)^(-1)]'); 33 | expect(nerdamer('solve(-5*sqrt(14)x-14x^2*sqrt(83)-10x=0,x)').toString()).toEqual('[(-5/14)*(2+sqrt(14))*sqrt(83)^(-1),0]'); 34 | expect(nerdamer('solve(8*x^3-26x^2+3x+9,x)').toString()).toEqual('[3/4,-1/2,3]'); 35 | expect(nerdamer('solve(a*x^2+b*x+c, x)').toString()).toEqual('[(1/2)*(-b+sqrt(-4*a*c+b^2))*a^(-1),(1/2)*(-b-sqrt(-4*a*c+b^2))*a^(-1)]'); 36 | expect(nerdamer('solve(sqrt(x^3)+sqrt(x^2)-sqrt(x)=0,x)').toString()).toEqual('[0,9854668106203761/30348392770825801]'); 37 | expect(nerdamer('solve(x^3-10x^2+31x-30,x)').toString()).toEqual('[3,5,2]'); 38 | expect(nerdamer('solve(sqrt(x)+sqrt(2x+1)=5,x)').toString()).toEqual('[4]'); 39 | expect(nerdamer('solve(x=2/(3-x),x)').toString()).toEqual('[1,2]'); 40 | expect(nerdamer('solve(1/x=a,x)').toString()).toEqual('[a^(-1)]'); 41 | expect(nerdamer('solve(sqrt(x^2-1),x)').toString()).toEqual('[1,-1]'); 42 | expect(nerdamer('solve(m*x^9+n,x)').toString()).toEqual('[2*m^(-1/9)*n^(1/9),2*e^((2/9)*i*pi)*m^(-1/9)*n^(1/9),2*e^((4/9)*i*pi)*m^(-1/9)*n^(1/9),2*e^((2/3)*i*pi)*m^(-1/9)*n^(1/9),2*e^((8/9)*i*pi)*m^(-1/9)*n^(1/9),2*e^((10/9)*i*pi)*m^(-1/9)*n^(1/9),2*e^((4/3)*i*pi)*m^(-1/9)*n^(1/9),2*e^((14/9)*i*pi)*m^(-1/9)*n^(1/9),2*e^((16/9)*i*pi)*m^(-1/9)*n^(1/9)]'); 43 | expect(nerdamer('solve(sqrt(97)x^2-sqrt(13)x+sqrt(14)x+sqrt(43)x^2+sqrt(3)*sqrt(101)=0,x)').toString()).toEqual('[(-1/2)*(sqrt(43)+sqrt(97))^(-1)*sqrt(14)+(1/2)*(sqrt(43)+sqrt(97))^(-1)*sqrt(-2*sqrt(13)*sqrt(14)-4*sqrt(101)*sqrt(3)*sqrt(43)-4*sqrt(101)*sqrt(3)*sqrt(97)+27)+(1/2)*(sqrt(43)+sqrt(97))^(-1)*sqrt(13),(-1/2)*(sqrt(43)+sqrt(97))^(-1)*sqrt(-2*sqrt(13)*sqrt(14)-4*sqrt(101)*sqrt(3)*sqrt(43)-4*sqrt(101)*sqrt(3)*sqrt(97)+27)+(-1/2)*(sqrt(43)+sqrt(97))^(-1)*sqrt(14)+(1/2)*(sqrt(43)+sqrt(97))^(-1)*sqrt(13)]'); 44 | expect(nerdamer('solve(a*y^2*x^3-1, x)').toString()).toEqual('[((-1/2)*abs(a^(-1)*y^(-2))+(1/2)*a^(-1)*y^(-2))^(1/3)+((1/2)*a^(-1)*y^(-2)+(1/2)*abs(a^(-1)*y^(-2)))^(1/3),(((-1/2)*abs(a^(-1)*y^(-2))+(1/2)*a^(-1)*y^(-2))^(1/3)+((1/2)*a^(-1)*y^(-2)+(1/2)*abs(a^(-1)*y^(-2)))^(1/3))*((1/2)*i*sqrt(3)+1/2),(((-1/2)*abs(a^(-1)*y^(-2))+(1/2)*a^(-1)*y^(-2))^(1/3)+((1/2)*a^(-1)*y^(-2)+(1/2)*abs(a^(-1)*y^(-2)))^(1/3))*((1/2)*i*sqrt(3)+1/2)^2]'); 45 | expect(nerdamer('solve((1/2)*sqrt(-4*x+4*y)-2+y, y)').toString()).toEqual('[(-1/2)*(-5+sqrt(-4*x+9)),(-1/2)*(-5-sqrt(-4*x+9))]'); 46 | expect(nerdamer('solve(log(a*x-c)-b=21, x)').toString()).toEqual('[-(-c-e^(21+b))*a^(-1)]'); 47 | expect(nerdamer('solve(x/(x-a)+4,x)').toString()).toEqual('[(4/5)*a]'); 48 | expect(nerdamer('solve(3*sin(a^2*x-b)-4,x)').toString()).toEqual('[a^(-2)*asin(4/3)]'); 49 | expect(nerdamer('solve(a*log(x^2-4)-4,x)').toString()).toEqual('[(1/2)*sqrt(16+4*e^(4*a^(-1))),(-1/2)*sqrt(16+4*e^(4*a^(-1)))]'); 50 | expect(nerdamer('solve(x/(x^2+2*x+1)+4,x)').toString()).toEqual('[(1/8)*sqrt(17)-9/8,(-1/8)*sqrt(17)-9/8]'); 51 | expect(nerdamer('solve((a*x^2+1),x)').toString()).toEqual('[a^(-1)*sqrt(-a),-a^(-1)*sqrt(-a)]'); 52 | expect(nerdamer('solve(sqrt(x)-2x+x^2,x)').toString()).toEqual('[(-1/2)*sqrt(5)+3/2,0,832040/2178309,1]'); 53 | expect(nerdamer('solve((2x+x^2)^2-x,x)').toString()).toEqual('[0,((-1/6)*sqrt(3)^(-1)*sqrt(59)+43/54)^(1/3)+((1/6)*sqrt(3)^(-1)*sqrt(59)+43/54)^(1/3)-4/3,(((-1/6)*sqrt(3)^(-1)*sqrt(59)+43/54)^(1/3)+((1/6)*sqrt(3)^(-1)*sqrt(59)+43/54)^(1/3)-4/3)*((1/2)*i*sqrt(3)+1/2),(((-1/6)*sqrt(3)^(-1)*sqrt(59)+43/54)^(1/3)+((1/6)*sqrt(3)^(-1)*sqrt(59)+43/54)^(1/3)-4/3)*((1/2)*i*sqrt(3)+1/2)^2]'); 54 | expect(nerdamer('solve((5*x^4-2)/(x+1)/(x^2-1),x)').toString()).toEqual('[72425485/91070226,-72425485/91070226,(316684236/398209345)*i,(-316684236/398209345)*i]'); 55 | expect(nerdamer('solve(0=(x^(2)-2)/(e^(x)-1), x)').toString()).toEqual('[sqrt(2),-sqrt(2)]'); 56 | expect(nerdamer('solve(4/y^2=x^2+1,y)').toString()).toEqual('[(1/2)*(1+x^2)^(-1)*sqrt(16+16*x^2),(-1/2)*(1+x^2)^(-1)*sqrt(16+16*x^2)]'); 57 | expect(nerdamer('solve(1/(x+x^2), x)').toString()).toEqual('[]'); 58 | expect(nerdamer('solve(1/(x+x^2-1), x)').toString()).toEqual('[]'); 59 | expect(nerdamer('solve(-1+11000*(-100*(10+x)^(-1)+20)^(-2)*(10+x)^(-2), x)').toString()).toEqual('[(-1/2)*sqrt(110)-5,(1/2)*sqrt(110)-5]'); 60 | expect(nerdamer('solve(x^3+y^3=3, x)').toString()).toEqual('[((-1/2)*y^3+3/2+abs((-1/2)*y^3+3/2))^(1/3)+((-1/2)*y^3-abs((-1/2)*y^3+3/2)+3/2)^(1/3),(((-1/2)*y^3+3/2+abs((-1/2)*y^3+3/2))^(1/3)+((-1/2)*y^3-abs((-1/2)*y^3+3/2)+3/2)^(1/3))*((1/2)*i*sqrt(3)+1/2),(((-1/2)*y^3+3/2+abs((-1/2)*y^3+3/2))^(1/3)+((-1/2)*y^3-abs((-1/2)*y^3+3/2)+3/2)^(1/3))*((1/2)*i*sqrt(3)+1/2)^2]'); 61 | expect(nerdamer('solve(sqrt(10x+186)=x+9,x)').toString()).toEqual('[7]'); 62 | expect(nerdamer('solve(x^3+8=x^2+6,x)').toString()).toEqual('[-1,1+i,-i+1]'); 63 | expect(nerdamer('solve(x^3-10x^2+31x-30,x)').toString()).toEqual('[3,5,2]'); 64 | expect(nerdamer('solve(8x^3-26x^2+3x+9,x)').toString()).toEqual('[3/4,-1/2,3]'); 65 | expect(nerdamer('solve(x^3-1/2x^2-13/2x-3,x)').toString()).toEqual('[-2,3,-1/2]'); 66 | expect(nerdamer('solve(x^3+2x^2+3x-4=0,x)').evaluate().text()).toEqual('[70419755/90741794,(9781803879340745/14554630046086988)*i+70419755/181483588,(5721734032321405/8513534220250202)*i-96689750724854153216/249185798564226019925]'); 67 | expect(nerdamer('solve(x*log(x),x)').toString()).toEqual('[1]'); 68 | expect(nerdamer('solve((9x+x^2)^3+10800x+40x^4+4440x^2+720x^3+20(9*x+x^2)^2+8000,x) ').toString()).toEqual('[-5,-4]'); 69 | expect(nerdamer('solve((x^3-4)/(x^3+7x-11),x)').evaluate().text()).toEqual('[123554237/77834292,(17162560630828463/12484317038729784)*i+123554237/155668584,(10039007984057947/7302532595409636)*i-17388712991038935924736/21908405527990418074125]'); 70 | expect(nerdamer('solve((93222358/131836323)*(-2*y+549964829/38888386)=10, y)').toString()).toEqual('[1/3625267041734188]'); 71 | expect(nerdamer('solve(sqrt(x)+sqrt(2x+1)=5,x) ').toString()).toEqual('[4]'); 72 | expect(nerdamer('solve(sqrt(x)-1,x) ').toString()).toEqual('[1]'); 73 | expect(nerdamer('solve(sqrt(x)+1,x)').toString()).toEqual('[]'); 74 | expect(nerdamer('solve((x-1)*(x+1)*x=3x,x)').toString()).toEqual('[0,2,-2]'); 75 | expect(nerdamer('solve(sqrt(x^2+1),x)').toString()).toEqual('[i,-i]'); 76 | expect(nerdamer('solve(sqrt(x^2-1),x)').toString()).toEqual('[1,-1]'); 77 | expect(nerdamer('solve(((x+1)*((x+1)+1))/2=n,x)').toString()).toEqual('[(1/2)*(-3+sqrt(1+8*n)),(1/2)*(-3-sqrt(1+8*n))]'); 78 | expect(nerdamer('solve(sqrt(10x+186)=x+9,x)').toString()).toEqual('[7]'); 79 | expect(nerdamer('solve(x^3+8=x^2+6,x)').toString()).toEqual('[-1,1+i,-i+1]'); 80 | expect(nerdamer('solve(x^2=x^-2,x)').toString()).toEqual('[1,-1,i,-i]'); 81 | expect(nerdamer('solve((x+1)(x+1)x=3x,x)').toString()).toEqual('[0,-1+sqrt(3),-1-sqrt(3)]'); 82 | expect(nerdamer('solve(log(y) = -t, y)').toString() ).toEqual('[e^(-t)]'); 83 | expect(nerdamer('solve(y=exp(4x),x)').toString() ).toEqual('[(1/4)*log(y)]'); 84 | expect(nerdamer('solve(s=exp(m/2), m)').toString()).toEqual('[2*log(s)]'); 85 | expect(nerdamer('solve(x=2^x/4,x)').toString()).toEqual('[6469019/20874070,4]'); 86 | expect(nerdamer('solve(x*y+y=0,x)').toString() ).toEqual('[-1]'); 87 | expect(nerdamer('(y+((x)^(2)))=9').solveFor('x').toString() ).toEqual('sqrt(-y+9),-sqrt(-y+9)'); 88 | expect(nerdamer('solve(c*((((((4*x)+1))*d))/(5))*f=((a)/(b)), x)').toString()).toEqual('[(-1/4)*(-5*a+b*c*d*f)*(b*c*d*f)^(-1)]'); 89 | expect(nerdamer('c*((((((4*x)+1))*d))/(5))*f=((a)/(b))').solveFor('x').toString()).toEqual('(1/4)*(-b*c*d*f+5*a)*(b*c*d*f)^(-1)'); 90 | expect(nerdamer('solve(1000+200*x=100*2^(x/2), x)').toString()).toEqual('[-89048725/18140732,145224097/14865809]'); 91 | expect(nerdamer('solve(x=100*2^((1/400)*(-1000+x)), x)').toString()).toEqual('[332944835/18248037,5890028082/1994051]'); 92 | expect(nerdamer('solve(10000000=10^(x+1), x)').toString()).toEqual('[6]'); 93 | expect(nerdamer('solve(m=(34141034228515471851/614756350000000000000000000)*(3631430813109509/100837351+51955423*log(5+m)), m)') 94 | .toString()).toEqual('[-89048725/18140732,145224097/14865809]'); 95 | expect(nerdamer('(5-3y)/(5+y)=(1-9y)/(3y-7)').solveFor("y").toString()).toEqual('1/2'); 96 | // np issue #26 97 | // expect(nerdamer("solve(h=1/2*(9.81)*m*s^(-2)*t^2, t)").evaluate().text()).toEqual("[0.4515236409857309*s*sqrt(h)*sqrt(m)^(-1),-0.4515236409857309*s*sqrt(h)*sqrt(m)^(-1)]"); 98 | // like: 99 | expect(nerdamer("solve(h=1/2*(9.81)*t^2, t)").evaluate().text()).toEqual("[(-35364869/78323405)*sqrt(h),(35364869/78323405)*sqrt(h)]"); 100 | expect(nerdamer("h=(981/200)*m*s^(-2)*t^2").solveFor("t").toString()).toEqual('(-10/327)*abs(s)*m^(-1)*sqrt(218)*sqrt(h)*sqrt(m),(10/327)*abs(s)*m^(-1)*sqrt(218)*sqrt(h)*sqrt(m)'); 101 | }); 102 | 103 | it('should solve system of equations correctly', function () { 104 | expect(nerdamer.solveEquations(['x+y=1', '2*x=6', '4*z+y=6']).toString()).toEqual('x,3,y,-2,z,2'); 105 | expect(nerdamer.solveEquations(['x+y=a', 'x-y=b', 'z+y=c'], ['x', 'y', 'z']).toString()).toEqual('x,0.5*a+0.5*b,y,-0.5*b+0.5*a,z,-0.5*a+0.5*b+c'); 106 | expect(nerdamer.solveEquations(['x-2*y=-3', 'x+y-z+2*d=8', '5*d-1=19', 'z+d=7']).toString()).toEqual('d,4,x,1,y,2,z,3'); 107 | expect(nerdamer.solveEquations('x^2+4=x-y').toString()).toEqual('(1/2)*(1+sqrt(-15-4*y)),(1/2)*(-sqrt(-15-4*y)+1)'); 108 | expect(nerdamer.solveEquations(['x+y=3', 'y^3-x=7']).toString()).toEqual('x,1,y,2'); 109 | expect(nerdamer.solveEquations(['x^2+y=3', 'x+y+z=6', 'z^2-y=7']).toString()).toEqual('x,1,y,2,z,3'); 110 | expect(nerdamer.solveEquations(['x*y-cos(z)=-3', '3*z^3-y^2+1=12', '3*sin(x)*cos(y)-x^3=-4']).toString()).toEqual('x,1.10523895006979,y,-2.98980336936266,z,1.88015428627437'); 111 | expect(nerdamer.solveEquations(['x=i','x+y=3']).toString()).toEqual('x,i,y,-i+3'); 112 | expect(nerdamer.solveEquations(["x/(45909438.9 + 0 + x)=0", "45909438.9+0+x=45909438.9"]).toString()).toEqual('x,0'); 113 | expect(nerdamer.solveEquations(["a=1"]).toString()).toEqual('a,1'); 114 | expect(nerdamer.solveEquations(["x=5", "0.6=1-(x/(10+y))"]).toString()).toEqual('x,5,y,2.5'); 115 | // np issue #13 116 | expect(nerdamer.solveEquations(["0=a*c", "0=b"], ["a", "b"]).toString()).toEqual('a,0,b,0'); 117 | }); 118 | //#55: nerdamer.solveEquation quits working 119 | it('should handle text("fractions") without later impact', function () { 120 | expect(nerdamer.solveEquations("x+1=2", "x").toString()).toEqual('1'); 121 | expect(nerdamer('x=1').text("fractions")).toEqual('x=1'); 122 | expect(nerdamer.solveEquations("x+1=2", "x").toString()).toEqual('1'); 123 | }); 124 | it('should parse equations correctly', function () { 125 | expect(nerdamer("-(a+1)=(a+3)^2").toString()).toEqual('-1-a=(3+a)^2'); 126 | }); 127 | //NOTE: contains duplicates 128 | it('should solve functions with factorials', function () { 129 | // Bug: And I don't believe the expected solution is correct, see Wolfram Alpha 130 | // expect(nerdamer('solve(x!-x^2,x)').text('decimals', 20)).toEqual('[-2.200391782610595,-4.010232827899529,-2.938361683501947,1,1.000000000000001,3.562382285390900,3.562382285390896,0.9999999999999910,1.000000000000000]'); 131 | }); 132 | it('should solve for variables other than x', function () { 133 | expect(nerdamer('solve(2*a^(2)+4*a*6=128, a)').toString()).toEqual('[4,-16]'); 134 | }); 135 | it('should solve nonlinear system of equations with multiple parameter functions', function () { 136 | var ans = nerdamer.solveEquations([ 137 | `y=x * 2`, 138 | `z=y + max (y * 0.1, 23)`, 139 | `j=y + max (y * 0.1, 23)`, 140 | `6694.895373 = j + z + (max(j * 0.280587, z * 0.280587, 176))` 141 | ]); 142 | expect(ans.toString()).toEqual('j,2935.601831019821,x,1334.364468645373,y,2668.728937290746,z,2935.601831019821'); 143 | }); 144 | 145 | it('should solve factors', function () { 146 | expect(nerdamer('solve((x-1)*(-a*c-a*x+c*x+x^2),x)').text()).toEqual('[1,-c,a]'); 147 | }); 148 | 149 | it('should solve circle equations', function() { 150 | var eq1 ="x^2+y^2=1"; 151 | var eq2 ="x+y=1"; 152 | var sol = nerdamer.solveEquations([eq1, eq2]); 153 | expect(sol.toString()).toEqual('x,1,0,y,0,1'); 154 | }); 155 | it('regression tests', function() { 156 | expect(nerdamer('solve(a^2-a-1=0,a)').toString()).toEqual('[(1/2)*sqrt(5)+1/2,(-1/2)*sqrt(5)+1/2]'); 157 | // issue #26 158 | expect(nerdamer("solve(h=(981/200)*baseunit_m*baseunit_s^(-2)*t^2, t)").text()) 159 | .toEqual('[(-10/327)*baseunit_m^(-1)*baseunit_s*sqrt(218)*sqrt(baseunit_m)*sqrt(h),(10/327)*baseunit_m^(-1)*baseunit_s*sqrt(218)*sqrt(baseunit_m)*sqrt(h)]'); 160 | expect(nerdamer('solve(abs(a-b)=0, a)').toString()).toEqual('[b]'); 161 | expect(nerdamer('solve(abs(a-b)=5, a)').toString()).toEqual('[-5+b,-(-5-b)]'); 162 | // issue #43 163 | expect(nerdamer('solve(x-6/x - 1 = 0,x)').toString()).toEqual('[-2,3]'); 164 | expect(nerdamer('solve(x^2-x-6=0,x)').toString()).toEqual('[-2,3]'); 165 | expect(nerdamer('solve((x^2-x-6)*e^2=0,x)').toString()).toEqual('[-2,3]'); 166 | expect(nerdamer('solve((x^2-x-6)*e^x=0,x)').toString()).toEqual('[-2,3]'); 167 | expect(nerdamer('solve((x^2-x-6)*e^(x-4)=0,x)').toString()).toEqual('[-2,3]'); 168 | expect(nerdamer('solve((x^2-x-6)*e^(x+4)=0,x)').toString()).toEqual('[-2,3]'); 169 | expect(nerdamer('solve((x^2-x-6)*e^(x^2)=0,x)').toString()).toEqual('[-2,3]'); 170 | expect(nerdamer('solve((x^2-x-6)*e^(x^2)=0,x)').toString()).toEqual('[-2,3]'); 171 | // API version 172 | expect(nerdamer.solve('x-6/x - 1 = 0','x').toString()).toEqual('[-2,3]'); 173 | expect(nerdamer.solve('(x^2-x-6=0)','x').toString()).toEqual('[-2,3]'); 174 | expect(nerdamer.solve('(x^2-x-6)*e^2=0','x').toString()).toEqual('[-2,3]'); 175 | expect(nerdamer.solve('(x^2-x-6)*e^x=0','x').toString()).toEqual('[-2,3]'); 176 | expect(nerdamer.solve('(x^2-x-6)*e^(x-4)=0','x').toString()).toEqual('[-2,3]'); 177 | expect(nerdamer.solve('(x^2-x-6)*e^(x+4)=0','x').toString()).toEqual('[-2,3]'); 178 | expect(nerdamer.solve('(x^2-x-6)*e^(x^2)=0','x').toString()).toEqual('[-2,3]'); 179 | expect(nerdamer.solve('(x^2-x-6)*e^(x^2)=0','x').toString()).toEqual('[-2,3]'); 180 | expect(nerdamer("y=-y+2+8*a").solveFor("y").toString()).toEqual('1+4*a'); 181 | 182 | // issue 52 183 | expect(nerdamer.solveEquations(['x*(b+1)+y=1', 'x+y=6'], ["x","y"]).toString()) 184 | .toEqual('x,((-(1+b)^(-1)+1)^(-1)*(1+b)^(-1)+1)*(1+b)^(-1)-6*(-(1+b)^(-1)+1)^(-1)*(1+b)^(-1),y,-(-(1+b)^(-1)+1)^(-1)*(1+b)^(-1)+6*(-(1+b)^(-1)+1)^(-1)'); 185 | 186 | // issue #54 187 | expect(nerdamer('x=y').solveFor('x').map(s=>s.text()).join(",")).toEqual('y'); 188 | }); 189 | }); 190 | 191 | // describe("profiler", () => { 192 | // it("stop", async function() { 193 | // console.profileEnd(); 194 | // }); 195 | // }); 196 | 197 | -------------------------------------------------------------------------------- /spec/support/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "spec", 3 | "spec_files": [ 4 | "**/*[sS]pec.js" 5 | ], 6 | "helpers": [ 7 | "helpers/**/*.js" 8 | ], 9 | "stopSpecOnExpectationFailure": false, 10 | "random": false 11 | } 12 | -------------------------------------------------------------------------------- /spec/support/utils.js: -------------------------------------------------------------------------------- 1 | /* global expect */ 2 | 3 | var nerdamer = require('../../nerdamer.core.js'); 4 | 5 | //fix for rounding errors in some functions 6 | var toFixed = function(x, n) { 7 | return Number(x).toFixed(n||14); 8 | }; 9 | 10 | /** 11 | * @param {Array} o An array of object to parse 12 | * @param {String} dec Get output as decimals 13 | */ 14 | var run = function(o, dec) { 15 | dec = dec || 'decimal'; 16 | for (var i = 0; i < o.length; ++i) { 17 | // when 18 | var parsed = nerdamer(o[i].given); 19 | var value = parsed.evaluate().text(dec); 20 | 21 | // then 22 | expect(parsed.toString()).toEqual(o[i].expected); 23 | expect(value).toEqual(o[i].expectedValue); 24 | } 25 | }; 26 | 27 | /** 28 | * @param {String} e The expression 29 | * @param {object} subs The substitution object 30 | */ 31 | var parse = function(e, subs) { 32 | var r = nerdamer(e, subs).evaluate().text('decimals'); 33 | if(!isNaN(r)) 34 | r = Number(r); 35 | return r; 36 | }; 37 | 38 | module.exports = { 39 | run: run, 40 | toFixed: toFixed, 41 | parse: parse 42 | }; -------------------------------------------------------------------------------- /spec/test.js: -------------------------------------------------------------------------------- 1 | /* global expect */ 2 | 3 | 'use strict'; 4 | 5 | var nerdamer = require('../nerdamer.core.js'); 6 | require('../Algebra.js'); 7 | require('../Calculus.js'); 8 | require('../Solve.js'); 9 | require('../Extra.js'); 10 | console.global = {tsDebugChannels: {notimeout: true}}; 11 | 12 | 13 | // console.log(nerdamer('solve((5-3y)/(5+y)=(1-9y)/(3y-7),y)') 14 | // .toString()); 15 | // console.log(nerdamer('(5-3y)/(5+y)=(1-9y)/(3y-7)').solveFor("y").toString()); 16 | 17 | // console.log(nerdamer('limit((2sin(x)-sin(2x))/(x-sin(x)),x,0)').toString()); 18 | 19 | // const testEq= ["0=a*c", "0=b"]; 20 | // const testUnkwowns = ["a", "b"]; 21 | // const sol = nerdamer.solveEquations(testEq, [...testUnkwowns]); 22 | // console.log(sol.toString()); 23 | 24 | // const x = nerdamer("-0.5*sqrt(5)+0.5").simplify().text() 25 | // let x = nerdamer("solve(-4901*t^2+1000*h*s^2, t)").evaluate().text(); 26 | // let x = nerdamer("factor(4901*t^2+1000*h*s^2)").evaluate().text("fractions"); 27 | // console.log(x); 28 | // x = nerdamer("factor(4902*t^2+1000*h*s^2)").evaluate().text(); 29 | // console.log(x); 30 | // x = nerdamer("factor(4903*t^2+1000*h*s^2)").evaluate().text(); 31 | // console.log(x); 32 | 33 | // let x = nerdamer("3.53540^3.5").evaluate().text(); 34 | // console.log(x); 35 | // x = nerdamer("3.535401^3.5").evaluate().text(); 36 | // console.log(x); 37 | 38 | let text; 39 | let x; 40 | 41 | try { 42 | x = nerdamer('vecget(primes(0,5),3)').evaluate(); 43 | console.log(x.text()); 44 | } catch (error) { 45 | console.log("error "+error) 46 | } 47 | 48 | // console.log(text); 49 | console.log("done"); 50 | -------------------------------------------------------------------------------- /spec/text.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var nerdamer = require('../nerdamer.core.js'); 4 | 5 | describe('The text function', function () { 6 | // given 7 | var testCases = [ 8 | { 9 | given: '6', 10 | expected_mixed: '6', 11 | expected_recurring: "6" 12 | }, 13 | { 14 | given: '-5', 15 | expected_mixed: '-5', 16 | expected_recurring: "-5" 17 | }, 18 | { 19 | given: '1/1', 20 | expected_mixed: '1', 21 | expected_recurring: "1" 22 | }, 23 | { 24 | given: '1/5', 25 | expected_mixed: '1/5', 26 | expected_recurring: "0.2" 27 | }, 28 | { 29 | given: '-1/5', 30 | expected_mixed: '-1/5', 31 | expected_recurring: "-0.2" 32 | }, 33 | { 34 | given: '1/-5', 35 | expected_mixed: '-1/5', 36 | expected_recurring: "-0.2" 37 | }, 38 | { 39 | given: '6/5', 40 | expected_mixed: '1+1/5', 41 | expected_recurring: "1.2" 42 | }, 43 | { 44 | given: '-6/5', 45 | expected_mixed: '-1-1/5', 46 | expected_recurring: "-1.2" 47 | }, 48 | { 49 | given: '6/5a', 50 | expected_mixed: '(1+1/5)*a', 51 | expected_recurring: "1.2*a" 52 | }, 53 | { 54 | given: 'a/5', 55 | expected_mixed: '(1/5)*a', 56 | expected_recurring: "0.2*a" 57 | }, 58 | { 59 | given: '1/a', 60 | expected_mixed: 'a^(-1)', 61 | expected_recurring: "a^(-1)" 62 | }, 63 | { 64 | given: '(2x)/(3y)', 65 | expected_mixed: '(2/3)*x*y^(-1)', 66 | expected_recurring: "(0.'6')*x*y^(-1)" 67 | }, 68 | { 69 | given: '(3x)/(2y)', 70 | expected_mixed: '(1+1/2)*x*y^(-1)', 71 | expected_recurring: "1.5*x*y^(-1)" 72 | }, 73 | { 74 | given: '(2x)/(-3y)', 75 | expected_mixed: '(-2/3)*x*y^(-1)', 76 | expected_recurring: "(-0.'6')*x*y^(-1)" 77 | }, 78 | { 79 | given: '(3x)/(-2y)', 80 | expected_mixed: '(-1-1/2)*x*y^(-1)', 81 | expected_recurring: "-1.5*x*y^(-1)" 82 | }, 83 | { 84 | given: '(10/-8)a^(-9/6)', 85 | expected_mixed: '(-1-1/4)*a^(-1-1/2)', 86 | expected_recurring: "-1.25*a^(-1.5)" 87 | }, 88 | { 89 | given: '1/2+3/4', 90 | expected_mixed: '1+1/4', 91 | expected_recurring: "1.25" 92 | }, 93 | { 94 | given: '2/3+4/7', 95 | expected_mixed: '1+5/21', 96 | expected_recurring: "1.'238095'" 97 | }, 98 | { 99 | given: '100-46/47-98/43-67/44', 100 | expected_mixed: '95+19517/88924', 101 | expected_recurring: "95.21'947955557554765867482344473932796545364580990508749044127569610004048400881651747559713913004363276505780216814358328460258198011785344788808420673833835634924204939049075615132022850973865323197337051864513517160721515001574378120642346273222077279474607530025639872250461067878188115694300751203274706490936080248301920741307183662453330934280959021186631280644145562502811389501147046916467995141918942017902928343304394764068193063739822770005847690162385857586253429895191399397238090954073141109261841572578831361612163195537762583779407134181998110746255229184472133507264630470963969232153299446718546174261166839098556070352210876703702037695110431379605056002878862849174576042463227025324996626332598623543700238405829697269578516485988034726283118168323512212675992982771805136970896495884125770320723314290855112230668885790112905402366065404165354684899464711438981602267104493724978633439791282443434843236921416040663937744590886599793081732715577346'" 102 | }, 103 | { 104 | given: '1+2-2/3+3/4-4/5+5/6-6/7+7/8-8/9+9/10-10/11', 105 | expected_mixed: '2+6557/27720', 106 | expected_recurring: "2.236'544011'" 107 | }, 108 | { 109 | given: '20*30/46', 110 | expected_mixed: '13+1/23', 111 | expected_recurring: "13.0'4347826086956521739130'" 112 | } 113 | ]; 114 | 115 | it('should give mixed fractions correctly', function () { 116 | for (var i = 0; i < testCases.length; ++i) { 117 | // when 118 | var text = nerdamer(testCases[i].given).text('mixed'); 119 | // then 120 | expect(text).toEqual(testCases[i].expected_mixed); 121 | } 122 | }); 123 | 124 | it('should give recurring decimals correctly', function () { 125 | for (var i = 0; i < testCases.length; ++i) { 126 | // when 127 | var text = nerdamer(testCases[i].given).text('recurring'); 128 | // then 129 | expect(text).toEqual(testCases[i].expected_recurring); 130 | } 131 | }); 132 | }); 133 | -------------------------------------------------------------------------------- /spec/unsolved.spec.js: -------------------------------------------------------------------------------- 1 | /* global expect */ 2 | 3 | 'use strict'; 4 | 5 | var nerdamer = require('../nerdamer.core.js'); 6 | require('../Algebra.js'); 7 | require('../Calculus.js'); 8 | require('../Solve.js'); 9 | 10 | describe('Known problems:', function () { 11 | it('Core', function() { 12 | }); 13 | 14 | 15 | it('Algebra', function () { 16 | // issue #1 - baseunit_m should cancel out 17 | // expect(nerdamer('baseunit_m ^(-1)*sqrt(baseunit_m ^2*cos(3)+baseunit_m ^2)') 18 | // .evaluate().text()) 19 | // .toEqual('0.100037509962788179'); 20 | }); 21 | it('Solve', function () { 22 | // solve x^3-1,x 23 | // solve x^3-2x^2+8x-12,x 24 | }); 25 | }); 26 | --------------------------------------------------------------------------------