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"; 37 import Type from "../utils/type"; 38 // import Const from "../base/constants"; 39 // import Text from "../base/text"; 40 // import Mat from "../math/math"; 41 // import Geometry from "../math/geometry"; 42 // import Statistics from "../math/statistics"; 43 // import Env from "../utils/env"; 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 * 702 */ 703 expandDerivatives: function (node, parent, ast) { 704 var len, 705 i, 706 j, 707 mapNode, 708 codeNode, 709 ret, 710 node2, 711 newNode, 712 mapName, 713 varname, 714 vArray, 715 order; 716 717 ret = 0; 718 if (!node) { 719 return ret; 720 } 721 722 this.line = node.line; 723 this.col = node.col; 724 725 // First we have to go down in the tree. 726 // This ensures that in cases like D(D(f,x),x) the inner D is expanded first. 727 len = node.children.length; 728 for (i = 0; i < len; ++i) { 729 if (node.children[i] && node.children[i].type) { 730 node.children[i] = this.expandDerivatives(node.children[i], node, ast); 731 } else if (Type.isArray(node.children[i])) { 732 for (j = 0; j < node.children[i].length; ++j) { 733 if (node.children[i][j] && node.children[i][j].type) { 734 node.children[i][j] = this.expandDerivatives( 735 node.children[i][j], 736 node, 737 ast 738 ); 739 } 740 } 741 } 742 } 743 744 switch (node.type) { 745 case "node_op": 746 switch (node.value) { 747 case "op_execfun": 748 if (node.children[0] && node.children[0].value === "D") { 749 if (node.children[1][0].type == "node_var") { 750 /* 751 * Derive map, that is compute D(f,x) 752 * where e.g. f = map (x) -> x^2 753 * 754 * First step: find node where the map is defined 755 */ 756 mapName = node.children[1][0].value; 757 mapNode = this.findMapNode(mapName, ast); 758 vArray = mapNode.children[0]; 759 760 // Variable name for differentiation 761 if (node.children[1].length >= 2) { 762 varname = node.children[1][1].value; 763 } else { 764 varname = mapNode.children[0][0]; // Usually it's 'x' 765 } 766 codeNode = mapNode.children[1]; 767 } else { 768 /* 769 * Derive expression, e.g. 770 * D(2*x, x) 771 */ 772 codeNode = node.children[1][0]; 773 vArray = ["x"]; 774 775 // Variable name for differentiation and order 776 if (node.children[1].length >= 2) { 777 varname = node.children[1][1].value; 778 } else { 779 varname = "x"; 780 } 781 } 782 783 // Differentiation order 784 if (node.children[1].length >= 3) { 785 order = node.children[1][2].value; 786 } else { 787 order = 1; 788 } 789 790 // Create node which contains the derivative 791 newNode = codeNode; 792 //newNode = this.removeTrivialNodes(newNode); 793 if (order >= 1) { 794 while (order >= 1) { 795 newNode = this.derivative(newNode, varname); 796 newNode = this.removeTrivialNodes(newNode); 797 order--; 798 } 799 } 800 801 // Replace the node containing e.g. D(f,x) by the derivative. 802 if (parent.type == "node_op" && parent.value == "op_assign") { 803 // If D is an assignment it has to be replaced by a map 804 // h = D(f, x) 805 node2 = this.createNode( 806 "node_op", 807 "op_map", 808 vArray, 809 newNode 810 ); 811 } else { 812 node2 = newNode; 813 } 814 815 this.setMath(node2); 816 node.type = node2.type; 817 node.value = node2.value; 818 node.children[0] = node2.children[0]; 819 node.children[1] = node2.children[1]; 820 } 821 } 822 break; 823 824 case "node_var": 825 case "node_const": 826 case "node_const_bool": 827 case "node_str": 828 break; 829 } 830 831 return node; 832 }, 833 834 removeTrivialNodes: function (node) { 835 var i, len, n0, n1, swap; 836 837 // In case of 'op_execfun' the children[1] node is an array. 838 if (Type.isArray(node)) { 839 len = node.length; 840 for (i = 0; i < len; ++i) { 841 node[i] = this.removeTrivialNodes(node[i]); 842 } 843 } 844 if (node.type != "node_op" || !node.children) { 845 return node; 846 } 847 848 len = node.children.length; 849 for (i = 0; i < len; ++i) { 850 this.mayNotBeSimplified = false; 851 do { 852 node.children[i] = this.removeTrivialNodes(node.children[i]); 853 } while (this.mayNotBeSimplified); 854 } 855 856 switch (node.value) { 857 // Allow maps of the form 858 // map (x) -> x; 859 case "op_map": 860 n0 = node.children[0]; 861 n1 = node.children[1]; 862 if (n1.type == "node_var") { 863 for (i = 0; i < n0.length; ++i) { 864 // Allow maps of the form map(x) -> x 865 if (n0[i] == n1.value) { 866 n1.isMath = true; 867 break; 868 } 869 } 870 } 871 break; 872 873 // a + 0 -> a 874 // 0 + a -> a 875 case "op_add": 876 n0 = node.children[0]; 877 n1 = node.children[1]; 878 if (n0.type == "node_const" && n0.value === 0.0) { 879 return n1; 880 } 881 if (n1.type == "node_const" && n1.value === 0.0) { 882 return n0; 883 } 884 885 // const + const -> const 886 if (n0.type == "node_const" && n1.type == "node_const") { 887 n0.value += n1.value; 888 return n0; 889 } 890 break; 891 892 // 1 * a = a 893 // a * 1 = a 894 // a * 0 = 0 895 // 0 * a = 0 896 // - * - = + 897 // Order children 898 case "op_mul": 899 n0 = node.children[0]; 900 n1 = node.children[1]; 901 if (n0.type == "node_const" && n0.value == 1.0) { 902 return n1; 903 } 904 if (n1.type == "node_const" && n1.value == 1.0) { 905 return n0; 906 } 907 if (n0.type == "node_const" && n0.value === 0.0) { 908 return n0; 909 } 910 if (n1.type == "node_const" && n1.value === 0.0) { 911 return n1; 912 } 913 if (n1.type == "node_const" && n1.value === 0.0) { 914 return n1; 915 } 916 917 // (-a) * (-b) -> a*b 918 if ( 919 n0.type == "node_op" && 920 n0.value == "op_neg" && 921 n1.type == "node_op" && 922 n1.value == "op_neg" 923 ) { 924 node.children = [n0.children[0], n1.children[0]]; 925 this.mayNotBeSimplified = true; 926 return node; 927 } 928 // (-a) * b -> -(a*b) 929 if (n0.value == "op_neg" && n1.value != "op_neg") { 930 node.type = "node_op"; 931 node.value = "op_neg"; 932 node.children = [ 933 this.createNode("node_op", "op_mul", n0.children[0], n1) 934 ]; 935 this.mayNotBeSimplified = true; 936 return node; 937 } 938 // a * (-b) -> -(a*b) 939 if (n0.value != "op_neg" && n1.value == "op_neg") { 940 node.type = "node_op"; 941 node.value = "op_neg"; 942 node.children = [ 943 this.createNode("node_op", "op_mul", n0, n1.children[0]) 944 ]; 945 this.mayNotBeSimplified = true; 946 return node; 947 } 948 // (1 / a) * b -> a / b 949 if ( 950 n0.value == "op_div" && 951 n0.children[0].type == "node_const" && 952 n0.children[0].value == 1.0 953 ) { 954 node.type = "node_op"; 955 node.value = "op_div"; 956 node.children = [n1, n0.children[1]]; 957 this.mayNotBeSimplified = true; 958 return node; 959 } 960 // a * (1 / b) -> a / b 961 if ( 962 n1.value == "op_div" && 963 n1.children[0].type == "node_const" && 964 n1.children[0].value == 1.0 965 ) { 966 node.type = "node_op"; 967 node.value = "op_div"; 968 node.children = [n0, n1.children[1]]; 969 this.mayNotBeSimplified = true; 970 return node; 971 } 972 973 // Order children 974 // a * const -> const * a 975 if (n0.type != "node_const" && n1.type == "node_const") { 976 node.children = [n1, n0]; 977 this.mayNotBeSimplified = true; 978 return node; 979 } 980 // a + (-const) -> -const * a 981 if ( 982 n0.type != "node_const" && 983 n1.type == "node_op" && 984 n1.value == "op_neg" && 985 n1.children[0].type == "node_const" 986 ) { 987 node.children = [n1, n0]; 988 this.mayNotBeSimplified = true; 989 return node; 990 } 991 992 // a * var -> var * a 993 // a * fun -> fun * a 994 if ( 995 n0.type == "node_op" && 996 n0.value != "op_execfun" && 997 (n1.type == "node_var" || 998 (n1.type == "node_op" && n1.value == "op_execfun")) 999 ) { 1000 node.children = [n1, n0]; 1001 this.mayNotBeSimplified = true; 1002 return node; 1003 } 1004 1005 // a + (-var) -> -var * a 1006 if ( 1007 n0.type != "node_op" && 1008 n1.type == "node_op" && 1009 n1.value == "op_neg" && 1010 n1.children[0].type == "node_var" 1011 ) { 1012 node.children = [n1, n0]; 1013 this.mayNotBeSimplified = true; 1014 return node; 1015 } 1016 // a * (const * b) -> const * (a*b) 1017 // a * (const / b) -> const * (a/b) 1018 if ( 1019 n0.type != "node_const" && 1020 n1.type == "node_op" && 1021 (n1.value == "op_mul" || n1.value == "op_div") && 1022 n1.children[0].type == "node_const" 1023 ) { 1024 swap = n1.children[0]; 1025 n1.children[0] = n0; 1026 node.children = [swap, n1]; 1027 this.mayNotBeSimplified = true; 1028 return node; 1029 } 1030 1031 // (const * a) * b -> const * (a * b) 1032 if ( 1033 n1.type != "node_const" && 1034 n0.type == "node_op" && 1035 n0.value == "op_mul" && 1036 n0.children[0].type == "node_const" 1037 ) { 1038 node.children = [ 1039 n0.children[0], 1040 this.createNode("node_op", "op_mul", n0.children[1], n1) 1041 ]; 1042 this.mayNotBeSimplified = true; 1043 return node; 1044 } 1045 1046 // const * const -> const 1047 if (n0.type == "node_const" && n1.type == "node_const") { 1048 n0.value *= n1.value; 1049 return n0; 1050 } 1051 1052 // const * (const * a) -> const * a 1053 // const * (const / a) -> const / a 1054 if ( 1055 n0.type == "node_const" && 1056 n1.type == "node_op" && 1057 (n1.value == "op_mul" || n1.value == "op_div") && 1058 n1.children[0].type == "node_const" 1059 ) { 1060 n1.children[0].value *= n0.value; 1061 return n1; 1062 } 1063 1064 // a * a-> a^2 1065 n0.hash = this.parser.compile(n0); 1066 n1.hash = this.parser.compile(n1); 1067 if (n0.hash === n1.hash) { 1068 node.value = "op_exp"; 1069 node.children[1] = this.createNode("node_const", 2.0); 1070 return node; 1071 } 1072 1073 if ( 1074 n0.type == "node_const" && 1075 n1.type == "node_op" && 1076 (n1.value == "op_mul" || n1.value == "op_div") && 1077 n1.children[0].type == "node_const" 1078 ) { 1079 n1.children[0].value *= n0.value; 1080 return n1; 1081 } 1082 1083 // a * a^b -> a^(b+1) 1084 if (n1.type == "node_op" && n1.value == "op_exp") { 1085 if (!n0.hash) { 1086 n0.hash = this.parser.compile(n0); 1087 } 1088 if (!n1.children[0].hash) { 1089 n1.children[0].hash = this.parser.compile(n1.children[0]); 1090 } 1091 if (n0.hash === n1.children[0].hash) { 1092 n1.children[1] = this.createNode( 1093 "node_op", 1094 "op_add", 1095 n1.children[1], 1096 this.createNode("node_const", 1.0) 1097 ); 1098 this.mayNotBeSimplified = true; 1099 return n1; 1100 } 1101 } 1102 1103 // a^b * a^c -> a^(b+c) 1104 if ( 1105 n0.type == "node_op" && 1106 n0.value == "op_exp" && 1107 n1.type == "node_op" && 1108 n1.value == "op_exp" 1109 ) { 1110 n0.children[0].hash = this.parser.compile(n0.children[0]); 1111 n1.children[0].hash = this.parser.compile(n1.children[0]); 1112 if (n0.children[0].hash === n1.children[0].hash) { 1113 n0.children[1] = this.createNode( 1114 "node_op", 1115 "op_add", 1116 n0.children[1], 1117 n1.children[1] 1118 ); 1119 this.mayNotBeSimplified = true; 1120 return n0; 1121 } 1122 } 1123 1124 break; 1125 1126 // 0 - a -> -a 1127 // a - 0 -> a 1128 // a - a -> 0 1129 case "op_sub": 1130 n0 = node.children[0]; 1131 n1 = node.children[1]; 1132 if (n0.type == "node_const" && n0.value === 0.0) { 1133 node.value = "op_neg"; 1134 node.children[0] = n1; 1135 return node; 1136 } 1137 if (n1.type == "node_const" && n1.value === 0.0) { 1138 return n0; 1139 } 1140 if ( 1141 n0.type == "node_const" && 1142 n1.type == "node_const" && 1143 n0.value == n1.value 1144 ) { 1145 return this.createNode("node_const", 0.0); 1146 } 1147 if ( 1148 n0.type == "node_var" && 1149 n1.type == "node_var" && 1150 n0.value == n1.value 1151 ) { 1152 return this.createNode("node_const", 0.0); 1153 } 1154 1155 // const - const -> const 1156 if (n0.type == "node_const" && n1.type == "node_const") { 1157 n0.value -= n1.value; 1158 return n0; 1159 } 1160 1161 // const * a - const * a -> const * a 1162 if ( 1163 n0.type == "node_op" && 1164 n0.value == "op_mul" && 1165 n1.type == "node_op" && 1166 n1.value == "op_mul" 1167 ) { 1168 n0.children[1].hash = this.parser.compile(n0.children[1]); 1169 n1.children[1].hash = this.parser.compile(n1.children[1]); 1170 if (n0.children[1].hash === n1.children[1].hash) { 1171 node.value = "op_mul"; 1172 node.children = [ 1173 this.createNode( 1174 "node_op", 1175 "op_sub", 1176 n0.children[0], 1177 n1.children[0] 1178 ), 1179 n0.children[1] 1180 ]; 1181 this.mayNotBeSimplified = true; 1182 return node; 1183 } 1184 } 1185 // const * a - a -> (const - 1) * a 1186 if (n0.type == "node_op" && n0.value == "op_mul") { 1187 n0.children[1].hash = this.parser.compile(n0.children[1]); 1188 n1.hash = this.parser.compile(n1); 1189 if (n0.children[1].hash === n1.hash) { 1190 node.value = "op_mul"; 1191 node.children = [ 1192 this.createNode( 1193 "node_op", 1194 "op_sub", 1195 n0.children[0], 1196 this.createNode("node_const", 1.0) 1197 ), 1198 n1 1199 ]; 1200 this.mayNotBeSimplified = true; 1201 return node; 1202 } 1203 } 1204 // a - const*a -> (const - 1) * a 1205 if (n1.type == "node_op" && n1.value == "op_mul") { 1206 n1.children[1].hash = this.parser.compile(n1.children[1]); 1207 n0.hash = this.parser.compile(n0); 1208 if (n1.children[1].hash === n0.hash) { 1209 node.value = "op_mul"; 1210 node.children = [ 1211 this.createNode( 1212 "node_op", 1213 "op_sub", 1214 this.createNode("node_const", 1.0), 1215 n1.children[0] 1216 ), 1217 n0 1218 ]; 1219 this.mayNotBeSimplified = true; 1220 return node; 1221 } 1222 } 1223 1224 break; 1225 1226 // -0 -> 0 1227 // -(-b) = b 1228 case "op_neg": 1229 n0 = node.children[0]; 1230 if (n0.type == "node_const" && n0.value === 0.0) { 1231 return n0; 1232 } 1233 if (n0.type == "node_op" && n0.value == "op_neg") { 1234 return n0.children[0]; 1235 } 1236 break; 1237 1238 // a / a -> 1, a != 0 1239 // 0 / a -> 0, a != 0 1240 // a / 0 -> Infinity, a != 0 1241 // 0 / 0 -> NaN, a == 0 1242 case "op_div": 1243 n0 = node.children[0]; 1244 n1 = node.children[1]; 1245 if ( 1246 n0.type == "node_const" && 1247 n1.type == "node_const" && 1248 n0.value == n1.value && 1249 n0.value !== 0 1250 ) { 1251 n0.value = 1.0; 1252 return n0; 1253 } 1254 if ( 1255 n0.type == "node_const" && 1256 n0.value === 0 && 1257 n1.type == "node_const" && 1258 n1.value !== 0 1259 ) { 1260 n0.value = 0.0; 1261 return n0; 1262 } 1263 1264 // Risky: 0 / (something != 0) -> 0.0 1265 if ( 1266 n0.type == "node_const" && 1267 n0.value === 0 && 1268 (n1.type == "node_op" || n1.type == "node_var") 1269 ) { 1270 node.type = "node_const"; 1271 node.value = 0.0; 1272 return node; 1273 } 1274 1275 if ( 1276 n0.type == "node_var" && 1277 n1.type == "node_var" && 1278 n0.value == n1.value 1279 ) { 1280 return this.createNode("node_const", 1.0); 1281 } 1282 if ( 1283 n0.type == "node_const" && 1284 n0.value !== 0 && 1285 n1.type == "node_const" && 1286 n1.value === 0 1287 ) { 1288 if (n0.value > 0.0) { 1289 n0.value = Infinity; 1290 } else { 1291 n0.value = -Infinity; // Do we ever need this? 1292 } 1293 return n0; 1294 } 1295 1296 // (-a) / (-b) -> a/b 1297 if ( 1298 n0.type == "node_op" && 1299 n0.value == "op_neg" && 1300 n1.type == "node_op" && 1301 n1.value == "op_neg" 1302 ) { 1303 node.children = [n0.children[0], n1.children[0]]; 1304 this.mayNotBeSimplified = true; 1305 return node; 1306 } 1307 // (-a) / b -> -(a/b) 1308 if (n0.value == "op_neg" && n1.value != "op_neg") { 1309 node.type = "node_op"; 1310 node.value = "op_neg"; 1311 node.children = [ 1312 this.createNode("node_op", "op_div", n0.children[0], n1) 1313 ]; 1314 this.mayNotBeSimplified = true; 1315 return node; 1316 } 1317 // a / (-b) -> -(a/b) 1318 if (n0.value != "op_neg" && n1.value == "op_neg") { 1319 node.type = "node_op"; 1320 node.value = "op_neg"; 1321 node.children = [ 1322 this.createNode("node_op", "op_div", n0, n1.children[0]) 1323 ]; 1324 this.mayNotBeSimplified = true; 1325 return node; 1326 } 1327 1328 // a^b / a -> a^(b-1) 1329 if (n0.type == "node_op" && n0.value == "op_exp") { 1330 if (!n1.hash) { 1331 n1.hash = this.parser.compile(n1); 1332 } 1333 if (!n0.children[0].hash) { 1334 n0.children[0].hash = this.parser.compile(n0.children[0]); 1335 } 1336 if (n1.hash === n0.children[0].hash) { 1337 n0.children[1] = this.createNode( 1338 "node_op", 1339 "op_sub", 1340 n0.children[1], 1341 this.createNode("node_const", 1.0) 1342 ); 1343 this.mayNotBeSimplified = true; 1344 return n0; 1345 } 1346 } 1347 1348 // (const * a) / b -> const * (a / b) 1349 if ( 1350 n1.type != "node_const" && 1351 n0.type == "node_op" && 1352 n0.value == "op_mul" && 1353 n0.children[0].type == "node_const" 1354 ) { 1355 node.value = "op_mul"; 1356 node.children = [ 1357 n0.children[0], 1358 this.createNode("node_op", "op_div", n0.children[1], n1) 1359 ]; 1360 this.mayNotBeSimplified = true; 1361 return node; 1362 } 1363 1364 // a^b / a^c -> a^(b-c) 1365 if ( 1366 n0.type == "node_op" && 1367 n0.value == "op_exp" && 1368 n1.type == "node_op" && 1369 n1.value == "op_exp" 1370 ) { 1371 n0.children[0].hash = this.parser.compile(n0.children[0]); 1372 n1.children[0].hash = this.parser.compile(n1.children[0]); 1373 if (n0.children[0].hash === n1.children[0].hash) { 1374 n0.children[1] = this.createNode( 1375 "node_op", 1376 "op_sub", 1377 n0.children[1], 1378 n1.children[1] 1379 ); 1380 this.mayNotBeSimplified = true; 1381 return n0; 1382 } 1383 } 1384 1385 break; 1386 1387 // a^0 = 1 1388 // a^1 -> a 1389 // 1^a -> 1 1390 // 0^a -> 0: a const != 0 1391 case "op_exp": 1392 n0 = node.children[0]; 1393 n1 = node.children[1]; 1394 if (n1.type == "node_const" && n1.value === 0.0) { 1395 n1.value = 1.0; 1396 return n1; 1397 } 1398 if (n1.type == "node_const" && n1.value == 1.0) { 1399 return n0; 1400 } 1401 if (n0.type == "node_const" && n0.value == 1.0) { 1402 return n0; 1403 } 1404 if ( 1405 n0.type == "node_const" && 1406 n0.value === 0.0 && 1407 n1.type == "node_const" && 1408 n1.value !== 0.0 1409 ) { 1410 return n0; 1411 } 1412 1413 // (a^b)^c -> a^(b*c) 1414 if (n0.type == "node_op" && n0.value == "op_exp") { 1415 node.children = [ 1416 n0.children[0], 1417 this.createNode("node_op", "op_mul", n0.children[1], n1) 1418 ]; 1419 return node; 1420 } 1421 break; 1422 } 1423 1424 switch (node.value) { 1425 // const_1 + const_2 -> (const_1 + const_2) 1426 // a + a -> 2*a 1427 // a + (-b) = a - b 1428 case "op_add": 1429 n0 = node.children[0]; 1430 n1 = node.children[1]; 1431 if ( 1432 n0.type == "node_const" && 1433 n1.type == "node_const" && 1434 n0.value == n1.value 1435 ) { 1436 n0.value += n1.value; 1437 return n0; 1438 } 1439 1440 if ( 1441 n0.type == "node_var" && 1442 n1.type == "node_var" && 1443 n0.value == n1.value 1444 ) { 1445 node.children[0] = this.createNode("node_const", 2.0); 1446 node.value = "op_mul"; 1447 return node; 1448 } 1449 1450 if (n0.type == "node_op" && n0.value == "op_neg") { 1451 node.value = "op_sub"; 1452 node.children[0] = n1; 1453 node.children[1] = n0.children[0]; 1454 this.mayNotBeSimplified = true; 1455 return node; 1456 } 1457 1458 if (n1.type == "node_op" && n1.value == "op_neg") { 1459 node.value = "op_sub"; 1460 node.children[1] = n1.children[0]; 1461 this.mayNotBeSimplified = true; 1462 return node; 1463 } 1464 1465 // const * a + const * a -> const * a 1466 if ( 1467 n0.type == "node_op" && 1468 n0.value == "op_mul" && 1469 n1.type == "node_op" && 1470 n1.value == "op_mul" 1471 ) { 1472 n0.children[1].hash = this.parser.compile(n0.children[1]); 1473 n1.children[1].hash = this.parser.compile(n1.children[1]); 1474 if (n0.children[1].hash === n1.children[1].hash) { 1475 node.value = "op_mul"; 1476 node.children = [ 1477 this.createNode( 1478 "node_op", 1479 "op_add", 1480 n0.children[0], 1481 n1.children[0] 1482 ), 1483 n0.children[1] 1484 ]; 1485 this.mayNotBeSimplified = true; 1486 return node; 1487 } 1488 } 1489 // const * a + a -> (const + 1) * a 1490 if (n0.type == "node_op" && n0.value == "op_mul") { 1491 n0.children[1].hash = this.parser.compile(n0.children[1]); 1492 n1.hash = this.parser.compile(n1); 1493 if (n0.children[1].hash === n1.hash) { 1494 node.value = "op_mul"; 1495 node.children = [ 1496 this.createNode( 1497 "node_op", 1498 "op_add", 1499 n0.children[0], 1500 this.createNode("node_const", 1.0) 1501 ), 1502 n1 1503 ]; 1504 this.mayNotBeSimplified = true; 1505 return node; 1506 } 1507 } 1508 // a + const*a -> (const + 1) * a 1509 if (n1.type == "node_op" && n1.value == "op_mul") { 1510 n1.children[1].hash = this.parser.compile(n1.children[1]); 1511 n0.hash = this.parser.compile(n0); 1512 if (n1.children[1].hash === n0.hash) { 1513 node.value = "op_mul"; 1514 node.children = [ 1515 this.createNode( 1516 "node_op", 1517 "op_add", 1518 this.createNode("node_const", 1.0), 1519 n1.children[0] 1520 ), 1521 n0 1522 ]; 1523 this.mayNotBeSimplified = true; 1524 return node; 1525 } 1526 } 1527 1528 break; 1529 1530 // a - (-b) = a + b 1531 case "op_sub": 1532 n0 = node.children[0]; 1533 n1 = node.children[1]; 1534 if (n1.type == "node_op" && n1.value == "op_neg") { 1535 node.value = "op_add"; 1536 node.children[1] = n1.children[0]; 1537 this.mayNotBeSimplified = true; 1538 return node; 1539 } 1540 break; 1541 1542 case "op_execfun": 1543 return this.simplifyElementary(node); 1544 } 1545 1546 return node; 1547 }, 1548 1549 simplifyElementary: function (node) { 1550 var fun = node.children[0].value, 1551 arg = node.children[1]; 1552 1553 // Catch errors of the form sin() 1554 if (arg.length == 0) { 1555 return node; 1556 } 1557 1558 switch (fun) { 1559 // sin(0) -> 0 1560 // sin(PI) -> 0 1561 // sin (int * PI) -> 0 1562 // sin (PI * int) -> 0 1563 // Same for tan() 1564 case "sin": 1565 case "tan": 1566 if (arg[0].type == "node_const" && arg[0].value === 0) { 1567 node.type = "node_const"; 1568 node.value = 0.0; 1569 return node; 1570 } 1571 if (arg[0].type == "node_var" && arg[0].value == "PI") { 1572 node.type = "node_const"; 1573 node.value = 0.0; 1574 return node; 1575 } 1576 if ( 1577 arg[0].type == "node_op" && 1578 arg[0].value == "op_mul" && 1579 arg[0].children[0].type == "node_const" && 1580 arg[0].children[0].value % 1 === 0 && 1581 arg[0].children[1].type == "node_var" && 1582 arg[0].children[1].value == "PI" 1583 ) { 1584 node.type = "node_const"; 1585 node.value = 0.0; 1586 return node; 1587 } 1588 break; 1589 1590 // cos(0) -> 1.0 1591 // cos(PI) -> -1.0 1592 // cos(int * PI) -> +/- 1.0 1593 // cos(PI * int) -> +/- 1.0 1594 case "cos": 1595 if (arg[0].type == "node_const" && arg[0].value === 0) { 1596 node.type = "node_const"; 1597 node.value = 1.0; 1598 return node; 1599 } 1600 if (arg[0].type == "node_var" && arg[0].value == "PI") { 1601 node.type = "node_op"; 1602 node.value = "op_neg"; 1603 node.children = [this.createNode("node_const", 1.0)]; 1604 return node; 1605 } 1606 /* 1607 if (arg[0].type == 'node_op' && arg[0].value == 'op_mul' && 1608 ((arg[0].children[0].type == 'node_const' && arg[0].children[0].value % 1 === 0 && 1609 arg[0].children[1].type == 'node_var' && arg[0].children[1].value == 'PI') || 1610 (arg[0].children[1].type == 'node_const' && arg[0].children[1].value % 1 === 0 && 1611 arg[0].children[0].type == 'node_var' && arg[0].children[0].value == 'PI'))) { 1612 node.type = 'node_const'; 1613 node.value = 1.0; 1614 return node; 1615 } 1616 */ 1617 break; 1618 1619 // exp(0) -> 1 1620 case "exp": 1621 if (arg[0].type == "node_const" && arg[0].value === 0) { 1622 node.type = "node_const"; 1623 node.value = 1.0; 1624 return node; 1625 } 1626 break; 1627 1628 // pow(a, 0) -> 1 1629 case "pow": 1630 if (arg[1].type == "node_const" && arg[1].value === 0) { 1631 node.type = "node_const"; 1632 node.value = 1.0; 1633 return node; 1634 } 1635 break; 1636 } 1637 1638 return node; 1639 } 1640 } 1641 ); 1642 1643 export default JXG.CA; 1644