UNPKG

mathsteps-experimental-fork

Version:

Step by step math solutions. Experimental Fork

207 lines (192 loc) 6.38 kB
'use strict'; const config = require('../config.js'); const NodeType = {}; const NODE_TWO = new config.default.ConstantNode(config.default.bignumber(2)); NodeType.isOperator = function (node, operator = null) { return node.type === 'OperatorNode' && node.fn !== 'unaryMinus' && '*+-/^'.includes(node.op) && (operator ? node.op === operator : true) }; NodeType.isUnaryMinus = function (node) { return node.type === 'OperatorNode' && node.fn === 'unaryMinus' }; NodeType.isFunction = function (node, functionName = null) { if (node.type !== 'FunctionNode') return false if (functionName && (node.fn.name !== functionName)) return false return true }; NodeType.isNthRoot = function (node) { return NodeType.isFunction(node, 'nthRoot') || NodeType.isFunction(node, 'sqrt') }; // Given an nthRoot node, will return the root node. // The root node is the second child of the nthRoot node, but if one doesn't // exist, we assume it's a square root and return 2. NodeType.getRootNode = function (node) { if (NodeType.isFunction(node, 'nthRoot')) return node.args.length === 2 ? node.args[1] : NODE_TWO else if (NodeType.isFunction(node, 'sqrt')) return NODE_TWO else throw new Error('Expected nthRoot or sqrt') }; // Given an nthRoot node, will return the radicand node. NodeType.getRadicandNode = function (node) { if (NodeType.isNthRoot(node)) return node.args[0] else throw new Error('Expected nthRoot or sqrt') }; NodeType.isSymbol = function (node, allowUnaryMinus = false) { if (node.type === 'SymbolNode') { return true } else if (allowUnaryMinus && NodeType.isUnaryMinus(node)) { return NodeType.isSymbol(node.args[0], false) } else if ((node.type === 'AccessorNode') && (node.object.name === 'const') && (node.index.dimensions.length === 1)) { // Built-in constant: const.pi like. return true } else { return false } }; NodeType.isNamedSymbol = function (node, expectedName) { return (node.type === 'SymbolNode') && (node.name === expectedName) }; NodeType.isConstant = function (node, allowUnaryMinus = false) { if (node.type === 'ConstantNode') { return true } else if (allowUnaryMinus && NodeType.isUnaryMinus(node)) { if (NodeType.isConstant(node.args[0], false)) return config.default.isNumeric(node.args[0].value) else return false } else { return false } }; // Function branch from al_mixed_numbers // TODO: Review is it still useful? NodeType.isMixedNumber = function (node, _allowUnaryMinus = false) { if (node.op === '*' && NodeType.isConstant(node.args[0]) && (node.args[1].type === 'ParenthesisNode') && NodeType.isConstantFraction(node.args[1].content)) { return true } return false }; NodeType.isConstantFraction = function (node, allowUnaryMinus = false) { if (NodeType.isOperator(node, '/')) return node.args.every(n => NodeType.isConstant(n, allowUnaryMinus)) else return false }; NodeType.isConstantOrConstantFraction = function (node, allowUnaryMinus = false) { if (NodeType.isConstant(node, allowUnaryMinus) || NodeType.isConstantFraction(node, allowUnaryMinus)) { return true } else { return false } }; NodeType.isIntegerFraction = function (node, allowUnaryMinus = false) { if (!NodeType.isConstantFraction(node, allowUnaryMinus)) return false let numerator = node.args[0]; let denominator = node.args[1]; if (allowUnaryMinus) { if (NodeType.isUnaryMinus(numerator)) numerator = numerator.args[0]; if (NodeType.isUnaryMinus(denominator)) denominator = denominator.args[0]; } return (config.default.isInteger(numerator.value) && config.default.isInteger(denominator.value)) }; NodeType.kemuIsConstantInteger = function (node, expectedValue) { const isConstant = (node.type === 'ConstantNode'); if (expectedValue != null) return isConstant && config.default.equal(node.value, expectedValue) else return isConstant && config.default.isInteger(node.value) }; NodeType.isZero = function (node) { return NodeType.isConstant(node) && config.default.isZero(node.value) }; NodeType.kemuIsConstantNegative = function (node) { return (node.type === 'ConstantNode') && config.default.isNegative(node.value) }; NodeType.kemuIsConstantPositive = function (node) { return (node.type === 'ConstantNode') && config.default.isPositive(node.value) }; NodeType.kemuIsConstantOrSymbol = function (node) { return NodeType.isConstant(node) || NodeType.isSymbol(node) }; NodeType.doesContainSymbol = function (node, symbolName) { const stamp = `_containsSymbol_${symbolName}`; let rv = node[stamp]; if (rv == null) { // Node checked first time. Go on. if (NodeType.isSymbol(node)) { rv = node.name === symbolName; } else if (NodeType.isConstant(node)) { rv = false; } else { rv = false; for (const idx in node.args) { rv = NodeType.doesContainSymbol(node.args[idx], symbolName); if (rv) { // At least one arg contains searched symbol. // Don't go on anymore. break } } } // Save result for further calls. node[stamp] = rv; } return rv }; NodeType.isPowerOfSymbol = function (node) { // x^(...) return NodeType.isOperator(node, '^') && NodeType.isSymbol(node.args[0]) }; NodeType.isSymbolOrPowerOfSymbol = function (node) { // x // x^(...) return NodeType.isSymbol(node) || NodeType.isPowerOfSymbol(node) }; NodeType.isPolynomialTerm = function (node) { let rv = true; if (NodeType.isSymbolOrPowerOfSymbol(node)) ; else if (NodeType.isUnaryMinus(node) && NodeType.isSymbolOrPowerOfSymbol(node.args[0])) ; else if (NodeType.isOperator(node, '*') && (node.args.length === 2) && NodeType.isConstantOrConstantFraction(node.args[0]) && NodeType.isSymbolOrPowerOfSymbol(node.args[1])) ; else if (NodeType.isOperator(node, '/') && (node.args.length === 2) && NodeType.isConstantOrConstantFraction(node.args[1])) ; else { // Unmatched - not a polynomial term. rv = false; } return rv }; module.exports = NodeType;