-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMathFunction.js
More file actions
175 lines (161 loc) · 5.55 KB
/
MathFunction.js
File metadata and controls
175 lines (161 loc) · 5.55 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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
// naming convention is PascalCase
module.exports = class MathFunction {
// constructor
constructor(mathFunction) {
// instance properties are often defined in the constructor
this.mathFunction = mathFunction;
// "this" refers to the instance object
}
/**
* This function is responsible for taking the user input
* and then outputting the result of the expression
* to the user.
* This function does not handle the computation
* of the expression itself; there are other functions
* which do that
* @param {String} expression - String representation
* of the expression input taken by the caller of this
* API
* @returns {String} - returns a String representation
* of the answer of the original expression
*/
computeExpression() {
let infixExpression = this.mathFunction;
if (infixExpression === null) { // something was wrong with the input format
return null;
}
let postFixExpression = this.infixToPostfix(infixExpression);
if (postFixExpression === null) { // something was wrong with the input format
return null;
}
return this.evaluatePostFixExpression(postFixExpression) + "";
}
/**
* Implements Dijkstra's Shunting-yard algorithm
* for converting mathematical expressions
* from infix notation(standard notation) to postfix notation.
* We want to convert the expression into postfix notation
* because it makes it easy to compute the expression
*
* @param {String} expression - String which represents the
* mathematical expression input from the user
* ex: 3 + (6 / 2) * 2
*
* @returns {Array}- returns an array postfix version
* of the origin infix expression parameter
*/
infixToPostfix(expression) {
let outputQueue = [];
let operatorStack = [];
// use a hashmap to assign a precedence for each operator
let operatorPrecedences = new Map([
['^', 3],
['*', 2],
['/', 2],
['+', 1],
['-', 1],
['(', 0]
]);
for (let i = 0; i < expression.length; i++) {
let curr = expression.charAt(i);
if (this.isNum(curr)) {
let num = curr;
i++;
while (i < expression.length && this.isNum(expression.charAt(i))) {
curr = expression.charAt(i);
if (curr !== ' ') {
num = num.concat(curr);
}
i++;
}
outputQueue.push(num);
if (i !== expression.length) {
i--;
}
} else if (this.isOperator(curr)) {
if (operatorStack.length === 0 || curr === '(') {
operatorStack.push(curr);
} else if (curr === ')') {
let operator = operatorStack.pop();
while (operatorStack.length > 0 && operator !== '(') {
outputQueue.push(operator);
operator = operatorStack.pop();
}
} else {
// while element on top of stack has greater or equal precedence than curr
while (operatorStack.length > 0
&& operatorPrecedences.get(operatorStack[operatorStack.length - 1]) >=
operatorPrecedences.get(curr)) {
let operator = operatorStack.pop();
outputQueue.push(operator);
}
operatorStack.push(curr);
}
} else if (curr !== " ") {
return null;
}
}
while (operatorStack.length > 0) {
outputQueue.push(operatorStack.pop());
}
return outputQueue;
}
/**
* This function takes the postFix version of the user's expression,
* evaluates it mathematically, then returns the result
* @param {Array} expression - the postFix version of the user's input expression
* @returns {int} - return the result of the expression
*/
evaluatePostFixExpression(expression) {
let stackOfNumbers = [];
for (let i = 0; i < expression.length; i++) {
let curr = expression[i];
if (this.isNum(curr)) {
const ZEROASASCII = 48;
stackOfNumbers.push(curr.charCodeAt() - ZEROASASCII);
} else if (this.isOperator(curr)) {
if (stackOfNumbers.length < 2) {
return null;
}
let num2Int = stackOfNumbers.pop();
let num1Int = stackOfNumbers.pop();
let result = 0;
if (curr === '+') {
result = num1Int + num2Int;
} else if (curr === '-') {
result = num1Int - num2Int;
} else if (curr === '*') {
result = num1Int * num2Int;
} else if (curr === '/') {
result = num1Int / num2Int;
}
stackOfNumbers.push(result);
}
}
return stackOfNumbers.pop();
}
/**
* This function takes a symbol/char and determines if it is
* a number or not.
* @param {char} symbol - takes in a symbol(char of the expression)
* @returns {boolean} - returns a boolean which represents whether or not
* the symbol is a number. Returns true if it is a number, otherwise return false
*/
isNum(symbol) {
const LOWERNUMASCIIBOUND = 48;
const UPPERNUMASCIIBOUND = 57;
return LOWERNUMASCIIBOUND <= symbol.charCodeAt() &&
symbol.charCodeAt() <= UPPERNUMASCIIBOUND;
}
/**
* This function takes a symbol/char and determines if it is
* an operator or not.
* @param {char} symbol - takes in a symbol(char of the expression)
* @returns {boolean} - returns a boolean which represents whether or not
* the symbol is an operator. Returns true if it is an operator, otherwise return false
*/
isOperator(symbol) {
return symbol === '^' || symbol === '*' || symbol === '/' || symbol === '+'
|| symbol === '-' || symbol === '(' || symbol === ')';
}
};