UNPKG

mathjs

Version:

Math.js is an extensive math library for JavaScript and Node.js. It features a flexible expression parser with support for symbolic computation, comes with a large set of built-in functions and constants, and offers an integrated solution to work with dif

302 lines (301 loc) 10.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createSimplifyCore = void 0; var _is = require("../../utils/is.js"); var _operators = require("../../expression/operators.js"); var _util = require("./simplify/util.js"); var _factory = require("../../utils/factory.js"); var name = 'simplifyCore'; var dependencies = ['typed', 'parse', 'equal', 'isZero', 'add', 'subtract', 'multiply', 'divide', 'pow', 'AccessorNode', 'ArrayNode', 'ConstantNode', 'FunctionNode', 'IndexNode', 'ObjectNode', 'OperatorNode', 'ParenthesisNode', 'SymbolNode']; var createSimplifyCore = exports.createSimplifyCore = /* #__PURE__ */(0, _factory.factory)(name, dependencies, function (_ref) { var typed = _ref.typed, parse = _ref.parse, equal = _ref.equal, isZero = _ref.isZero, add = _ref.add, subtract = _ref.subtract, multiply = _ref.multiply, divide = _ref.divide, pow = _ref.pow, AccessorNode = _ref.AccessorNode, ArrayNode = _ref.ArrayNode, ConstantNode = _ref.ConstantNode, FunctionNode = _ref.FunctionNode, IndexNode = _ref.IndexNode, ObjectNode = _ref.ObjectNode, OperatorNode = _ref.OperatorNode, ParenthesisNode = _ref.ParenthesisNode, SymbolNode = _ref.SymbolNode; var node0 = new ConstantNode(0); var node1 = new ConstantNode(1); var nodeT = new ConstantNode(true); var nodeF = new ConstantNode(false); // test if a node will always have a boolean value (true/false) // not sure if this list is complete function isAlwaysBoolean(node) { return (0, _is.isOperatorNode)(node) && ['and', 'not', 'or'].includes(node.op); } var _createUtil = (0, _util.createUtil)({ FunctionNode: FunctionNode, OperatorNode: OperatorNode, SymbolNode: SymbolNode }), hasProperty = _createUtil.hasProperty, isCommutative = _createUtil.isCommutative; /** * simplifyCore() performs single pass simplification suitable for * applications requiring ultimate performance. To roughly summarize, * it handles cases along the lines of simplifyConstant() but where * knowledge of a single argument is sufficient to determine the value. * In contrast, simplify() extends simplifyCore() with additional passes * to provide deeper simplification (such as gathering like terms). * * Specifically, simplifyCore: * * * Converts all function calls with operator equivalents to their * operator forms. * * Removes operators or function calls that are guaranteed to have no * effect (such as unary '+'). * * Removes double unary '-', '~', and 'not' * * Eliminates addition/subtraction of 0 and multiplication/division/powers * by 1 or 0. * * Converts addition of a negation into subtraction. * * Eliminates logical operations with constant true or false leading * arguments. * * Puts constants on the left of a product, if multiplication is * considered commutative by the options (which is the default) * * Syntax: * * math.simplifyCore(expr) * math.simplifyCore(expr, options) * * Examples: * * const f = math.parse('2 * 1 * x ^ (1 - 0)') * math.simplifyCore(f) // Node "2 * x" * math.simplify('2 * 1 * x ^ (1 - 0)', [math.simplifyCore]) // Node "2 * x" * * See also: * * simplify, simplifyConstant, resolve, derivative * * @param {Node | string} node * The expression to be simplified * @param {Object} options * Simplification options, as per simplify() * @return {Node} Returns expression with basic simplifications applied */ function _simplifyCore(nodeToSimplify) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var context = options ? options.context : undefined; if (hasProperty(nodeToSimplify, 'trivial', context)) { // This node does nothing if it has only one argument, so if so, // return that argument simplified if ((0, _is.isFunctionNode)(nodeToSimplify) && nodeToSimplify.args.length === 1) { return _simplifyCore(nodeToSimplify.args[0], options); } // For other node types, we try the generic methods var simpChild = false; var childCount = 0; nodeToSimplify.forEach(function (c) { ++childCount; if (childCount === 1) { simpChild = _simplifyCore(c, options); } }); if (childCount === 1) { return simpChild; } } var node = nodeToSimplify; if ((0, _is.isFunctionNode)(node)) { var op = (0, _operators.getOperator)(node.name); if (op) { // Replace FunctionNode with a new OperatorNode if (node.args.length > 2 && hasProperty(node, 'associative', context)) { // unflatten into binary operations since that's what simplifyCore handles while (node.args.length > 2) { var last = node.args.pop(); var seclast = node.args.pop(); node.args.push(new OperatorNode(op, node.name, [last, seclast])); } } node = new OperatorNode(op, node.name, node.args); } else { return new FunctionNode(_simplifyCore(node.fn), node.args.map(function (n) { return _simplifyCore(n, options); })); } } if ((0, _is.isOperatorNode)(node) && node.isUnary()) { var a0 = _simplifyCore(node.args[0], options); if (node.op === '~') { // bitwise not if ((0, _is.isOperatorNode)(a0) && a0.isUnary() && a0.op === '~') { return a0.args[0]; } } if (node.op === 'not') { // logical not if ((0, _is.isOperatorNode)(a0) && a0.isUnary() && a0.op === 'not') { // Has the effect of turning the argument into a boolean // So can only eliminate the double negation if // the inside is already boolean if (isAlwaysBoolean(a0.args[0])) { return a0.args[0]; } } } var finish = true; if (node.op === '-') { // unary minus if ((0, _is.isOperatorNode)(a0)) { if (a0.isBinary() && a0.fn === 'subtract') { node = new OperatorNode('-', 'subtract', [a0.args[1], a0.args[0]]); finish = false; // continue to process the new binary node } if (a0.isUnary() && a0.op === '-') { return a0.args[0]; } } } if (finish) return new OperatorNode(node.op, node.fn, [a0]); } if ((0, _is.isOperatorNode)(node) && node.isBinary()) { var _a = _simplifyCore(node.args[0], options); var a1 = _simplifyCore(node.args[1], options); if (node.op === '+') { if ((0, _is.isConstantNode)(_a) && isZero(_a.value)) { return a1; } if ((0, _is.isConstantNode)(a1) && isZero(a1.value)) { return _a; } if ((0, _is.isOperatorNode)(a1) && a1.isUnary() && a1.op === '-') { a1 = a1.args[0]; node = new OperatorNode('-', 'subtract', [_a, a1]); } } if (node.op === '-') { if ((0, _is.isOperatorNode)(a1) && a1.isUnary() && a1.op === '-') { return _simplifyCore(new OperatorNode('+', 'add', [_a, a1.args[0]]), options); } if ((0, _is.isConstantNode)(_a) && isZero(_a.value)) { return _simplifyCore(new OperatorNode('-', 'unaryMinus', [a1])); } if ((0, _is.isConstantNode)(a1) && isZero(a1.value)) { return _a; } return new OperatorNode(node.op, node.fn, [_a, a1]); } if (node.op === '*') { if ((0, _is.isConstantNode)(_a)) { if (isZero(_a.value)) { return node0; } else if (equal(_a.value, 1)) { return a1; } } if ((0, _is.isConstantNode)(a1)) { if (isZero(a1.value)) { return node0; } else if (equal(a1.value, 1)) { return _a; } if (isCommutative(node, context)) { return new OperatorNode(node.op, node.fn, [a1, _a], node.implicit); // constants on left } } return new OperatorNode(node.op, node.fn, [_a, a1], node.implicit); } if (node.op === '/') { if ((0, _is.isConstantNode)(_a) && isZero(_a.value)) { return node0; } if ((0, _is.isConstantNode)(a1) && equal(a1.value, 1)) { return _a; } return new OperatorNode(node.op, node.fn, [_a, a1]); } if (node.op === '^') { if ((0, _is.isConstantNode)(a1)) { if (isZero(a1.value)) { return node1; } else if (equal(a1.value, 1)) { return _a; } } } if (node.op === 'and') { if ((0, _is.isConstantNode)(_a)) { if (_a.value) { if (isAlwaysBoolean(a1)) return a1; if ((0, _is.isConstantNode)(a1)) { return a1.value ? nodeT : nodeF; } } else { return nodeF; } } if ((0, _is.isConstantNode)(a1)) { if (a1.value) { if (isAlwaysBoolean(_a)) return _a; } else { return nodeF; } } } if (node.op === 'or') { if ((0, _is.isConstantNode)(_a)) { if (_a.value) { return nodeT; } else { if (isAlwaysBoolean(a1)) return a1; } } if ((0, _is.isConstantNode)(a1)) { if (a1.value) { return nodeT; } else { if (isAlwaysBoolean(_a)) return _a; } } } return new OperatorNode(node.op, node.fn, [_a, a1]); } if ((0, _is.isOperatorNode)(node)) { return new OperatorNode(node.op, node.fn, node.args.map(function (a) { return _simplifyCore(a, options); })); } if ((0, _is.isArrayNode)(node)) { return new ArrayNode(node.items.map(function (n) { return _simplifyCore(n, options); })); } if ((0, _is.isAccessorNode)(node)) { return new AccessorNode(_simplifyCore(node.object, options), _simplifyCore(node.index, options)); } if ((0, _is.isIndexNode)(node)) { return new IndexNode(node.dimensions.map(function (n) { return _simplifyCore(n, options); })); } if ((0, _is.isObjectNode)(node)) { var newProps = {}; for (var prop in node.properties) { newProps[prop] = _simplifyCore(node.properties[prop], options); } return new ObjectNode(newProps); } // cannot simplify return node; } return typed(name, { Node: _simplifyCore, 'Node,Object': _simplifyCore }); });