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 <https://www.gnu.org/licenses/> 25 and <https://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 /** 33 * @fileoverview Here, the computer algebra algorithms are implemented. 34 */ 35 36 import JXG from "../jxg.js"; 37 import Type from "../utils/type.js"; 38 // import Const from "../base/constants.js"; 39 // import Text from "../base/text.js"; 40 // import Mat from "../math/math.js"; 41 // import Geometry from "../math/geometry.js"; 42 // import Statistics from "../math/statistics.js"; 43 // import Env from "../utils/env.js"; 44 45 /** 46 * A JessieCode object provides an interface to the parser and stores all variables and objects used within a JessieCode script. 47 * The optional argument <tt>code</tt> is interpreted after initializing. To evaluate more code after initializing a JessieCode instance 48 * please use {@link JXG.JessieCode#parse}. For code snippets like single expressions use {@link JXG.JessieCode#snippet}. 49 * @constructor 50 * @param {String} [code] Code to parse. 51 * @param {Boolean} [geonext=false] Geonext compatibility mode. 52 */ 53 JXG.CA = function (node, createNode, parser) { 54 this.node = node; 55 this.createNode = createNode; 56 this.parser = parser; 57 }; 58 59 JXG.extend( 60 JXG.CA.prototype, 61 /** @lends JXG.CA.prototype */ { 62 findMapNode: function (mapname, node) { 63 var i, len, ret; 64 65 //console.log("FINDMAP", node); 66 if (node.value === "op_assign" && node.children[0].value === mapname) { 67 return node.children[1]; 68 } else if (node.children) { 69 len = node.children.length; 70 for (i = 0; i < len; ++i) { 71 ret = this.findMapNode(mapname, node.children[i]); 72 if (ret !== null) { 73 return ret; 74 } 75 } 76 } 77 return null; 78 }, 79 80 /** 81 * Declare all subnodes as math nodes, 82 * i.e recursively set node.isMath = true; 83 */ 84 setMath: function (node) { 85 var i, len; 86 87 if ( 88 (node.type == "node_op" && 89 (node.value == "op_add" || 90 node.value == "op_sub" || 91 node.value == "op_mul" || 92 node.value == "op_div" || 93 node.value == "op_neg" || 94 node.value == "op_execfun" || 95 node.value == "op_exp")) || 96 node.type == "node_var" || 97 node.type == "node_const" 98 ) { 99 node.isMath = true; 100 } 101 if (node.children) { 102 len = node.children.length; 103 for (i = 0; i < len; ++i) { 104 this.setMath(node.children[i]); 105 } 106 } 107 }, 108 109 deriveElementary: function (node, varname) { 110 var fun = node.children[0].value, 111 arg = node.children[1], 112 newNode; 113 114 switch (fun) { 115 case "abs": 116 // x / sqrt(x * x) 117 newNode = this.createNode( 118 "node_op", 119 "op_div", 120 arg[0], 121 this.createNode( 122 "node_op", 123 "op_execfun", 124 this.createNode("node_var", "sqrt"), 125 [ 126 this.createNode( 127 "node_op", 128 "op_mul", 129 Type.deepCopy(arg[0]), 130 Type.deepCopy(arg[0]) 131 ) 132 ] 133 ) 134 ); 135 break; 136 137 case "sqrt": 138 newNode = this.createNode( 139 "node_op", 140 "op_div", 141 this.createNode("node_const", 1.0), 142 this.createNode( 143 "node_op", 144 "op_mul", 145 this.createNode("node_const", 2.0), 146 this.createNode( 147 node.type, 148 node.value, 149 Type.deepCopy(node.children[0]), 150 Type.deepCopy(node.children[1]) 151 ) 152 ) 153 ); 154 break; 155 156 case "sin": 157 newNode = this.createNode( 158 "node_op", 159 "op_execfun", 160 this.createNode("node_var", "cos"), 161 Type.deepCopy(arg) 162 ); 163 break; 164 165 case "cos": 166 newNode = this.createNode( 167 "node_op", 168 "op_neg", 169 this.createNode( 170 "node_op", 171 "op_execfun", 172 this.createNode("node_var", "sin"), 173 Type.deepCopy(arg) 174 ) 175 ); 176 break; 177 178 case "tan": 179 newNode = this.createNode( 180 "node_op", 181 "op_div", 182 this.createNode("node_const", 1.0), 183 this.createNode( 184 "node_op", 185 "op_exp", 186 this.createNode( 187 "node_op", 188 "op_execfun", 189 this.createNode("node_var", "cos"), 190 Type.deepCopy(arg) 191 ), 192 this.createNode("node_const", 2) 193 ) 194 ); 195 break; 196 197 case "cot": 198 newNode = this.createNode( 199 "node_op", 200 "op_neg", 201 this.createNode( 202 "node_op", 203 "op_div", 204 this.createNode("node_const", 1.0), 205 this.createNode( 206 "node_op", 207 "op_exp", 208 this.createNode( 209 "node_op", 210 "op_execfun", 211 this.createNode("node_var", "sin"), 212 Type.deepCopy(arg) 213 ), 214 this.createNode("node_const", 2) 215 ) 216 ) 217 ); 218 break; 219 220 case "exp": 221 newNode = this.createNode( 222 node.type, 223 node.value, 224 Type.deepCopy(node.children[0]), 225 Type.deepCopy(node.children[1]) 226 ); 227 break; 228 229 case "pow": 230 // (f^g)' = f^g*(f'g/f + g' log(f)) 231 newNode = this.createNode( 232 "node_op", 233 "op_mul", 234 this.createNode( 235 "node_op", 236 "op_execfun", 237 Type.deepCopy(node.children[0]), 238 Type.deepCopy(node.children[1]) 239 ), 240 this.createNode( 241 "node_op", 242 "op_add", 243 this.createNode( 244 "node_op", 245 "op_mul", 246 this.derivative(node.children[1][0], varname), 247 this.createNode( 248 "node_op", 249 "op_div", 250 Type.deepCopy(node.children[1][1]), 251 Type.deepCopy(node.children[1][0]) 252 ) 253 ), 254 this.createNode( 255 "node_op", 256 "op_mul", 257 this.derivative(node.children[1][1], varname), 258 this.createNode( 259 "node_op", 260 "op_execfun", 261 this.createNode("node_var", "log"), 262 [Type.deepCopy(node.children[1][0])] 263 ) 264 ) 265 ) 266 ); 267 break; 268 269 case "log": 270 case "ln": 271 newNode = this.createNode( 272 "node_op", 273 "op_div", 274 this.createNode("node_const", 1.0), 275 // Attention: single variable mode 276 Type.deepCopy(arg[0]) 277 ); 278 break; 279 280 case "log2": 281 case "lb": 282 case "ld": 283 newNode = this.createNode( 284 "node_op", 285 "op_mul", 286 this.createNode( 287 "node_op", 288 "op_div", 289 this.createNode("node_const", 1.0), 290 // Attention: single variable mode 291 Type.deepCopy(arg[0]) 292 ), 293 this.createNode("node_const", 1.4426950408889634) // 1/log(2) 294 ); 295 break; 296 297 case "log10": 298 case "lg": 299 newNode = this.createNode( 300 "node_op", 301 "op_mul", 302 this.createNode( 303 "node_op", 304 "op_div", 305 this.createNode("node_const", 1.0), 306 // Attention: single variable mode 307 Type.deepCopy(arg[0]) 308 ), 309 this.createNode("node_const", 0.43429448190325176) // 1/log(10) 310 ); 311 break; 312 313 case "asin": 314 newNode = this.createNode( 315 "node_op", 316 "op_div", 317 this.createNode("node_const", 1.0), 318 this.createNode( 319 "node_op", 320 "op_execfun", 321 this.createNode("node_var", "sqrt"), 322 [ 323 this.createNode( 324 "node_op", 325 "op_sub", 326 this.createNode("node_const", 1.0), 327 this.createNode( 328 "node_op", 329 "op_mul", 330 Type.deepCopy(arg[0]), 331 Type.deepCopy(arg[0]) 332 ) 333 ) 334 ] 335 ) 336 ); 337 break; 338 339 case "acos": 340 newNode = this.createNode( 341 "node_op", 342 "op_neg", 343 this.createNode( 344 "node_op", 345 "op_div", 346 this.createNode("node_const", 1.0), 347 this.createNode( 348 "node_op", 349 "op_execfun", 350 this.createNode("node_var", "sqrt"), 351 [ 352 this.createNode( 353 "node_op", 354 "op_sub", 355 this.createNode("node_const", 1.0), 356 this.createNode( 357 "node_op", 358 "op_mul", 359 Type.deepCopy(arg[0]), 360 Type.deepCopy(arg[0]) 361 ) 362 ) 363 ] 364 ) 365 ) 366 ); 367 break; 368 369 //case 'atan2': 370 371 case "atan": 372 newNode = this.createNode( 373 "node_op", 374 "op_div", 375 this.createNode("node_const", 1.0), 376 this.createNode( 377 "node_op", 378 "op_add", 379 this.createNode("node_const", 1.0), 380 this.createNode( 381 "node_op", 382 "op_mul", 383 Type.deepCopy(arg[0]), 384 Type.deepCopy(arg[0]) 385 ) 386 ) 387 ); 388 break; 389 390 case "acot": 391 newNode = this.createNode( 392 "node_op", 393 "op_neg", 394 this.createNode( 395 "node_op", 396 "op_div", 397 this.createNode("node_const", 1.0), 398 this.createNode( 399 "node_op", 400 "op_add", 401 this.createNode("node_const", 1.0), 402 this.createNode( 403 "node_op", 404 "op_mul", 405 Type.deepCopy(arg[0]), 406 Type.deepCopy(arg[0]) 407 ) 408 ) 409 ) 410 ); 411 break; 412 413 case "sinh": 414 newNode = this.createNode( 415 "node_op", 416 "op_execfun", 417 this.createNode("node_var", "cosh"), 418 [Type.deepCopy(arg[0])] 419 ); 420 break; 421 422 case "cosh": 423 newNode = this.createNode( 424 "node_op", 425 "op_execfun", 426 this.createNode("node_var", "sinh"), 427 [Type.deepCopy(arg[0])] 428 ); 429 break; 430 431 case "tanh": 432 newNode = this.createNode( 433 "node_op", 434 "op_sub", 435 this.createNode("node_const", 1.0), 436 this.createNode( 437 "node_op", 438 "op_exp", 439 this.createNode( 440 "node_op", 441 "op_execfun", 442 this.createNode("node_var", "tanh"), 443 [Type.deepCopy(arg[0])] 444 ), 445 this.createNode("node_const", 2.0) 446 ) 447 ); 448 break; 449 450 case "asinh": 451 newNode = this.createNode( 452 "node_op", 453 "op_div", 454 this.createNode("node_const", 1.0), 455 this.createNode( 456 "node_op", 457 "op_execfun", 458 this.createNode("node_var", "sqrt"), 459 [ 460 this.createNode( 461 "node_op", 462 "op_add", 463 this.createNode( 464 "node_op", 465 "op_mul", 466 Type.deepCopy(arg[0]), 467 Type.deepCopy(arg[0]) 468 ), 469 this.createNode("node_const", 1.0) 470 ) 471 ] 472 ) 473 ); 474 break; 475 476 case "acosh": 477 newNode = this.createNode( 478 "node_op", 479 "op_div", 480 this.createNode("node_const", 1.0), 481 this.createNode( 482 "node_op", 483 "op_execfun", 484 this.createNode("node_var", "sqrt"), 485 [ 486 this.createNode( 487 "node_op", 488 "op_sub", 489 this.createNode( 490 "node_op", 491 "op_mul", 492 Type.deepCopy(arg[0]), 493 Type.deepCopy(arg[0]) 494 ), 495 this.createNode("node_const", 1.0) 496 ) 497 ] 498 ) 499 ); 500 break; 501 502 case "atanh": 503 newNode = this.createNode( 504 "node_op", 505 "op_div", 506 this.createNode("node_const", 1.0), 507 this.createNode( 508 "node_op", 509 "op_sub", 510 this.createNode("node_const", 1.0), 511 this.createNode( 512 "node_op", 513 "op_mul", 514 Type.deepCopy(arg[0]), 515 Type.deepCopy(arg[0]) 516 ) 517 ) 518 ); 519 break; 520 521 default: 522 newNode = this.createNode("node_const", 0.0); 523 console.log('Derivative of "' + fun + '" not yet implemented'); 524 throw new Error("Error(" + this.line + "): "); 525 // this._error('Derivative of "' + fun + '" not yet implemented'); 526 } 527 528 return newNode; 529 }, 530 531 derivative: function (node, varname) { 532 var newNode; 533 534 switch (node.type) { 535 case "node_op": 536 switch (node.value) { 537 /* 538 case 'op_map': 539 if (true) { 540 newNode = this.createNode('node_op', 'op_map', 541 Type.deepCopy(node.children[0]), 542 this.derivative(node.children[1], varname) 543 ); 544 } else { 545 newNode = this.derivative(node.children[1], varname); 546 } 547 break; 548 */ 549 case "op_execfun": 550 // f'(g(x))g'(x) 551 if (node.children[0].value == "pow") { 552 newNode = this.deriveElementary(node, varname); 553 } else { 554 if (node.children[1].length === 0) { 555 newNode = this.createNode("node_const", 0.0); 556 } else { 557 newNode = this.createNode( 558 "node_op", 559 "op_mul", 560 this.deriveElementary(node, varname), 561 // Warning: single variable mode 562 this.derivative(node.children[1][0], varname) 563 ); 564 } 565 } 566 break; 567 568 case "op_div": 569 // (f'g − g'f )/(g*g) 570 newNode = this.createNode( 571 "node_op", 572 "op_div", 573 this.createNode( 574 "node_op", 575 "op_sub", 576 this.createNode( 577 "node_op", 578 "op_mul", 579 this.derivative(node.children[0], varname), 580 Type.deepCopy(node.children[1]) 581 ), 582 this.createNode( 583 "node_op", 584 "op_mul", 585 Type.deepCopy(node.children[0]), 586 this.derivative(node.children[1], varname) 587 ) 588 ), 589 this.createNode( 590 "node_op", 591 "op_mul", 592 Type.deepCopy(node.children[1]), 593 Type.deepCopy(node.children[1]) 594 ) 595 ); 596 break; 597 598 case "op_mul": 599 // fg' + f'g 600 newNode = this.createNode( 601 "node_op", 602 "op_add", 603 this.createNode( 604 "node_op", 605 "op_mul", 606 Type.deepCopy(node.children[0]), 607 this.derivative(node.children[1], varname) 608 ), 609 this.createNode( 610 "node_op", 611 "op_mul", 612 this.derivative(node.children[0], varname), 613 Type.deepCopy(node.children[1]) 614 ) 615 ); 616 break; 617 618 case "op_neg": 619 newNode = this.createNode( 620 "node_op", 621 "op_neg", 622 this.derivative(node.children[0], varname) 623 ); 624 break; 625 626 case "op_add": 627 case "op_sub": 628 newNode = this.createNode( 629 "node_op", 630 node.value, 631 this.derivative(node.children[0], varname), 632 this.derivative(node.children[1], varname) 633 ); 634 break; 635 636 case "op_exp": 637 // (f^g)' = f^g*(f'g/f + g' log(f)) 638 newNode = this.createNode( 639 "node_op", 640 "op_mul", 641 Type.deepCopy(node), 642 this.createNode( 643 "node_op", 644 "op_add", 645 this.createNode( 646 "node_op", 647 "op_mul", 648 this.derivative(node.children[0], varname), 649 this.createNode( 650 "node_op", 651 "op_div", 652 Type.deepCopy(node.children[1]), 653 Type.deepCopy(node.children[0]) 654 ) 655 ), 656 this.createNode( 657 "node_op", 658 "op_mul", 659 this.derivative(node.children[1], varname), 660 this.createNode( 661 "node_op", 662 "op_execfun", 663 this.createNode("node_var", "log"), 664 [Type.deepCopy(node.children[0])] 665 ) 666 ) 667 ) 668 ); 669 break; 670 } 671 break; 672 673 case "node_var": 674 //console.log('node_var', node); 675 if (node.value === varname) { 676 newNode = this.createNode("node_const", 1.0); 677 } else { 678 newNode = this.createNode("node_const", 0.0); 679 } 680 break; 681 682 case "node_const": 683 newNode = this.createNode("node_const", 0.0); 684 break; 685 686 case "node_const_bool": 687 break; 688 689 case "node_str": 690 break; 691 } 692 693 return newNode; 694 }, 695 696 /** 697 * f = map (x) -> x*sin(x); 698 * Usages: 699 * h = D(f, x); 700 * h = map (x) -> D(f, x); 701 * or 702 * D(x^2, x); 703 */ 704 expandDerivatives: function (node, parent, ast) { 705 var len, i, j, mapNode, codeNode, 706 ret, node2, newNode, mapName, 707 varname, vArray, order, isMap; 708 709 ret = 0; 710 if (!node) { 711 return ret; 712 } 713 714 this.line = node.line; 715 this.col = node.col; 716 717 // First we have to go down in the tree. 718 // This ensures that in cases like D(D(f,x),x) the inner D is expanded first. 719 len = node.children.length; 720 for (i = 0; i < len; ++i) { 721 if (node.children[i] && node.children[i].type) { 722 node.children[i] = this.expandDerivatives(node.children[i], node, ast); 723 } else if (Type.isArray(node.children[i])) { 724 for (j = 0; j < node.children[i].length; ++j) { 725 if (node.children[i][j] && node.children[i][j].type) { 726 node.children[i][j] = this.expandDerivatives( 727 node.children[i][j], 728 node, 729 ast 730 ); 731 } 732 } 733 } 734 } 735 736 switch (node.type) { 737 case "node_op": 738 switch (node.value) { 739 case "op_execfun": 740 if (node.children[0] && node.children[0].value === "D") { 741 /* 742 * Distinguish the cases: 743 * D(f, x) where f is map -> isMap = true 744 * and 745 * D(2*x, x), D(sin(x), x), ... -> isMap = false 746 */ 747 isMap = false; 748 if (node.children[1][0].type == "node_var") { 749 mapName = node.children[1][0].value; 750 mapNode = this.findMapNode(mapName, ast); 751 if (mapNode !== null) { 752 isMap = true; 753 } 754 } 755 756 if (isMap) { 757 /* 758 * Derivative of map, that is compute D(f,x) 759 * where e.g. f = map (x) -> x^2 760 * 761 * First step: find node where the map is defined 762 */ 763 // Already done above 764 // mapName = node.children[1][0].value; 765 // mapNode = this.findMapNode(mapName, ast); 766 vArray = mapNode.children[0]; 767 768 // Variable name for differentiation 769 if (node.children[1].length >= 2) { 770 varname = node.children[1][1].value; 771 } else { 772 varname = mapNode.children[0][0]; // Usually it's 'x' 773 } 774 codeNode = mapNode.children[1]; 775 } else { 776 /* 777 * Derivative of expression, e.g. 778 * D(2*x, x) or D(sin(x), x) 779 */ 780 codeNode = node.children[1][0]; 781 vArray = ["x"]; 782 783 // Variable name for differentiation and order 784 if (node.children[1].length >= 2) { 785 varname = node.children[1][1].value; 786 } else { 787 varname = "x"; 788 } 789 } 790 791 // Differentiation order 792 if (node.children[1].length >= 3) { 793 order = node.children[1][2].value; 794 } else { 795 order = 1; 796 } 797 798 // Create node which contains the derivative 799 newNode = codeNode; 800 //newNode = this.removeTrivialNodes(newNode); 801 if (order >= 1) { 802 while (order >= 1) { 803 newNode = this.derivative(newNode, varname); 804 newNode = this.removeTrivialNodes(newNode); 805 order--; 806 } 807 } 808 809 // Replace the node containing e.g. D(f,x) by the derivative. 810 if (parent.type == "node_op" && parent.value == "op_assign") { 811 // If D is an assignment it has to be replaced by a map 812 // h = D(f, x) 813 node2 = this.createNode( 814 "node_op", 815 "op_map", 816 vArray, 817 newNode 818 ); 819 } else { 820 node2 = newNode; 821 } 822 823 this.setMath(node2); 824 node.type = node2.type; 825 node.value = node2.value; 826 if (node2.children.length > 0) { 827 node.children[0] = node2.children[0]; 828 } 829 if (node2.children.length > 1) { 830 node.children[1] = node2.children[1]; 831 } 832 } 833 } 834 break; 835 836 case "node_var": 837 case "node_const": 838 case "node_const_bool": 839 case "node_str": 840 break; 841 } 842 843 return node; 844 }, 845 846 removeTrivialNodes: function (node) { 847 var i, len, n0, n1, swap; 848 849 // In case of 'op_execfun' the children[1] node is an array. 850 if (Type.isArray(node)) { 851 len = node.length; 852 for (i = 0; i < len; ++i) { 853 node[i] = this.removeTrivialNodes(node[i]); 854 } 855 } 856 if (node.type != "node_op" || !node.children) { 857 return node; 858 } 859 860 len = node.children.length; 861 for (i = 0; i < len; ++i) { 862 this.mayNotBeSimplified = false; 863 do { 864 node.children[i] = this.removeTrivialNodes(node.children[i]); 865 } while (this.mayNotBeSimplified); 866 } 867 868 switch (node.value) { 869 // Allow maps of the form 870 // map (x) -> x; 871 case "op_map": 872 n0 = node.children[0]; 873 n1 = node.children[1]; 874 if (n1.type == "node_var") { 875 for (i = 0; i < n0.length; ++i) { 876 // Allow maps of the form map(x) -> x 877 if (n0[i] == n1.value) { 878 n1.isMath = true; 879 break; 880 } 881 } 882 } 883 break; 884 885 // a + 0 -> a 886 // 0 + a -> a 887 case "op_add": 888 n0 = node.children[0]; 889 n1 = node.children[1]; 890 if (n0.type == "node_const" && n0.value === 0.0) { 891 return n1; 892 } 893 if (n1.type == "node_const" && n1.value === 0.0) { 894 return n0; 895 } 896 897 // const + const -> const 898 if (n0.type == "node_const" && n1.type == "node_const") { 899 n0.value += n1.value; 900 return n0; 901 } 902 break; 903 904 // 1 * a = a 905 // a * 1 = a 906 // a * 0 = 0 907 // 0 * a = 0 908 // - * - = + 909 // Order children 910 case "op_mul": 911 n0 = node.children[0]; 912 n1 = node.children[1]; 913 if (n0.type == "node_const" && n0.value == 1.0) { 914 return n1; 915 } 916 if (n1.type == "node_const" && n1.value == 1.0) { 917 return n0; 918 } 919 if (n0.type == "node_const" && n0.value === 0.0) { 920 return n0; 921 } 922 if (n1.type == "node_const" && n1.value === 0.0) { 923 return n1; 924 } 925 if (n1.type == "node_const" && n1.value === 0.0) { 926 return n1; 927 } 928 929 // (-a) * (-b) -> a*b 930 if ( 931 n0.type == "node_op" && 932 n0.value == "op_neg" && 933 n1.type == "node_op" && 934 n1.value == "op_neg" 935 ) { 936 node.children = [n0.children[0], n1.children[0]]; 937 this.mayNotBeSimplified = true; 938 return node; 939 } 940 // (-a) * b -> -(a*b) 941 if (n0.value == "op_neg" && n1.value != "op_neg") { 942 node.type = "node_op"; 943 node.value = "op_neg"; 944 node.children = [ 945 this.createNode("node_op", "op_mul", n0.children[0], n1) 946 ]; 947 this.mayNotBeSimplified = true; 948 return node; 949 } 950 // a * (-b) -> -(a*b) 951 if (n0.value != "op_neg" && n1.value == "op_neg") { 952 node.type = "node_op"; 953 node.value = "op_neg"; 954 node.children = [ 955 this.createNode("node_op", "op_mul", n0, n1.children[0]) 956 ]; 957 this.mayNotBeSimplified = true; 958 return node; 959 } 960 // (1 / a) * b -> a / b 961 if ( 962 n0.value == "op_div" && 963 n0.children[0].type == "node_const" && 964 n0.children[0].value == 1.0 965 ) { 966 node.type = "node_op"; 967 node.value = "op_div"; 968 node.children = [n1, n0.children[1]]; 969 this.mayNotBeSimplified = true; 970 return node; 971 } 972 // a * (1 / b) -> a / b 973 if ( 974 n1.value == "op_div" && 975 n1.children[0].type == "node_const" && 976 n1.children[0].value == 1.0 977 ) { 978 node.type = "node_op"; 979 node.value = "op_div"; 980 node.children = [n0, n1.children[1]]; 981 this.mayNotBeSimplified = true; 982 return node; 983 } 984 985 // Order children 986 // a * const -> const * a 987 if (n0.type != "node_const" && n1.type == "node_const") { 988 node.children = [n1, n0]; 989 this.mayNotBeSimplified = true; 990 return node; 991 } 992 // a + (-const) -> -const + a 993 if ( 994 n0.type != "node_const" && 995 n1.type == "node_op" && 996 n1.value == "op_neg" && 997 n1.children[0].type == "node_const" 998 ) { 999 node.children = [n1, n0]; 1000 this.mayNotBeSimplified = true; 1001 return node; 1002 } 1003 1004 // a * var -> var * a 1005 // a * fun -> fun * a 1006 if ( 1007 n0.type == "node_op" && 1008 n0.value != "op_execfun" && 1009 (n1.type == "node_var" || 1010 (n1.type == "node_op" && n1.value == "op_execfun")) 1011 ) { 1012 node.children = [n1, n0]; 1013 this.mayNotBeSimplified = true; 1014 return node; 1015 } 1016 1017 // a + (-var) -> -var + a 1018 if ( 1019 n0.type != "node_op" && 1020 n1.type == "node_op" && 1021 n1.value == "op_neg" && 1022 n1.children[0].type == "node_var" 1023 ) { 1024 node.children = [n1, n0]; 1025 this.mayNotBeSimplified = true; 1026 return node; 1027 } 1028 // a * (const * b) -> const * (a*b) 1029 // a * (const / b) -> const * (a/b) 1030 if ( 1031 n0.type != "node_const" && 1032 n1.type == "node_op" && 1033 (n1.value == "op_mul" || n1.value == "op_div") && 1034 n1.children[0].type == "node_const" 1035 ) { 1036 swap = n1.children[0]; 1037 n1.children[0] = n0; 1038 node.children = [swap, n1]; 1039 this.mayNotBeSimplified = true; 1040 return node; 1041 } 1042 1043 // (const * a) * b -> const * (a * b) 1044 if ( 1045 n1.type != "node_const" && 1046 n0.type == "node_op" && 1047 n0.value == "op_mul" && 1048 n0.children[0].type == "node_const" 1049 ) { 1050 node.children = [ 1051 n0.children[0], 1052 this.createNode("node_op", "op_mul", n0.children[1], n1) 1053 ]; 1054 this.mayNotBeSimplified = true; 1055 return node; 1056 } 1057 1058 // const * const -> const 1059 if (n0.type == "node_const" && n1.type == "node_const") { 1060 n0.value *= n1.value; 1061 return n0; 1062 } 1063 1064 // const * (const * a) -> const * a 1065 // const * (const / a) -> const / a 1066 if ( 1067 n0.type == "node_const" && 1068 n1.type == "node_op" && 1069 (n1.value == "op_mul" || n1.value == "op_div") && 1070 n1.children[0].type == "node_const" 1071 ) { 1072 n1.children[0].value *= n0.value; 1073 return n1; 1074 } 1075 1076 // a * a-> a^2 1077 n0.hash = this.parser.compile(n0); 1078 n1.hash = this.parser.compile(n1); 1079 if (n0.hash === n1.hash) { 1080 node.value = "op_exp"; 1081 node.children[1] = this.createNode("node_const", 2.0); 1082 return node; 1083 } 1084 1085 if ( 1086 n0.type == "node_const" && 1087 n1.type == "node_op" && 1088 (n1.value == "op_mul" || n1.value == "op_div") && 1089 n1.children[0].type == "node_const" 1090 ) { 1091 n1.children[0].value *= n0.value; 1092 return n1; 1093 } 1094 1095 // a * a^b -> a^(b+1) 1096 if (n1.type == "node_op" && n1.value == "op_exp") { 1097 if (!n0.hash) { 1098 n0.hash = this.parser.compile(n0); 1099 } 1100 if (!n1.children[0].hash) { 1101 n1.children[0].hash = this.parser.compile(n1.children[0]); 1102 } 1103 if (n0.hash === n1.children[0].hash) { 1104 n1.children[1] = this.createNode( 1105 "node_op", 1106 "op_add", 1107 n1.children[1], 1108 this.createNode("node_const", 1.0) 1109 ); 1110 this.mayNotBeSimplified = true; 1111 return n1; 1112 } 1113 } 1114 1115 // a^b * a^c -> a^(b+c) 1116 if ( 1117 n0.type == "node_op" && 1118 n0.value == "op_exp" && 1119 n1.type == "node_op" && 1120 n1.value == "op_exp" 1121 ) { 1122 n0.children[0].hash = this.parser.compile(n0.children[0]); 1123 n1.children[0].hash = this.parser.compile(n1.children[0]); 1124 if (n0.children[0].hash === n1.children[0].hash) { 1125 n0.children[1] = this.createNode( 1126 "node_op", 1127 "op_add", 1128 n0.children[1], 1129 n1.children[1] 1130 ); 1131 this.mayNotBeSimplified = true; 1132 return n0; 1133 } 1134 } 1135 1136 break; 1137 1138 // 0 - a -> -a 1139 // a - 0 -> a 1140 // a - a -> 0 1141 case "op_sub": 1142 n0 = node.children[0]; 1143 n1 = node.children[1]; 1144 if (n0.type == "node_const" && n0.value === 0.0) { 1145 node.value = "op_neg"; 1146 node.children[0] = n1; 1147 return node; 1148 } 1149 if (n1.type == "node_const" && n1.value === 0.0) { 1150 return n0; 1151 } 1152 if ( 1153 n0.type == "node_const" && 1154 n1.type == "node_const" && 1155 n0.value == n1.value 1156 ) { 1157 return this.createNode("node_const", 0.0); 1158 } 1159 if ( 1160 n0.type == "node_var" && 1161 n1.type == "node_var" && 1162 n0.value == n1.value 1163 ) { 1164 return this.createNode("node_const", 0.0); 1165 } 1166 1167 // const - const -> const 1168 if (n0.type == "node_const" && n1.type == "node_const") { 1169 n0.value -= n1.value; 1170 return n0; 1171 } 1172 1173 // const * a - const * a -> const * a 1174 if ( 1175 n0.type == "node_op" && 1176 n0.value == "op_mul" && 1177 n1.type == "node_op" && 1178 n1.value == "op_mul" 1179 ) { 1180 n0.children[1].hash = this.parser.compile(n0.children[1]); 1181 n1.children[1].hash = this.parser.compile(n1.children[1]); 1182 if (n0.children[1].hash === n1.children[1].hash) { 1183 node.value = "op_mul"; 1184 node.children = [ 1185 this.createNode( 1186 "node_op", 1187 "op_sub", 1188 n0.children[0], 1189 n1.children[0] 1190 ), 1191 n0.children[1] 1192 ]; 1193 this.mayNotBeSimplified = true; 1194 return node; 1195 } 1196 } 1197 // const * a - a -> (const - 1) * a 1198 if (n0.type == "node_op" && n0.value == "op_mul") { 1199 n0.children[1].hash = this.parser.compile(n0.children[1]); 1200 n1.hash = this.parser.compile(n1); 1201 if (n0.children[1].hash === n1.hash) { 1202 node.value = "op_mul"; 1203 node.children = [ 1204 this.createNode( 1205 "node_op", 1206 "op_sub", 1207 n0.children[0], 1208 this.createNode("node_const", 1.0) 1209 ), 1210 n1 1211 ]; 1212 this.mayNotBeSimplified = true; 1213 return node; 1214 } 1215 } 1216 // a - const*a -> (const - 1) * a 1217 if (n1.type == "node_op" && n1.value == "op_mul") { 1218 n1.children[1].hash = this.parser.compile(n1.children[1]); 1219 n0.hash = this.parser.compile(n0); 1220 if (n1.children[1].hash === n0.hash) { 1221 node.value = "op_mul"; 1222 node.children = [ 1223 this.createNode( 1224 "node_op", 1225 "op_sub", 1226 this.createNode("node_const", 1.0), 1227 n1.children[0] 1228 ), 1229 n0 1230 ]; 1231 this.mayNotBeSimplified = true; 1232 return node; 1233 } 1234 } 1235 1236 break; 1237 1238 // -0 -> 0 1239 // -(-b) = b 1240 case "op_neg": 1241 n0 = node.children[0]; 1242 if (n0.type == "node_const" && n0.value === 0.0) { 1243 return n0; 1244 } 1245 if (n0.type == "node_op" && n0.value == "op_neg") { 1246 return n0.children[0]; 1247 } 1248 break; 1249 1250 // a / a -> 1, a != 0 1251 // 0 / a -> 0, a != 0 1252 // a / 0 -> Infinity, a != 0 1253 // 0 / 0 -> NaN, a == 0 1254 case "op_div": 1255 n0 = node.children[0]; 1256 n1 = node.children[1]; 1257 if ( 1258 n0.type == "node_const" && 1259 n1.type == "node_const" && 1260 n0.value == n1.value && 1261 n0.value !== 0 1262 ) { 1263 n0.value = 1.0; 1264 return n0; 1265 } 1266 if ( 1267 n0.type == "node_const" && 1268 n0.value === 0 && 1269 n1.type == "node_const" && 1270 n1.value !== 0 1271 ) { 1272 n0.value = 0.0; 1273 return n0; 1274 } 1275 1276 // Risky: 0 / (something != 0) -> 0.0 1277 if ( 1278 n0.type == "node_const" && 1279 n0.value === 0 && 1280 (n1.type == "node_op" || n1.type == "node_var") 1281 ) { 1282 node.type = "node_const"; 1283 node.value = 0.0; 1284 return node; 1285 } 1286 1287 if ( 1288 n0.type == "node_var" && 1289 n1.type == "node_var" && 1290 n0.value == n1.value 1291 ) { 1292 return this.createNode("node_const", 1.0); 1293 } 1294 if ( 1295 n0.type == "node_const" && 1296 n0.value !== 0 && 1297 n1.type == "node_const" && 1298 n1.value === 0 1299 ) { 1300 if (n0.value > 0.0) { 1301 n0.value = Infinity; 1302 } else { 1303 n0.value = -Infinity; // Do we ever need this? 1304 } 1305 return n0; 1306 } 1307 1308 // (-a) / (-b) -> a/b 1309 if ( 1310 n0.type == "node_op" && 1311 n0.value == "op_neg" && 1312 n1.type == "node_op" && 1313 n1.value == "op_neg" 1314 ) { 1315 node.children = [n0.children[0], n1.children[0]]; 1316 this.mayNotBeSimplified = true; 1317 return node; 1318 } 1319 // (-a) / b -> -(a/b) 1320 if (n0.value == "op_neg" && n1.value != "op_neg") { 1321 node.type = "node_op"; 1322 node.value = "op_neg"; 1323 node.children = [ 1324 this.createNode("node_op", "op_div", n0.children[0], n1) 1325 ]; 1326 this.mayNotBeSimplified = true; 1327 return node; 1328 } 1329 // a / (-b) -> -(a/b) 1330 if (n0.value != "op_neg" && n1.value == "op_neg") { 1331 node.type = "node_op"; 1332 node.value = "op_neg"; 1333 node.children = [ 1334 this.createNode("node_op", "op_div", n0, n1.children[0]) 1335 ]; 1336 this.mayNotBeSimplified = true; 1337 return node; 1338 } 1339 1340 // a^b / a -> a^(b-1) 1341 if (n0.type == "node_op" && n0.value == "op_exp") { 1342 if (!n1.hash) { 1343 n1.hash = this.parser.compile(n1); 1344 } 1345 if (!n0.children[0].hash) { 1346 n0.children[0].hash = this.parser.compile(n0.children[0]); 1347 } 1348 if (n1.hash === n0.children[0].hash) { 1349 n0.children[1] = this.createNode( 1350 "node_op", 1351 "op_sub", 1352 n0.children[1], 1353 this.createNode("node_const", 1.0) 1354 ); 1355 this.mayNotBeSimplified = true; 1356 return n0; 1357 } 1358 } 1359 1360 // (const * a) / b -> const * (a / b) 1361 if ( 1362 n1.type != "node_const" && 1363 n0.type == "node_op" && 1364 n0.value == "op_mul" && 1365 n0.children[0].type == "node_const" 1366 ) { 1367 node.value = "op_mul"; 1368 node.children = [ 1369 n0.children[0], 1370 this.createNode("node_op", "op_div", n0.children[1], n1) 1371 ]; 1372 this.mayNotBeSimplified = true; 1373 return node; 1374 } 1375 1376 // a^b / a^c -> a^(b-c) 1377 if ( 1378 n0.type == "node_op" && 1379 n0.value == "op_exp" && 1380 n1.type == "node_op" && 1381 n1.value == "op_exp" 1382 ) { 1383 n0.children[0].hash = this.parser.compile(n0.children[0]); 1384 n1.children[0].hash = this.parser.compile(n1.children[0]); 1385 if (n0.children[0].hash === n1.children[0].hash) { 1386 n0.children[1] = this.createNode( 1387 "node_op", 1388 "op_sub", 1389 n0.children[1], 1390 n1.children[1] 1391 ); 1392 this.mayNotBeSimplified = true; 1393 return n0; 1394 } 1395 } 1396 1397 break; 1398 1399 // a^0 = 1 1400 // a^1 -> a 1401 // 1^a -> 1 1402 // 0^a -> 0: a const != 0 1403 case "op_exp": 1404 n0 = node.children[0]; 1405 n1 = node.children[1]; 1406 if (n1.type == "node_const" && n1.value === 0.0) { 1407 n1.value = 1.0; 1408 return n1; 1409 } 1410 if (n1.type == "node_const" && n1.value == 1.0) { 1411 return n0; 1412 } 1413 if (n0.type == "node_const" && n0.value == 1.0) { 1414 return n0; 1415 } 1416 if ( 1417 n0.type == "node_const" && 1418 n0.value === 0.0 && 1419 n1.type == "node_const" && 1420 n1.value !== 0.0 1421 ) { 1422 return n0; 1423 } 1424 1425 // (a^b)^c -> a^(b*c) 1426 if (n0.type == "node_op" && n0.value == "op_exp") { 1427 node.children = [ 1428 n0.children[0], 1429 this.createNode("node_op", "op_mul", n0.children[1], n1) 1430 ]; 1431 return node; 1432 } 1433 break; 1434 } 1435 1436 switch (node.value) { 1437 // const_1 + const_2 -> (const_1 + const_2) 1438 // a + a -> 2*a 1439 // a + (-b) = a - b 1440 case "op_add": 1441 n0 = node.children[0]; 1442 n1 = node.children[1]; 1443 if ( 1444 n0.type == "node_const" && 1445 n1.type == "node_const" && 1446 n0.value == n1.value 1447 ) { 1448 n0.value += n1.value; 1449 return n0; 1450 } 1451 1452 if ( 1453 n0.type == "node_var" && 1454 n1.type == "node_var" && 1455 n0.value == n1.value 1456 ) { 1457 node.children[0] = this.createNode("node_const", 2.0); 1458 node.value = "op_mul"; 1459 return node; 1460 } 1461 1462 if (n0.type == "node_op" && n0.value == "op_neg") { 1463 node.value = "op_sub"; 1464 node.children[0] = n1; 1465 node.children[1] = n0.children[0]; 1466 this.mayNotBeSimplified = true; 1467 return node; 1468 } 1469 1470 if (n1.type == "node_op" && n1.value == "op_neg") { 1471 node.value = "op_sub"; 1472 node.children[1] = n1.children[0]; 1473 this.mayNotBeSimplified = true; 1474 return node; 1475 } 1476 1477 // const * a + const * a -> const * a 1478 if ( 1479 n0.type == "node_op" && 1480 n0.value == "op_mul" && 1481 n1.type == "node_op" && 1482 n1.value == "op_mul" 1483 ) { 1484 n0.children[1].hash = this.parser.compile(n0.children[1]); 1485 n1.children[1].hash = this.parser.compile(n1.children[1]); 1486 if (n0.children[1].hash === n1.children[1].hash) { 1487 node.value = "op_mul"; 1488 node.children = [ 1489 this.createNode( 1490 "node_op", 1491 "op_add", 1492 n0.children[0], 1493 n1.children[0] 1494 ), 1495 n0.children[1] 1496 ]; 1497 this.mayNotBeSimplified = true; 1498 return node; 1499 } 1500 } 1501 // const * a + a -> (const + 1) * a 1502 if (n0.type == "node_op" && n0.value == "op_mul") { 1503 n0.children[1].hash = this.parser.compile(n0.children[1]); 1504 n1.hash = this.parser.compile(n1); 1505 if (n0.children[1].hash === n1.hash) { 1506 node.value = "op_mul"; 1507 node.children = [ 1508 this.createNode( 1509 "node_op", 1510 "op_add", 1511 n0.children[0], 1512 this.createNode("node_const", 1.0) 1513 ), 1514 n1 1515 ]; 1516 this.mayNotBeSimplified = true; 1517 return node; 1518 } 1519 } 1520 // a + const*a -> (const + 1) * a 1521 if (n1.type == "node_op" && n1.value == "op_mul") { 1522 n1.children[1].hash = this.parser.compile(n1.children[1]); 1523 n0.hash = this.parser.compile(n0); 1524 if (n1.children[1].hash === n0.hash) { 1525 node.value = "op_mul"; 1526 node.children = [ 1527 this.createNode( 1528 "node_op", 1529 "op_add", 1530 this.createNode("node_const", 1.0), 1531 n1.children[0] 1532 ), 1533 n0 1534 ]; 1535 this.mayNotBeSimplified = true; 1536 return node; 1537 } 1538 } 1539 1540 break; 1541 1542 // a - (-b) = a + b 1543 case "op_sub": 1544 n0 = node.children[0]; 1545 n1 = node.children[1]; 1546 if (n1.type == "node_op" && n1.value == "op_neg") { 1547 node.value = "op_add"; 1548 node.children[1] = n1.children[0]; 1549 this.mayNotBeSimplified = true; 1550 return node; 1551 } 1552 break; 1553 1554 case "op_execfun": 1555 return this.simplifyElementary(node); 1556 } 1557 1558 return node; 1559 }, 1560 1561 simplifyElementary: function (node) { 1562 var fun = node.children[0].value, 1563 arg = node.children[1]; 1564 1565 // Catch errors of the form sin() 1566 if (arg.length == 0) { 1567 return node; 1568 } 1569 1570 switch (fun) { 1571 // sin(0) -> 0 1572 // sin(PI) -> 0 1573 // sin (int * PI) -> 0 1574 // sin (PI * int) -> 0 1575 // Same for tan() 1576 case "sin": 1577 case "tan": 1578 if (arg[0].type == "node_const" && arg[0].value === 0) { 1579 node.type = "node_const"; 1580 node.value = 0.0; 1581 return node; 1582 } 1583 if (arg[0].type == "node_var" && arg[0].value == "PI") { 1584 node.type = "node_const"; 1585 node.value = 0.0; 1586 return node; 1587 } 1588 if ( 1589 arg[0].type == "node_op" && 1590 arg[0].value == "op_mul" && 1591 arg[0].children[0].type == "node_const" && 1592 arg[0].children[0].value % 1 === 0 && 1593 arg[0].children[1].type == "node_var" && 1594 arg[0].children[1].value == "PI" 1595 ) { 1596 node.type = "node_const"; 1597 node.value = 0.0; 1598 return node; 1599 } 1600 break; 1601 1602 // cos(0) -> 1.0 1603 // cos(PI) -> -1.0 1604 // cos(int * PI) -> +/- 1.0 1605 // cos(PI * int) -> +/- 1.0 1606 case "cos": 1607 if (arg[0].type == "node_const" && arg[0].value === 0) { 1608 node.type = "node_const"; 1609 node.value = 1.0; 1610 return node; 1611 } 1612 if (arg[0].type == "node_var" && arg[0].value == "PI") { 1613 node.type = "node_op"; 1614 node.value = "op_neg"; 1615 node.children = [this.createNode("node_const", 1.0)]; 1616 return node; 1617 } 1618 /* 1619 if (arg[0].type == 'node_op' && arg[0].value == 'op_mul' && 1620 ((arg[0].children[0].type == 'node_const' && arg[0].children[0].value % 1 === 0 && 1621 arg[0].children[1].type == 'node_var' && arg[0].children[1].value == 'PI') || 1622 (arg[0].children[1].type == 'node_const' && arg[0].children[1].value % 1 === 0 && 1623 arg[0].children[0].type == 'node_var' && arg[0].children[0].value == 'PI'))) { 1624 node.type = 'node_const'; 1625 node.value = 1.0; 1626 return node; 1627 } 1628 */ 1629 break; 1630 1631 // exp(0) -> 1 1632 case "exp": 1633 if (arg[0].type == "node_const" && arg[0].value === 0) { 1634 node.type = "node_const"; 1635 node.value = 1.0; 1636 return node; 1637 } 1638 break; 1639 1640 // pow(a, 0) -> 1 1641 case "pow": 1642 if (arg[1].type == "node_const" && arg[1].value === 0) { 1643 node.type = "node_const"; 1644 node.value = 1.0; 1645 return node; 1646 } 1647 break; 1648 } 1649 1650 return node; 1651 } 1652 } 1653 ); 1654 1655 export default JXG.CA; 1656