This repository was archived by the owner on Aug 29, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 282
Expand file tree
/
Copy pathindex.js
More file actions
118 lines (104 loc) · 3.84 KB
/
index.js
File metadata and controls
118 lines (104 loc) · 3.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Collects and combines like terms
const addLikeTerms = require('./addLikeTerms');
const checks = require('../../checks');
const clone = require('../../util/clone');
const divideLikeTerms = require('./divideLikeTerms');
const multiplyLikeTerms = require('./multiplyLikeTerms');
const ChangeTypes = require('../../ChangeTypes');
const LikeTermCollector = require('./LikeTermCollector');
const Node = require('../../node');
const TreeSearch = require('../../TreeSearch');
const termCollectorFunctions = {
'+': addLikeTerms,
'/': divideLikeTerms,
'*': multiplyLikeTerms
};
// Iterates through the tree looking for like terms to collect and combine.
// Will prioritize deeper expressions. Returns a Node.Status object.
const search = TreeSearch.postOrder(collectAndCombineLikeTerms);
// Given an operator node, maybe collects and then combines if possible
// e.g. 2x + 4x + y => 6x + y
// e.g. 2x * x^2 * 5x => 10 x^4
function collectAndCombineLikeTerms(node) {
if (node.op === '+') {
const status = collectAndCombineOperation(node);
if (status.hasChanged()) {
return status;
}
// we might also be able to just combine if they're all the same term
// e.g. 2x + 4x + x (doesn't need collecting)
return addLikeTerms(node, true);
}
else if (node.op === '*') {
// collect and combine involves there being coefficients pulled the front
// e.g. 2x * x^2 * 5x => (2*5) * (x * x^2 * x) => ... => 10 x^4
if (checks.canMultiplyLikeTermConstantNodes(node)) {
return multiplyLikeTerms(node, true);
}
const status = collectAndCombineOperation(node);
if (status.hasChanged()) {
// make sure there's no * between the coefficient and the symbol part
status.newNode.implicit = true;
return status;
}
// we might also be able to just combine polynomial terms
// e.g. x * x^2 * x => ... => x^4
return multiplyLikeTerms(node, true);
}
else if (node.op === '/') {
// we might also be able to just combine like terms
// e.g 10^6 / 10^3 => ... => 10^3
// e.g x^5 / x^2 => ... => x^3
return divideLikeTerms(node, true);
}
else {
return Node.Status.noChange(node);
}
}
// Collects and combines (if possible) the arguments of an addition or
// multiplication
function collectAndCombineOperation(node) {
let substeps = [];
const status = LikeTermCollector.collectLikeTerms(clone(node));
if (!status.hasChanged()) {
return status;
}
// STEP 1: collect like terms, e.g. 2x + 4x^2 + 5x => 4x^2 + (2x + 5x)
substeps.push(status);
let newNode = Node.Status.resetChangeGroups(status.newNode);
// STEP 2 onwards: combine like terms for each group that can be combined
// e.g. (x + 3x) + (2 + 2) has two groups
const combineSteps = combineLikeTerms(newNode);
if (combineSteps.length > 0) {
substeps = substeps.concat(combineSteps);
const lastStep = combineSteps[combineSteps.length - 1];
newNode = Node.Status.resetChangeGroups(lastStep.newNode);
}
return Node.Status.nodeChanged(
ChangeTypes.COLLECT_AND_COMBINE_LIKE_TERMS,
node, newNode, true, substeps);
}
// step 2 onwards for collectAndCombineOperation
// combine like terms for each group that can be combined
// e.g. (x + 3x) + (2 + 2) has two groups
// returns a list of combine steps
function combineLikeTerms(node) {
const steps = [];
let newNode = clone(node);
for (let i = 0; i < node.args.length; i++) {
let child = node.args[i];
// All groups of terms will be surrounded by parenthesis
if (!Node.Type.isParenthesis(child)) {
continue;
}
child = child.content;
const childStatus = termCollectorFunctions[newNode.op](child);
if (childStatus.hasChanged()) {
const status = Node.Status.childChanged(newNode, childStatus, i);
steps.push(status);
newNode = Node.Status.resetChangeGroups(status.newNode);
}
}
return steps;
}
module.exports = search;