Skip to content

Commit 6f46fe9

Browse files
committed
Calculator
1 parent 3c8b519 commit 6f46fe9

File tree

1 file changed

+259
-0
lines changed

1 file changed

+259
-0
lines changed

src/hard/Calculator.java

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
package hard;
2+
3+
import java.util.ArrayList;
4+
import java.util.Dictionary;
5+
import java.util.Hashtable;
6+
import java.util.Stack;
7+
import java.util.regex.Matcher;
8+
import java.util.regex.Pattern;
9+
10+
/**
11+
* Have the function Calculator(str) take the str parameter being passed
12+
* and evaluate the mathematical expression within in.
13+
* For example, if str were "2+(3-1)*3" the output should be 8.
14+
* Another example: if str were "(2-0)(6/2)" the output should be 6.
15+
* There can be parenthesis within the string, so you must evaluate it
16+
* properly according to the rules of arithmetic.
17+
* The string will contain the operators: +, -, /, *, (, and ).
18+
* If you have a string like this: #/#*# or #+#(#)/#,
19+
* then evaluate from left to right. So divide then multiply,
20+
* and for the second one multiply, divide, then add.
21+
* The evaluations will be such that there will not be any decimal operations,
22+
* so you do not need to account for rounding and whatnot.
23+
*/
24+
class Calculator {
25+
26+
// evaluate the Postfix expression
27+
private static final Stack<String> stack = new Stack<>();
28+
29+
/**
30+
* Cleaning, parsing and splitting the input string into the array of tokens
31+
* 1. convert to lowercase
32+
* 2. replace (a)(b) with (a) * (b)
33+
* 3. replace a(b) with a * (b)
34+
* 4. split two bundled characters (but only when the second character is not a digit)
35+
* 5. split bundled command/operator and a digit. Excl. minus to keep negative numbers intact.
36+
* 6. remove multiple spaces
37+
* 7. trim (remove leading and trailing spaces)
38+
* 8. split to array of strings
39+
*
40+
* @param input input string
41+
* @return array of strings with parsed operators and operands
42+
*/
43+
private static String[] parseConsoleInput(String input) {
44+
return input
45+
.toLowerCase()
46+
.replaceAll("(\\)\\()", ") * (")
47+
.replaceAll("([0-9])(?=[(])", "$1 *")
48+
.replaceAll("([\\p{P}\\p{S}a-z0-9])(?=[\\p{P}\\p{S}a-z])", "$1 ")
49+
.replaceAll("([^0-9])(?=[0-9])", "$1 ")
50+
.replaceAll(" +", " ")
51+
.trim()
52+
.split(" ");
53+
}
54+
55+
/**
56+
* Prints out a message to the console if the user input is invalid.
57+
*
58+
* @param op single element of the input string
59+
*/
60+
private static void printInputError(String op) {
61+
System.out.println("Unrecognised operator or operand: \"" + op + "\".");
62+
}
63+
64+
/**
65+
* Reduces two operands to a single result
66+
* by performing an arithmetical operation.
67+
*
68+
* @param a operand A
69+
* @param b operand B
70+
* @param operator denotes operation type (addition, substraction, division etc.)
71+
* @return result of the arithmetical operation
72+
* @throws ArithmeticException if divisor is 0
73+
*/
74+
public static long reduceOperands(long a, long b, String operator) {
75+
switch (operator) {
76+
case "+":
77+
return a + b;
78+
case "-":
79+
return a - b;
80+
case "*":
81+
return a * b;
82+
case "/":
83+
if (b == 0) {
84+
System.out.println("Divide by 0.");
85+
throw new ArithmeticException();
86+
}
87+
return a / b;
88+
default:
89+
return 0;
90+
}
91+
}
92+
93+
/**
94+
* Checks if the token is an operand (0-9).
95+
*
96+
* @param op a single token from the input string
97+
* @return true if the token is an operand, false if not
98+
*/
99+
private static boolean isOperand(String op) {
100+
Pattern pattern = Pattern.compile("^[\\d]|^-[\\d]");
101+
Matcher matcher = pattern.matcher(op);
102+
return matcher.find();
103+
}
104+
105+
/**
106+
* Checks if the token is an operator + - * / : ^ ( ) etc.
107+
*
108+
* @param op a single token from the input string
109+
* @return true if the token is an operator, false if not
110+
*/
111+
private static boolean isOperator(String op) {
112+
Pattern pattern = Pattern.compile("^[+\\-*/^%]");
113+
Matcher matcher = pattern.matcher(op);
114+
return matcher.find();
115+
}
116+
117+
/**
118+
* Converts the Infix expression to Postfix.
119+
*
120+
* @param tokens expression tokens that are already parsed and split
121+
*/
122+
private static String[] convertToPostfix(String[] tokens) {
123+
Stack<String> infStack = new Stack<>();
124+
String terminating = "#";
125+
Dictionary<String, Integer> precedence = new Hashtable<>() {
126+
{
127+
put(terminating, 0);
128+
put("(", 0);
129+
put(")", 0);
130+
put("+", 1);
131+
put("-", 1);
132+
put("*", 2);
133+
put("/", 2);
134+
}
135+
};
136+
ArrayList<String> output = new ArrayList<>();
137+
infStack.push(terminating);
138+
for (String token : tokens) {
139+
// token is an operand, add to output and move on
140+
if (isOperand(token)) {
141+
output.add(token);
142+
continue;
143+
}
144+
// left parenthesis, push it to stack and move on
145+
if (token.equals("(")) {
146+
infStack.push(token);
147+
continue;
148+
}
149+
// right parenthesis, keep popping until the left parenthesis is found
150+
if (token.equals(")")) {
151+
while (true) {
152+
String op = infStack.pop();
153+
if (op.equals("(")) {
154+
break;
155+
} else {
156+
output.add(op);
157+
}
158+
}
159+
continue;
160+
}
161+
// token is an operator
162+
if (isOperator(token)) {
163+
int cmp1 = precedence.get(token);
164+
while (true) {
165+
int cmp2 = precedence.get(infStack.peek());
166+
// operand has higher precedence than item on the top of stack
167+
if (cmp1 > cmp2) {
168+
infStack.push(token);
169+
break;
170+
} else {
171+
output.add(infStack.pop());
172+
}
173+
}
174+
}
175+
}
176+
// pop the remaining items until the terminating symbol is reached (complete)
177+
while (!infStack.empty() && !infStack.peek().equals(terminating)) {
178+
output.add(infStack.pop());
179+
}
180+
return output.toArray(new String[0]);
181+
}
182+
183+
/**
184+
* Takes two operands from stack and perform the operation with a provider operator.
185+
*
186+
* @param operator denotes operation type (addition, substraction, division etc.)
187+
* @return result of the evaluation
188+
*/
189+
private static String performArithOperation(String operator) {
190+
if (stack.size() >= 2) {
191+
// Safe to evaluate
192+
String elementB = stack.pop();
193+
String elementA = stack.pop();
194+
long opB = Long.parseLong(elementB);
195+
long opA = Long.parseLong(elementA);
196+
long result = reduceOperands(opA, opB, operator);
197+
return Long.toString(result);
198+
} else {
199+
// Stack underflow since at least one element is null
200+
return null;
201+
}
202+
}
203+
204+
/**
205+
* Computes the entire expression in Reverse Polish Notation.
206+
*
207+
* @param tokens expression tokens that are already parsed and split to Array of Strings
208+
*/
209+
private static Long evaluateExpression(String[] tokens) {
210+
for (String token : tokens) {
211+
// token is an operand, push it to stack and move on
212+
if (isOperand(token)) {
213+
stack.push(token);
214+
continue;
215+
}
216+
// token is an operator, evaluate
217+
if (isOperator(token)) {
218+
String result = performArithOperation(token);
219+
if (result != null) {
220+
stack.push(result);
221+
}
222+
continue;
223+
}
224+
// token is illegal
225+
printInputError(token);
226+
}
227+
// all tokens have been processed
228+
if (stack.isEmpty()) {
229+
return null;
230+
}
231+
return Long.parseLong(stack.peek());
232+
}
233+
234+
235+
/**
236+
* Calculate function.
237+
*
238+
* @param expression input to evaluate
239+
* @return result of evaluation
240+
*/
241+
public static String calculate(String expression) {
242+
String[] tokens = parseConsoleInput(expression);
243+
String[] postfix = convertToPostfix(tokens);
244+
Long result = evaluateExpression(postfix);
245+
return result == null ? "" : result.toString();
246+
}
247+
248+
/**
249+
* Entry point.
250+
*
251+
* @param args command line arguments
252+
*/
253+
public static void main(String[] args) {
254+
String expression = "8-7*(12+100/2)*9-2";
255+
String result = calculate(expression);
256+
System.out.println(result);
257+
}
258+
259+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy