UNPKG

4.06 kBJavaScriptView Raw
1import { isOperatorNode } from '../../../utils/is'
2import { factory } from '../../../utils/factory'
3
4const name = 'simplifyUtil'
5const dependencies = [
6 'FunctionNode',
7 'OperatorNode',
8 'SymbolNode'
9]
10
11export const createUtil = /* #__PURE__ */ factory(name, dependencies, ({ FunctionNode, OperatorNode, SymbolNode }) => {
12 // TODO commutative/associative properties rely on the arguments
13 // e.g. multiply is not commutative for matrices
14 // The properties should be calculated from an argument to simplify, or possibly something in math.config
15 // the other option is for typed() to specify a return type so that we can evaluate the type of arguments
16 const commutative = {
17 'add': true,
18 'multiply': true
19 }
20 const associative = {
21 'add': true,
22 'multiply': true
23 }
24
25 function isCommutative (node, context) {
26 if (!isOperatorNode(node)) {
27 return true
28 }
29 const name = node.fn.toString()
30 if (context && context.hasOwnProperty(name) && context[name].hasOwnProperty('commutative')) {
31 return context[name].commutative
32 }
33 return commutative[name] || false
34 }
35
36 function isAssociative (node, context) {
37 if (!isOperatorNode(node)) {
38 return false
39 }
40 const name = node.fn.toString()
41 if (context && context.hasOwnProperty(name) && context[name].hasOwnProperty('associative')) {
42 return context[name].associative
43 }
44 return associative[name] || false
45 }
46
47 /**
48 * Flatten all associative operators in an expression tree.
49 * Assumes parentheses have already been removed.
50 */
51 function flatten (node) {
52 if (!node.args || node.args.length === 0) {
53 return node
54 }
55 node.args = allChildren(node)
56 for (let i = 0; i < node.args.length; i++) {
57 flatten(node.args[i])
58 }
59 }
60
61 /**
62 * Get the children of a node as if it has been flattened.
63 * TODO implement for FunctionNodes
64 */
65 function allChildren (node) {
66 let op
67 const children = []
68 const findChildren = function (node) {
69 for (let i = 0; i < node.args.length; i++) {
70 const child = node.args[i]
71 if (isOperatorNode(child) && op === child.op) {
72 findChildren(child)
73 } else {
74 children.push(child)
75 }
76 }
77 }
78
79 if (isAssociative(node)) {
80 op = node.op
81 findChildren(node)
82 return children
83 } else {
84 return node.args
85 }
86 }
87
88 /**
89 * Unflatten all flattened operators to a right-heavy binary tree.
90 */
91 function unflattenr (node) {
92 if (!node.args || node.args.length === 0) {
93 return
94 }
95 const makeNode = createMakeNodeFunction(node)
96 const l = node.args.length
97 for (let i = 0; i < l; i++) {
98 unflattenr(node.args[i])
99 }
100 if (l > 2 && isAssociative(node)) {
101 let curnode = node.args.pop()
102 while (node.args.length > 0) {
103 curnode = makeNode([node.args.pop(), curnode])
104 }
105 node.args = curnode.args
106 }
107 }
108
109 /**
110 * Unflatten all flattened operators to a left-heavy binary tree.
111 */
112 function unflattenl (node) {
113 if (!node.args || node.args.length === 0) {
114 return
115 }
116 const makeNode = createMakeNodeFunction(node)
117 const l = node.args.length
118 for (let i = 0; i < l; i++) {
119 unflattenl(node.args[i])
120 }
121 if (l > 2 && isAssociative(node)) {
122 let curnode = node.args.shift()
123 while (node.args.length > 0) {
124 curnode = makeNode([curnode, node.args.shift()])
125 }
126 node.args = curnode.args
127 }
128 }
129
130 function createMakeNodeFunction (node) {
131 if (isOperatorNode(node)) {
132 return function (args) {
133 try {
134 return new OperatorNode(node.op, node.fn, args, node.implicit)
135 } catch (err) {
136 console.error(err)
137 return []
138 }
139 }
140 } else {
141 return function (args) {
142 return new FunctionNode(new SymbolNode(node.name), args)
143 }
144 }
145 }
146
147 return {
148 createMakeNodeFunction,
149 isCommutative,
150 isAssociative,
151 flatten,
152 allChildren,
153 unflattenr,
154 unflattenl
155 }
156})