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