1 /* 2 JessieCode Computer algebra algorithms 3 4 Copyright 2011-2019 5 Michael Gerhaeuser, 6 Alfred Wassermann 7 8 JessieCode is free software dual licensed under the GNU LGPL or MIT License. 9 10 You can redistribute it and/or modify it under the terms of the 11 12 * GNU Lesser General Public License as published by 13 the Free Software Foundation, either version 3 of the License, or 14 (at your option) any later version 15 OR 16 * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT 17 18 JessieCode is distributed in the hope that it will be useful, 19 but WITHOUT ANY WARRANTY; without even the implied warranty of 20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 GNU Lesser General Public License for more details. 22 23 You should have received a copy of the GNU Lesser General Public License and 24 the MIT License along with JessieCode. If not, see <http://www.gnu.org/licenses/> 25 and <http://opensource.org/licenses/MIT/>. 26 */ 27 28 /*global JXG: true, define: true, window: true, console: true, self: true, document: true, parser: true*/ 29 /*jslint nomen: true, plusplus: true*/ 30 /*eslint eqeqeq: "off"*/ 31 32 /* depends: 33 jxg 34 parser/geonext 35 base/constants 36 base/text 37 math/math 38 math/geometry 39 math/statistics 40 utils/type 41 utils/uuid 42 */ 43 44 /** 45 * @fileoverview Here, the computer algebra algorithms are implemented. 46 */ 47 48 define([ 49 'jxg', 'base/constants', 'base/text', 'math/math', 'math/geometry', 'math/statistics', 'utils/type', 'utils/env' 50 ], function (JXG, Const, Text, Mat, Geometry, Statistics, Type, Env) { 51 52 "use strict"; 53 54 /** 55 * A JessieCode object provides an interface to the parser and stores all variables and objects used within a JessieCode script. 56 * The optional argument <tt>code</tt> is interpreted after initializing. To evaluate more code after initializing a JessieCode instance 57 * please use {@link JXG.JessieCode#parse}. For code snippets like single expressions use {@link JXG.JessieCode#snippet}. 58 * @constructor 59 * @param {String} [code] Code to parse. 60 * @param {Boolean} [geonext=false] Geonext compatibility mode. 61 */ 62 JXG.CA = function (node, createNode, parser) { 63 this.node = node; 64 this.createNode = createNode; 65 this.parser = parser; 66 }; 67 68 JXG.extend(JXG.CA.prototype, /** @lends JXG.CA.prototype */ { 69 findMapNode: function (mapname, node) { 70 var i, len, ret; 71 72 //console.log("FINDMAP", node); 73 if (node.value === 'op_assign' && node.children[0].value === mapname) { 74 return node.children[1]; 75 } else if (node.children) { 76 len = node.children.length; 77 for (i = 0; i < len; ++i) { 78 ret = this.findMapNode(mapname, node.children[i]); 79 if (ret !== null) { 80 return ret; 81 } 82 } 83 } 84 return null; 85 }, 86 87 /** 88 * Declare all subnodes as math nodes, 89 * i.e recursively set node.isMath = true; 90 */ 91 setMath: function (node) { 92 var i, len; 93 94 if ((node.type == 'node_op' && ( 95 node.value == 'op_add' || node.value == 'op_sub' || 96 node.value == 'op_mul' || node.value == 'op_div' || 97 node.value == 'op_neg' || node.value == 'op_execfun' || 98 node.value == 'op_exp')) || 99 node.type == 'node_var' || node.type == 'node_const') { 100 101 node.isMath = true; 102 } 103 if (node.children) { 104 len = node.children.length; 105 for (i = 0; i < len; ++i) { 106 this.setMath(node.children[i]); 107 } 108 } 109 }, 110 111 deriveElementary: function (node, varname) { 112 var fun = node.children[0].value, 113 arg = node.children[1], 114 newNode; 115 116 117 switch (fun) { 118 case 'abs': 119 // x / sqrt(x * x) 120 newNode = this.createNode('node_op', 'op_div', 121 arg[0], 122 this.createNode('node_op', 'op_execfun', 123 this.createNode('node_var', 'sqrt'), 124 [this.createNode('node_op', 'op_mul', 125 Type.deepCopy(arg[0]), 126 Type.deepCopy(arg[0]) 127 )] 128 ) 129 ); 130 break; 131 132 case 'sqrt': 133 newNode = this.createNode('node_op', 'op_div', 134 this.createNode('node_const', 1.0), 135 this.createNode('node_op', 'op_mul', 136 this.createNode('node_const', 2.0), 137 this.createNode(node.type, node.value, 138 Type.deepCopy(node.children[0]), 139 Type.deepCopy(node.children[1]) 140 ) 141 ) 142 ); 143 break; 144 145 case 'sin': 146 newNode = this.createNode('node_op', 'op_execfun', 147 this.createNode('node_var', 'cos'), 148 Type.deepCopy(arg) 149 ); 150 break; 151 152 case 'cos': 153 newNode = this.createNode('node_op', 'op_neg', 154 this.createNode('node_op', 'op_execfun', 155 this.createNode('node_var', 'sin'), 156 Type.deepCopy(arg) 157 ) 158 ); 159 break; 160 161 case 'tan': 162 newNode = this.createNode('node_op', 'op_div', 163 this.createNode('node_const', 1.0), 164 this.createNode('node_op', 'op_exp', 165 this.createNode('node_op', 'op_execfun', 166 this.createNode('node_var', 'cos'), 167 Type.deepCopy(arg) 168 ), 169 this.createNode('node_const', 2) 170 ) 171 ); 172 break; 173 174 case 'cot': 175 newNode = this.createNode('node_op', 'op_neg', 176 this.createNode('node_op', 'op_div', 177 this.createNode('node_const', 1.0), 178 this.createNode('node_op', 'op_exp', 179 this.createNode('node_op', 'op_execfun', 180 this.createNode('node_var', 'sin'), 181 Type.deepCopy(arg) 182 ), 183 this.createNode('node_const', 2) 184 ) 185 ) 186 ); 187 break; 188 189 case 'exp': 190 newNode = this.createNode(node.type, node.value, 191 Type.deepCopy(node.children[0]), 192 Type.deepCopy(node.children[1]) 193 ); 194 break; 195 196 case 'pow': 197 // (f^g)' = f^g*(f'g/f + g' log(f)) 198 newNode = this.createNode('node_op', 'op_mul', 199 this.createNode('node_op', 'op_execfun', 200 Type.deepCopy(node.children[0]), 201 Type.deepCopy(node.children[1]) 202 ), 203 this.createNode('node_op', 'op_add', 204 this.createNode('node_op', 'op_mul', 205 this.derivative(node.children[1][0], varname), 206 this.createNode('node_op', 'op_div', 207 Type.deepCopy(node.children[1][1]), 208 Type.deepCopy(node.children[1][0]) 209 ) 210 ), 211 this.createNode('node_op', 'op_mul', 212 this.derivative(node.children[1][1], varname), 213 this.createNode('node_op', 'op_execfun', 214 this.createNode('node_var', 'log'), 215 [Type.deepCopy(node.children[1][0])] 216 ) 217 ) 218 ) 219 ); 220 break; 221 222 case 'log': 223 case 'ln': 224 newNode = this.createNode('node_op', 'op_div', 225 this.createNode('node_const', 1.0), 226 // Attention: single variable mode 227 Type.deepCopy(arg[0]) 228 ); 229 break; 230 231 case 'log2': 232 case 'lb': 233 case 'ld': 234 newNode = this.createNode('node_op', 'op_mul', 235 this.createNode('node_op', 'op_div', 236 this.createNode('node_const', 1.0), 237 // Attention: single variable mode 238 Type.deepCopy(arg[0]) 239 ), 240 this.createNode('node_const', 1.4426950408889634) // 1/log(2) 241 ); 242 break; 243 244 case 'log10': 245 case 'lg': 246 newNode = this.createNode('node_op', 'op_mul', 247 this.createNode('node_op', 'op_div', 248 this.createNode('node_const', 1.0), 249 // Attention: single variable mode 250 Type.deepCopy(arg[0]) 251 ), 252 this.createNode('node_const', 0.43429448190325176) // 1/log(10) 253 ); 254 break; 255 256 case 'asin': 257 newNode = this.createNode('node_op', 'op_div', 258 this.createNode('node_const', 1.0), 259 this.createNode('node_op', 'op_execfun', 260 this.createNode('node_var', 'sqrt'), 261 [ 262 this.createNode('node_op', 'op_sub', 263 this.createNode('node_const', 1.0), 264 this.createNode('node_op', 'op_mul', 265 Type.deepCopy(arg[0]), 266 Type.deepCopy(arg[0]) 267 ) 268 ) 269 ] 270 ) 271 ); 272 break; 273 274 case 'acos': 275 newNode = this.createNode('node_op', 'op_neg', 276 this.createNode('node_op', 'op_div', 277 this.createNode('node_const', 1.0), 278 this.createNode('node_op', 'op_execfun', 279 this.createNode('node_var', 'sqrt'), 280 [ 281 this.createNode('node_op', 'op_sub', 282 this.createNode('node_const', 1.0), 283 this.createNode('node_op', 'op_mul', 284 Type.deepCopy(arg[0]), 285 Type.deepCopy(arg[0]) 286 ) 287 ) 288 ] 289 ) 290 ) 291 ); 292 break; 293 294 //case 'atan2': 295 296 case 'atan': 297 newNode = this.createNode('node_op', 'op_div', 298 this.createNode('node_const', 1.0), 299 this.createNode('node_op', 'op_add', 300 this.createNode('node_const', 1.0), 301 this.createNode('node_op', 'op_mul', 302 Type.deepCopy(arg[0]), 303 Type.deepCopy(arg[0]) 304 ) 305 ) 306 ); 307 break; 308 309 case 'acot': 310 newNode = this.createNode('node_op', 'op_neg', 311 this.createNode('node_op', 'op_div', 312 this.createNode('node_const', 1.0), 313 this.createNode('node_op', 'op_add', 314 this.createNode('node_const', 1.0), 315 this.createNode('node_op', 'op_mul', 316 Type.deepCopy(arg[0]), 317 Type.deepCopy(arg[0]) 318 ) 319 ) 320 ) 321 ); 322 break; 323 324 case 'sinh': 325 newNode = this.createNode('node_op', 'op_execfun', 326 this.createNode('node_var', 'cosh'), 327 [Type.deepCopy(arg[0])] 328 ); 329 break; 330 331 case 'cosh': 332 newNode = this.createNode('node_op', 'op_execfun', 333 this.createNode('node_var', 'sinh'), 334 [Type.deepCopy(arg[0])] 335 ); 336 break; 337 338 case 'tanh': 339 newNode = this.createNode('node_op', 'op_sub', 340 this.createNode('node_const', 1.0), 341 this.createNode('node_op', 'op_exp', 342 this.createNode('node_op', 'op_execfun', 343 this.createNode('node_var', 'tanh'), 344 [Type.deepCopy(arg[0])] 345 ), 346 this.createNode('node_const', 2.0) 347 ) 348 ); 349 break; 350 351 case 'asinh': 352 newNode = this.createNode('node_op', 'op_div', 353 this.createNode('node_const', 1.0), 354 this.createNode('node_op', 'op_execfun', 355 this.createNode('node_var', 'sqrt'), 356 [ 357 this.createNode('node_op', 'op_add', 358 this.createNode('node_op', 'op_mul', 359 Type.deepCopy(arg[0]), 360 Type.deepCopy(arg[0]) 361 ), 362 this.createNode('node_const', 1.0) 363 ) 364 ] 365 ) 366 ); 367 break; 368 369 case 'acosh': 370 newNode = this.createNode('node_op', 'op_div', 371 this.createNode('node_const', 1.0), 372 this.createNode('node_op', 'op_execfun', 373 this.createNode('node_var', 'sqrt'), 374 [ 375 this.createNode('node_op', 'op_sub', 376 this.createNode('node_op', 'op_mul', 377 Type.deepCopy(arg[0]), 378 Type.deepCopy(arg[0]) 379 ), 380 this.createNode('node_const', 1.0) 381 ) 382 ] 383 ) 384 ); 385 break; 386 387 case 'atanh': 388 newNode = this.createNode('node_op', 'op_div', 389 this.createNode('node_const', 1.0), 390 this.createNode('node_op', 'op_sub', 391 this.createNode('node_const', 1.0), 392 this.createNode('node_op', 'op_mul', 393 Type.deepCopy(arg[0]), 394 Type.deepCopy(arg[0]) 395 ) 396 ) 397 ); 398 break; 399 400 default: 401 newNode = this.createNode('node_const', 0.0); 402 console.log('Derivative of "' + fun + '" not yet implemented'); 403 throw new Error('Error(' + this.line + '): '); 404 // this._error('Derivative of "' + fun + '" not yet implemented'); 405 406 } 407 408 return newNode; 409 }, 410 411 derivative: function (node, varname) { 412 var newNode; 413 414 switch (node.type) { 415 case 'node_op': 416 switch (node.value) { 417 /* 418 case 'op_map': 419 if (true) { 420 newNode = this.createNode('node_op', 'op_map', 421 Type.deepCopy(node.children[0]), 422 this.derivative(node.children[1], varname) 423 ); 424 } else { 425 newNode = this.derivative(node.children[1], varname); 426 } 427 break; 428 */ 429 case 'op_execfun': 430 // f'(g(x))g'(x) 431 if (node.children[0].value == 'pow') { 432 newNode = this.deriveElementary(node, varname); 433 } else { 434 if (node.children[1].length === 0) { 435 newNode = this.createNode('node_const', 0.0); 436 } else { 437 newNode = this.createNode('node_op', 'op_mul', 438 this.deriveElementary(node, varname), 439 // Warning: single variable mode 440 this.derivative(node.children[1][0], varname) 441 ); 442 } 443 } 444 break; 445 446 case 'op_div': 447 // (f'g − g'f )/(g*g) 448 newNode = this.createNode('node_op', 'op_div', 449 this.createNode('node_op', 'op_sub', 450 this.createNode('node_op', 'op_mul', 451 this.derivative(node.children[0], varname), 452 Type.deepCopy(node.children[1]) 453 ), 454 this.createNode('node_op', 'op_mul', 455 Type.deepCopy(node.children[0]), 456 this.derivative(node.children[1], varname) 457 ) 458 ), 459 this.createNode('node_op', 'op_mul', 460 Type.deepCopy(node.children[1]), 461 Type.deepCopy(node.children[1]) 462 ) 463 ); 464 break; 465 466 case 'op_mul': 467 // fg' + f'g 468 newNode = this.createNode('node_op', 'op_add', 469 this.createNode('node_op', 'op_mul', 470 Type.deepCopy(node.children[0]), 471 this.derivative(node.children[1], varname)), 472 this.createNode('node_op', 'op_mul', 473 this.derivative(node.children[0], varname), 474 Type.deepCopy(node.children[1])) 475 ); 476 break; 477 478 case 'op_neg': 479 newNode = this.createNode('node_op', 'op_neg', 480 this.derivative(node.children[0], varname) 481 ); 482 break; 483 484 case 'op_add': 485 case 'op_sub': 486 newNode = this.createNode('node_op', node.value, 487 this.derivative(node.children[0], varname), 488 this.derivative(node.children[1], varname) 489 ); 490 break; 491 492 case 'op_exp': 493 // (f^g)' = f^g*(f'g/f + g' log(f)) 494 newNode = this.createNode('node_op', 'op_mul', 495 Type.deepCopy(node), 496 this.createNode('node_op', 'op_add', 497 this.createNode('node_op', 'op_mul', 498 this.derivative(node.children[0], varname), 499 this.createNode('node_op', 'op_div', 500 Type.deepCopy(node.children[1]), 501 Type.deepCopy(node.children[0]) 502 ) 503 ), 504 this.createNode('node_op', 'op_mul', 505 this.derivative(node.children[1], varname), 506 this.createNode('node_op', 'op_execfun', 507 this.createNode('node_var', 'log'), 508 [Type.deepCopy(node.children[0])] 509 ) 510 ) 511 ) 512 ); 513 break; 514 } 515 break; 516 517 case 'node_var': 518 //console.log('node_var', node); 519 if (node.value === varname) { 520 newNode = this.createNode('node_const', 1.0); 521 } else { 522 newNode = this.createNode('node_const', 0.0); 523 } 524 break; 525 526 case 'node_const': 527 newNode = this.createNode('node_const', 0.0); 528 break; 529 530 case 'node_const_bool': 531 break; 532 533 case 'node_str': 534 break; 535 536 } 537 538 return newNode; 539 }, 540 541 /** 542 * f = map (x) -> x*sin(x); 543 * Usages: 544 * h = D(f, x); 545 * h = map (x) -> D(f, x); 546 * 547 */ 548 expandDerivatives: function (node, parent, ast) { 549 var len, i, j, mapNode, codeNode, ret, node2, newNode, 550 mapName, varname, vArray, order; 551 552 ret = 0; 553 if (!node) { 554 return ret; 555 } 556 557 this.line = node.line; 558 this.col = node.col; 559 560 // First we have to go down in the tree. 561 // This ensures that in cases like D(D(f,x),x) the inner D is expanded first. 562 len = node.children.length; 563 for (i = 0; i < len; ++i) { 564 if (node.children[i] && node.children[i].type) { 565 node.children[i] = this.expandDerivatives(node.children[i], node, ast); 566 } else if (Type.isArray(node.children[i])) { 567 for (j = 0; j < node.children[i].length; ++j) { 568 if (node.children[i][j] && node.children[i][j].type) { 569 node.children[i][j] = this.expandDerivatives(node.children[i][j], node, ast); 570 } 571 } 572 } 573 } 574 575 switch (node.type) { 576 case 'node_op': 577 switch (node.value) { 578 case 'op_execfun': 579 if (node.children[0] && node.children[0].value === 'D') { 580 if (node.children[1][0].type == 'node_var') { 581 /* 582 * Derive map, that is compute D(f,x) 583 * where e.g. f = map (x) -> x^2 584 * 585 * First step: find node where the map is defined 586 */ 587 mapName = node.children[1][0].value; 588 mapNode = this.findMapNode(mapName, ast); 589 vArray = mapNode.children[0]; 590 591 // Variable name for differentiation 592 if (node.children[1].length >= 2) { 593 varname = node.children[1][1].value; 594 } else { 595 varname = mapNode.children[0][0]; // Usually it's 'x' 596 } 597 codeNode = mapNode.children[1]; 598 } else { 599 /* 600 * Derive expression, e.g. 601 * D(2*x, x) 602 */ 603 codeNode = node.children[1][0]; 604 vArray = ['x']; 605 606 // Variable name for differentiation and order 607 if (node.children[1].length >= 2) { 608 varname = node.children[1][1].value; 609 } else { 610 varname = 'x'; 611 } 612 } 613 614 // Differentiation order 615 if (node.children[1].length >= 3) { 616 order = node.children[1][2].value; 617 } else { 618 order = 1; 619 } 620 621 // Create node which contains the derivative 622 newNode = codeNode; 623 //newNode = this.removeTrivialNodes(newNode); 624 if (order >= 1) { 625 while (order >= 1) { 626 newNode = this.derivative(newNode, varname); 627 newNode = this.removeTrivialNodes(newNode); 628 order--; 629 } 630 } 631 632 // Replace the node containing e.g. D(f,x) by the derivative. 633 if (parent.type == 'node_op' && parent.value == 'op_assign') { 634 // If D is an assignment it has to be replaced by a map 635 // h = D(f, x) 636 node2 = this.createNode('node_op', 'op_map', 637 vArray, 638 newNode 639 ); 640 } else { 641 node2 = newNode; 642 } 643 644 this.setMath(node2); 645 node.type = node2.type; 646 node.value = node2.value; 647 node.children[0] = node2.children[0]; 648 node.children[1] = node2.children[1]; 649 } 650 } 651 break; 652 653 case 'node_var': 654 case 'node_const': 655 case 'node_const_bool': 656 case 'node_str': 657 break; 658 } 659 660 return node; 661 }, 662 663 removeTrivialNodes: function (node) { 664 var i, len, n0, n1, swap; 665 666 // In case of 'op_execfun' the children[1] node is an array. 667 if (Type.isArray(node)) { 668 len = node.length; 669 for (i = 0; i < len; ++i) { 670 node[i] = this.removeTrivialNodes(node[i]); 671 } 672 } 673 if (node.type != 'node_op' || !node.children) { 674 return node; 675 } 676 677 len = node.children.length; 678 for (i = 0; i < len; ++i) { 679 this.mayNotBeSimplified = false; 680 do { 681 node.children[i] = this.removeTrivialNodes(node.children[i]); 682 } while (this.mayNotBeSimplified); 683 684 } 685 686 switch (node.value) { 687 // Allow maps of the form 688 // map (x) -> x; 689 case 'op_map': 690 n0 = node.children[0]; 691 n1 = node.children[1]; 692 if (n1.type == 'node_var') { 693 for (i = 0; i < n0.length; ++i) { 694 // Allow maps of the form map(x) -> x 695 if (n0[i] == n1.value) { 696 n1.isMath = true; 697 break; 698 } 699 } 700 } 701 break; 702 703 // a + 0 -> a 704 // 0 + a -> a 705 case 'op_add': 706 n0 = node.children[0]; 707 n1 = node.children[1]; 708 if (n0.type == 'node_const' && n0.value === 0.0) { 709 return n1; 710 } 711 if (n1.type == 'node_const' && n1.value === 0.0) { 712 return n0; 713 } 714 715 // const + const -> const 716 if (n0.type == 'node_const' && n1.type == 'node_const') { 717 n0.value += n1.value; 718 return n0; 719 } 720 break; 721 722 // 1 * a = a 723 // a * 1 = a 724 // a * 0 = 0 725 // 0 * a = 0 726 // - * - = + 727 // Order children 728 case 'op_mul': 729 n0 = node.children[0]; 730 n1 = node.children[1]; 731 if (n0.type == 'node_const' && n0.value == 1.0) { 732 return n1; 733 } 734 if (n1.type == 'node_const' && n1.value == 1.0) { 735 return n0; 736 } 737 if (n0.type == 'node_const' && n0.value === 0.0) { 738 return n0; 739 } 740 if (n1.type == 'node_const' && n1.value === 0.0) { 741 return n1; 742 } 743 if (n1.type == 'node_const' && n1.value === 0.0) { 744 return n1; 745 } 746 747 // (-a) * (-b) -> a*b 748 if (n0.type == 'node_op' && n0.value == 'op_neg' && 749 n1.type == 'node_op' && n1.value == 'op_neg') { 750 node.children = [n0.children[0], n1.children[0]]; 751 this.mayNotBeSimplified = true; 752 return node; 753 } 754 // (-a) * b -> -(a*b) 755 if (n0.value == 'op_neg' && n1.value != 'op_neg') { 756 node.type = 'node_op'; 757 node.value = 'op_neg'; 758 node.children = [this.createNode('node_op', 'op_mul', n0.children[0], n1)]; 759 this.mayNotBeSimplified = true; 760 return node; 761 } 762 // a * (-b) -> -(a*b) 763 if (n0.value != 'op_neg' && n1.value == 'op_neg') { 764 node.type = 'node_op'; 765 node.value = 'op_neg'; 766 node.children = [this.createNode('node_op', 'op_mul', n0, n1.children[0])]; 767 this.mayNotBeSimplified = true; 768 return node; 769 } 770 // (1 / a) * b -> a / b 771 if (n0.value == 'op_div' && 772 n0.children[0].type == 'node_const' && n0.children[0].value == 1.0) { 773 node.type = 'node_op'; 774 node.value = 'op_div'; 775 node.children = [n1, n0.children[1]]; 776 this.mayNotBeSimplified = true; 777 return node; 778 } 779 // a * (1 / b) -> a / b 780 if (n1.value == 'op_div' && 781 n1.children[0].type == 'node_const' && n1.children[0].value == 1.0) { 782 node.type = 'node_op'; 783 node.value = 'op_div'; 784 node.children = [n0, n1.children[1]]; 785 this.mayNotBeSimplified = true; 786 return node; 787 } 788 789 // Order children 790 // a * const -> const * a 791 if (n0.type != 'node_const' && n1.type == 'node_const') { 792 node.children = [n1, n0]; 793 this.mayNotBeSimplified = true; 794 return node; 795 } 796 // a + (-const) -> -const * a 797 if (n0.type != 'node_const' && n1.type == 'node_op' && 798 n1.value == 'op_neg' && n1.children[0].type == 'node_const') { 799 node.children = [n1, n0]; 800 this.mayNotBeSimplified = true; 801 return node; 802 } 803 804 // a * var -> var * a 805 // a * fun -> fun * a 806 if (n0.type == 'node_op' && n0.value != 'op_execfun' && 807 (n1.type == 'node_var' || (n1.type == 'node_op' && n1.value == 'op_execfun'))) { 808 node.children = [n1, n0]; 809 this.mayNotBeSimplified = true; 810 return node; 811 } 812 813 // a + (-var) -> -var * a 814 if (n0.type != 'node_op' && n1.type == 'node_op' && 815 n1.value == 'op_neg' && n1.children[0].type == 'node_var') { 816 node.children = [n1, n0]; 817 this.mayNotBeSimplified = true; 818 return node; 819 } 820 // a * (const * b) -> const * (a*b) 821 // a * (const / b) -> const * (a/b) 822 if (n0.type != 'node_const' && n1.type == 'node_op' && 823 (n1.value == 'op_mul' || n1.value == 'op_div') && 824 n1.children[0].type == 'node_const') { 825 swap = n1.children[0]; 826 n1.children[0] = n0; 827 node.children = [swap, n1]; 828 this.mayNotBeSimplified = true; 829 return node; 830 } 831 832 // (const * a) * b -> const * (a * b) 833 if (n1.type != 'node_const' && n0.type == 'node_op' && 834 n0.value == 'op_mul' && 835 n0.children[0].type == 'node_const') { 836 node.children = [ 837 n0.children[0], 838 this.createNode('node_op', 'op_mul', n0.children[1], n1) 839 ]; 840 this.mayNotBeSimplified = true; 841 return node; 842 } 843 844 // const * const -> const 845 if (n0.type == 'node_const' && n1.type == 'node_const') { 846 n0.value *= n1.value; 847 return n0; 848 } 849 850 // const * (const * a) -> const * a 851 // const * (const / a) -> const / a 852 if (n0.type == 'node_const' && n1.type == 'node_op' && 853 (n1.value == 'op_mul' || n1.value == 'op_div') && 854 n1.children[0].type == 'node_const') { 855 n1.children[0].value *= n0.value; 856 return n1; 857 } 858 859 // a * a-> a^2 860 n0.hash = this.parser.compile(n0); 861 n1.hash = this.parser.compile(n1); 862 if (n0.hash === n1.hash) { 863 node.value = 'op_exp'; 864 node.children[1] = this.createNode('node_const', 2.0); 865 return node; 866 } 867 868 if (n0.type == 'node_const' && n1.type == 'node_op' && 869 (n1.value == 'op_mul' || n1.value == 'op_div') && 870 n1.children[0].type == 'node_const') { 871 n1.children[0].value *= n0.value; 872 return n1; 873 } 874 875 // a * a^b -> a^(b+1) 876 if (n1.type == 'node_op' && n1.value == 'op_exp') { 877 if (!n0.hash) { 878 n0.hash = this.parser.compile(n0); 879 } 880 if (!n1.children[0].hash) { 881 n1.children[0].hash = this.parser.compile(n1.children[0]); 882 } 883 if (n0.hash === n1.children[0].hash) { 884 n1.children[1] = this.createNode('node_op', 'op_add', 885 n1.children[1], 886 this.createNode('node_const', 1.0) 887 ); 888 this.mayNotBeSimplified = true; 889 return n1; 890 } 891 } 892 893 // a^b * a^c -> a^(b+c) 894 if (n0.type == 'node_op' && n0.value == 'op_exp' && 895 n1.type == 'node_op' && n1.value == 'op_exp') { 896 n0.children[0].hash = this.parser.compile(n0.children[0]); 897 n1.children[0].hash = this.parser.compile(n1.children[0]); 898 if (n0.children[0].hash === n1.children[0].hash) { 899 n0.children[1] = this.createNode('node_op', 'op_add', 900 n0.children[1], 901 n1.children[1] 902 ); 903 this.mayNotBeSimplified = true; 904 return n0; 905 } 906 } 907 908 break; 909 910 // 0 - a -> -a 911 // a - 0 -> a 912 // a - a -> 0 913 case 'op_sub': 914 n0 = node.children[0]; 915 n1 = node.children[1]; 916 if (n0.type == 'node_const' && n0.value === 0.0) { 917 node.value = 'op_neg'; 918 node.children[0] = n1; 919 return node; 920 } 921 if (n1.type == 'node_const' && n1.value === 0.0) { 922 return n0; 923 } 924 if (n0.type == 'node_const' && n1.type == 'node_const' && 925 n0.value == n1.value) { 926 return this.createNode('node_const', 0.0); 927 } 928 if (n0.type == 'node_var' && n1.type == 'node_var' && 929 n0.value == n1.value) { 930 return this.createNode('node_const', 0.0); 931 } 932 933 // const - const -> const 934 if (n0.type == 'node_const' && n1.type == 'node_const') { 935 n0.value -= n1.value; 936 return n0; 937 } 938 939 // const * a - const * a -> const * a 940 if (n0.type == 'node_op' && n0.value == 'op_mul' && 941 n1.type == 'node_op' && n1.value == 'op_mul') { 942 943 n0.children[1].hash = this.parser.compile(n0.children[1]); 944 n1.children[1].hash = this.parser.compile(n1.children[1]); 945 if (n0.children[1].hash === n1.children[1].hash) { 946 947 node.value = 'op_mul'; 948 node.children = [ 949 this.createNode('node_op', 'op_sub', 950 n0.children[0], 951 n1.children[0]), 952 n0.children[1] 953 ]; 954 this.mayNotBeSimplified = true; 955 return node; 956 } 957 } 958 // const * a - a -> (const - 1) * a 959 if (n0.type == 'node_op' && n0.value == 'op_mul') { 960 961 n0.children[1].hash = this.parser.compile(n0.children[1]); 962 n1.hash = this.parser.compile(n1); 963 if (n0.children[1].hash === n1.hash) { 964 965 node.value = 'op_mul'; 966 node.children = [ 967 this.createNode('node_op', 'op_sub', 968 n0.children[0], 969 this.createNode('node_const', 1.0)), 970 n1 971 ]; 972 this.mayNotBeSimplified = true; 973 return node; 974 } 975 } 976 // a - const*a -> (const - 1) * a 977 if (n1.type == 'node_op' && n1.value == 'op_mul') { 978 979 n1.children[1].hash = this.parser.compile(n1.children[1]); 980 n0.hash = this.parser.compile(n0); 981 if (n1.children[1].hash === n0.hash) { 982 983 node.value = 'op_mul'; 984 node.children = [ 985 this.createNode('node_op', 'op_sub', 986 this.createNode('node_const', 1.0), 987 n1.children[0]), 988 n0 989 ]; 990 this.mayNotBeSimplified = true; 991 return node; 992 } 993 } 994 995 break; 996 997 // -0 -> 0 998 // -(-b) = b 999 case 'op_neg': 1000 n0 = node.children[0]; 1001 if (n0.type == 'node_const' && n0.value === 0.0) { 1002 return n0; 1003 } 1004 if (n0.type == 'node_op' && n0.value == 'op_neg') { 1005 return n0.children[0]; 1006 } 1007 break; 1008 1009 // a / a -> 1, a != 0 1010 // 0 / a -> 0, a != 0 1011 // a / 0 -> Infinity, a != 0 1012 // 0 / 0 -> NaN, a == 0 1013 case 'op_div': 1014 n0 = node.children[0]; 1015 n1 = node.children[1]; 1016 if (n0.type == 'node_const' && n1.type == 'node_const' && 1017 n0.value == n1.value && n0.value !== 0) { 1018 n0.value = 1.0; 1019 return n0; 1020 } 1021 if (n0.type == 'node_const' && n0.value === 0 && 1022 n1.type == 'node_const' && n1.value !== 0) { 1023 n0.value = 0.0; 1024 return n0; 1025 } 1026 1027 // Risky: 0 / (something != 0) -> 0.0 1028 if (n0.type == 'node_const' && n0.value === 0 && 1029 (n1.type == 'node_op' || n1.type == 'node_var')) { 1030 node.type = 'node_const'; 1031 node.value = 0.0; 1032 return node; 1033 } 1034 1035 if (n0.type == 'node_var' && n1.type == 'node_var' && 1036 n0.value == n1.value) { 1037 return this.createNode('node_const', 1.0); 1038 } 1039 if (n0.type == 'node_const' && n0.value !== 0 && 1040 n1.type == 'node_const' && n1.value === 0) { 1041 if (n0.value > 0.0) { 1042 n0.value = Infinity; 1043 } else { 1044 n0.value = -Infinity; // Do we ever need this? 1045 } 1046 return n0; 1047 } 1048 1049 // (-a) / (-b) -> a/b 1050 if (n0.type == 'node_op' && n0.value == 'op_neg' && 1051 n1.type == 'node_op' && n1.value == 'op_neg') { 1052 node.children = [n0.children[0], n1.children[0]]; 1053 this.mayNotBeSimplified = true; 1054 return node; 1055 } 1056 // (-a) / b -> -(a/b) 1057 if (n0.value == 'op_neg' && n1.value != 'op_neg') { 1058 node.type = 'node_op'; 1059 node.value = 'op_neg'; 1060 node.children = [this.createNode('node_op', 'op_div', n0.children[0], n1)]; 1061 this.mayNotBeSimplified = true; 1062 return node; 1063 } 1064 // a / (-b) -> -(a/b) 1065 if (n0.value != 'op_neg' && n1.value == 'op_neg') { 1066 node.type = 'node_op'; 1067 node.value = 'op_neg'; 1068 node.children = [this.createNode('node_op', 'op_div', n0, n1.children[0])]; 1069 this.mayNotBeSimplified = true; 1070 return node; 1071 } 1072 1073 // a^b / a -> a^(b-1) 1074 if (n0.type == 'node_op' && n0.value == 'op_exp') { 1075 if (!n1.hash) { 1076 n1.hash = this.parser.compile(n1); 1077 } 1078 if (!n0.children[0].hash) { 1079 n0.children[0].hash = this.parser.compile(n0.children[0]); 1080 } 1081 if (n1.hash === n0.children[0].hash) { 1082 n0.children[1] = this.createNode('node_op', 'op_sub', 1083 n0.children[1], 1084 this.createNode('node_const', 1.0) 1085 ); 1086 this.mayNotBeSimplified = true; 1087 return n0; 1088 } 1089 } 1090 1091 // (const * a) / b -> const * (a / b) 1092 if (n1.type != 'node_const' && n0.type == 'node_op' && 1093 n0.value == 'op_mul' && 1094 n0.children[0].type == 'node_const') { 1095 node.value = 'op_mul'; 1096 node.children = [ 1097 n0.children[0], 1098 this.createNode('node_op', 'op_div', n0.children[1], n1) 1099 ]; 1100 this.mayNotBeSimplified = true; 1101 return node; 1102 } 1103 1104 // a^b / a^c -> a^(b-c) 1105 if (n0.type == 'node_op' && n0.value == 'op_exp' && 1106 n1.type == 'node_op' && n1.value == 'op_exp') { 1107 n0.children[0].hash = this.parser.compile(n0.children[0]); 1108 n1.children[0].hash = this.parser.compile(n1.children[0]); 1109 if (n0.children[0].hash === n1.children[0].hash) { 1110 n0.children[1] = this.createNode('node_op', 'op_sub', 1111 n0.children[1], 1112 n1.children[1] 1113 ); 1114 this.mayNotBeSimplified = true; 1115 return n0; 1116 } 1117 } 1118 1119 1120 break; 1121 1122 // a^0 = 1 1123 // a^1 -> a 1124 // 1^a -> 1 1125 // 0^a -> 0: a const != 0 1126 case 'op_exp': 1127 n0 = node.children[0]; 1128 n1 = node.children[1]; 1129 if (n1.type == 'node_const' && n1.value === 0.0) { 1130 n1.value = 1.0; 1131 return n1; 1132 } 1133 if (n1.type == 'node_const' && n1.value == 1.0) { 1134 return n0; 1135 } 1136 if (n0.type == 'node_const' && n0.value == 1.0) { 1137 return n0; 1138 } 1139 if (n0.type == 'node_const' && n0.value === 0.0 && 1140 n1.type == 'node_const' && n1.value !== 0.0) { 1141 return n0; 1142 } 1143 1144 // (a^b)^c -> a^(b*c) 1145 if (n0.type == 'node_op' && n0.value == 'op_exp') { 1146 node.children = [ 1147 n0.children[0], 1148 this.createNode('node_op', 'op_mul', 1149 n0.children[1], 1150 n1) 1151 ]; 1152 return node; 1153 } 1154 break; 1155 } 1156 1157 switch (node.value) { 1158 // const_1 + const_2 -> (const_1 + const_2) 1159 // a + a -> 2*a 1160 // a + (-b) = a - b 1161 case 'op_add': 1162 n0 = node.children[0]; 1163 n1 = node.children[1]; 1164 if (n0.type == 'node_const' && n1.type == 'node_const' && 1165 n0.value == n1.value) { 1166 n0.value += n1.value; 1167 return n0; 1168 } 1169 1170 if (n0.type == 'node_var' && n1.type == 'node_var' && 1171 n0.value == n1.value) { 1172 node.children[0] = this.createNode('node_const', 2.0); 1173 node.value = 'op_mul'; 1174 return node; 1175 } 1176 1177 if (n0.type == 'node_op' && n0.value == 'op_neg') { 1178 node.value = 'op_sub'; 1179 node.children[0] = n1; 1180 node.children[1] = n0.children[0]; 1181 this.mayNotBeSimplified = true; 1182 return node; 1183 } 1184 1185 if (n1.type == 'node_op' && n1.value == 'op_neg') { 1186 node.value = 'op_sub'; 1187 node.children[1] = n1.children[0]; 1188 this.mayNotBeSimplified = true; 1189 return node; 1190 } 1191 1192 // const * a + const * a -> const * a 1193 if (n0.type == 'node_op' && n0.value == 'op_mul' && 1194 n1.type == 'node_op' && n1.value == 'op_mul') { 1195 1196 n0.children[1].hash = this.parser.compile(n0.children[1]); 1197 n1.children[1].hash = this.parser.compile(n1.children[1]); 1198 if (n0.children[1].hash === n1.children[1].hash) { 1199 1200 node.value = 'op_mul'; 1201 node.children = [ 1202 this.createNode('node_op', 'op_add', 1203 n0.children[0], 1204 n1.children[0]), 1205 n0.children[1] 1206 ]; 1207 this.mayNotBeSimplified = true; 1208 return node; 1209 } 1210 } 1211 // const * a + a -> (const + 1) * a 1212 if (n0.type == 'node_op' && n0.value == 'op_mul') { 1213 1214 n0.children[1].hash = this.parser.compile(n0.children[1]); 1215 n1.hash = this.parser.compile(n1); 1216 if (n0.children[1].hash === n1.hash) { 1217 1218 node.value = 'op_mul'; 1219 node.children = [ 1220 this.createNode('node_op', 'op_add', 1221 n0.children[0], 1222 this.createNode('node_const', 1.0)), 1223 n1 1224 ]; 1225 this.mayNotBeSimplified = true; 1226 return node; 1227 } 1228 } 1229 // a + const*a -> (const + 1) * a 1230 if (n1.type == 'node_op' && n1.value == 'op_mul') { 1231 1232 n1.children[1].hash = this.parser.compile(n1.children[1]); 1233 n0.hash = this.parser.compile(n0); 1234 if (n1.children[1].hash === n0.hash) { 1235 1236 node.value = 'op_mul'; 1237 node.children = [ 1238 this.createNode('node_op', 'op_add', 1239 this.createNode('node_const', 1.0), 1240 n1.children[0]), 1241 n0 1242 ]; 1243 this.mayNotBeSimplified = true; 1244 return node; 1245 } 1246 } 1247 1248 break; 1249 1250 // a - (-b) = a + b 1251 case 'op_sub': 1252 n0 = node.children[0]; 1253 n1 = node.children[1]; 1254 if (n1.type == 'node_op' && n1.value == 'op_neg') { 1255 node.value = 'op_add'; 1256 node.children[1] = n1.children[0]; 1257 this.mayNotBeSimplified = true; 1258 return node; 1259 } 1260 break; 1261 1262 case 'op_execfun': 1263 return this.simplifyElementary(node); 1264 } 1265 1266 return node; 1267 }, 1268 1269 simplifyElementary: function (node) { 1270 var fun = node.children[0].value, 1271 arg = node.children[1]; 1272 1273 // Catch errors of the form sin() 1274 if (arg.length == 0) { 1275 return node; 1276 } 1277 1278 switch (fun) { 1279 // sin(0) -> 0 1280 // sin(PI) -> 0 1281 // sin (int * PI) -> 0 1282 // sin (PI * int) -> 0 1283 // Same for tan() 1284 case 'sin': 1285 case 'tan': 1286 if (arg[0].type == 'node_const' && arg[0].value === 0) { 1287 node.type = 'node_const'; 1288 node.value = 0.0; 1289 return node; 1290 } 1291 if (arg[0].type == 'node_var' && arg[0].value == 'PI') { 1292 node.type = 'node_const'; 1293 node.value = 0.0; 1294 return node; 1295 } 1296 if (arg[0].type == 'node_op' && arg[0].value == 'op_mul' && 1297 arg[0].children[0].type == 'node_const' && arg[0].children[0].value % 1 === 0 && 1298 arg[0].children[1].type == 'node_var' && arg[0].children[1].value == 'PI') { 1299 node.type = 'node_const'; 1300 node.value = 0.0; 1301 return node; 1302 } 1303 break; 1304 1305 // cos(0) -> 1.0 1306 // cos(PI) -> -1.0 1307 // cos(int * PI) -> +/- 1.0 1308 // cos(PI * int) -> +/- 1.0 1309 case 'cos': 1310 if (arg[0].type == 'node_const' && arg[0].value === 0) { 1311 node.type = 'node_const'; 1312 node.value = 1.0; 1313 return node; 1314 } 1315 if (arg[0].type == 'node_var' && arg[0].value == 'PI') { 1316 node.type = 'node_op'; 1317 node.value = 'op_neg'; 1318 node.children = [this.createNode('node_const', 1.0)]; 1319 return node; 1320 } 1321 /* 1322 if (arg[0].type == 'node_op' && arg[0].value == 'op_mul' && 1323 ((arg[0].children[0].type == 'node_const' && arg[0].children[0].value % 1 === 0 && 1324 arg[0].children[1].type == 'node_var' && arg[0].children[1].value == 'PI') || 1325 (arg[0].children[1].type == 'node_const' && arg[0].children[1].value % 1 === 0 && 1326 arg[0].children[0].type == 'node_var' && arg[0].children[0].value == 'PI'))) { 1327 node.type = 'node_const'; 1328 node.value = 1.0; 1329 return node; 1330 } 1331 */ 1332 break; 1333 1334 // exp(0) -> 1 1335 case 'exp': 1336 if (arg[0].type == 'node_const' && arg[0].value === 0) { 1337 node.type = 'node_const'; 1338 node.value = 1.0; 1339 return node; 1340 } 1341 break; 1342 1343 // pow(a, 0) -> 1 1344 case 'pow': 1345 if (arg[1].type == 'node_const' && arg[1].value === 0) { 1346 node.type = 'node_const'; 1347 node.value = 1.0; 1348 return node; 1349 } 1350 break; 1351 1352 } 1353 1354 return node; 1355 } 1356 1357 }); 1358 1359 return JXG.CA; 1360 }); 1361