UNPKG

mathsteps-experimental-fork

Version:

Step by step math solutions. Experimental Fork

124 lines (117 loc) 4.42 kB
'use strict'; const config = require('../config.js'); const Type = require('./Type.js'); const NodeCreator = { operator(op, args, implicit = false) { if ((args.length === 1) && '*+'.includes(op)) return args[0] switch (op) { case '+': return new config.default.OperatorNode('+', 'add', args) case '-': return new config.default.OperatorNode('-', 'subtract', args) case '/': return new config.default.OperatorNode('/', 'divide', args) case '*': return new config.default.OperatorNode('*', 'multiply', args, implicit) case '^': return new config.default.OperatorNode('^', 'pow', args) default: throw new Error(`Unsupported operation: ${op}`) } }, // In almost all cases, use Negative.negate (with naive = true) to add a // unary minus to your node, rather than calling this constructor directly unaryMinus(content) { let rv = null; if (Type.isConstant(content)) { // -c : just multiply constant by -1 rv = new config.default.ConstantNode(config.default.multiply(config.default.bignumber(content.value), -1)); } else { // Default scenario - wrap into unaryMinus() function. rv = new config.default.OperatorNode('-', 'unaryMinus', [content]); } return rv }, constant(val) { if (typeof (val) === 'number') val = config.default.bignumber(val); return new config.default.ConstantNode(val) }, symbol(name) { return new config.default.SymbolNode(name) }, parenthesis(content) { return new config.default.ParenthesisNode(content) }, list(content) { return new config.default.ArrayNode(content) }, // exponent might be null, which means there's no exponent node. // similarly, coefficient might be null, which means there's no coefficient // the base node can never be null. term(base, exponent, coeff, explicitCoeff = false) { let term = base; if (exponent) term = this.operator('^', [term, exponent]); if (coeff && (explicitCoeff || config.default.unequal(coeff.value, 1))) { if (Type.isConstant(coeff) && config.default.equal(coeff.value, -1) && !explicitCoeff) { // if you actually want -1 as the coefficient, set explicitCoeff to true term = this.unaryMinus(term); } else { term = this.operator('*', [coeff, term], true); } } return term }, polynomialTerm(symbol, exponent, coeff, explicitCoeff = false) { return this.term(symbol, exponent, coeff, explicitCoeff) }, // Given a root value and a radicand (what is under the radical) nthRoot(radicandNode, rootNode) { if (rootNode && !Type.kemuIsConstantInteger(rootNode, 2)) { // Root of n degree. return new config.default.FunctionNode(NodeCreator.symbol('nthRoot'), [radicandNode, rootNode]) } else { // Root of 2 degree, fallback to sqrt. return new config.default.FunctionNode(NodeCreator.symbol('sqrt'), [radicandNode]) } }, kemuCreateAbs(node) { const symbol = NodeCreator.symbol('abs'); return new config.default.FunctionNode(symbol, [node]) }, kemuCreateSqrt(node) { const symbol = NodeCreator.symbol('sqrt'); return new config.default.FunctionNode(symbol, [node]) }, kemuCreateBuiltInConstant(name) { // Possible improvement: Limit pool of known constants? const objectNode = new config.default.SymbolNode('const'); const indexNode = new config.default.IndexNode([new config.default.ConstantNode(name)], true); return new config.default.AccessorNode(objectNode, indexNode) }, percent(node) { return new config.default.FunctionNode('percent', [node]) }, kemuCreateByFn(fn, args) { switch (fn) { case 'add': return NodeCreator.operator('+', args) case 'subtract': return NodeCreator.operator('-', args) case 'multiply': return NodeCreator.operator('*', args) case 'divide': return NodeCreator.operator('/', args) case 'pow': return NodeCreator.operator('^', args) case 'unaryMinus': return NodeCreator.unaryMinus(args) default: { // eslint-disable-next-line no-throw-literal throw `kemuCreateByFn: error: unknown fn: ${fn}` } } }, }; module.exports = NodeCreator;