Skip to content

Commit 76e0bad

Browse files
committed
Added test class for EquationEvaluator
1 parent 2b373f6 commit 76e0bad

File tree

2 files changed

+142
-105
lines changed

2 files changed

+142
-105
lines changed

src/main/java/world/bentobox/level/calculators/EquationEvaluator.java

+105-105
Original file line numberDiff line numberDiff line change
@@ -3,119 +3,119 @@
33
import java.text.ParseException;
44

55
/**
6-
* @author tastybento
6+
* Utility class to evaluate equations
77
*/
88
public class EquationEvaluator {
99

1010
private static class Parser {
11-
private final String input;
12-
private int pos = -1;
13-
private int currentChar;
14-
15-
public Parser(String input) {
16-
this.input = input;
17-
moveToNextChar();
18-
}
19-
20-
private void moveToNextChar() {
21-
currentChar = (++pos < input.length()) ? input.charAt(pos) : -1;
22-
}
23-
24-
private boolean tryToEat(int charToEat) {
25-
while (currentChar == ' ')
26-
moveToNextChar();
27-
if (currentChar == charToEat) {
28-
moveToNextChar();
29-
return true;
30-
}
31-
return false;
32-
}
33-
34-
public double evaluate() throws ParseException {
35-
double result = parseExpression();
36-
if (pos < input.length()) {
37-
throw new ParseException("Unexpected character: " + (char) currentChar, pos);
38-
}
39-
return result;
40-
}
41-
42-
private double parseExpression() throws ParseException {
43-
double result = parseTerm();
44-
while (true) {
45-
if (tryToEat('+'))
46-
result += parseTerm();
47-
else if (tryToEat('-'))
48-
result -= parseTerm();
49-
else
50-
return result;
51-
}
52-
}
53-
54-
private double parseFactor() throws ParseException {
55-
if (tryToEat('+'))
56-
return parseFactor(); // unary plus
57-
if (tryToEat('-'))
58-
return -parseFactor(); // unary minus
59-
60-
double x;
61-
int startPos = this.pos;
62-
if (tryToEat('(')) { // parentheses
63-
x = parseExpression();
64-
tryToEat(')');
65-
} else if ((currentChar >= '0' && currentChar <= '9') || currentChar == '.') { // numbers
66-
while ((currentChar >= '0' && currentChar <= '9') || currentChar == '.')
67-
moveToNextChar();
68-
x = Double.parseDouble(input.substring(startPos, this.pos));
69-
} else if (currentChar >= 'a' && currentChar <= 'z') { // functions
70-
while (currentChar >= 'a' && currentChar <= 'z')
71-
moveToNextChar();
72-
String func = input.substring(startPos, this.pos);
73-
x = parseFactor();
74-
switch (func) {
75-
case "sqrt":
76-
x = Math.sqrt(x);
77-
break;
78-
case "sin":
79-
x = Math.sin(Math.toRadians(x));
80-
break;
81-
case "cos":
82-
x = Math.cos(Math.toRadians(x));
83-
break;
84-
case "tan":
85-
x = Math.tan(Math.toRadians(x));
86-
break;
87-
case "log":
88-
x = Math.log(x);
89-
break;
90-
default:
91-
throw new ParseException("Unknown function: " + func, startPos);
92-
}
93-
} else {
94-
throw new ParseException("Unexpected: " + (char) currentChar, startPos);
95-
}
96-
97-
if (tryToEat('^'))
98-
x = Math.pow(x, parseFactor()); // exponentiation
99-
100-
return x;
101-
}
102-
103-
private double parseTerm() throws ParseException {
104-
double x = parseFactor();
105-
for (;;) {
106-
if (tryToEat('*'))
107-
x *= parseFactor(); // multiplication
108-
else if (tryToEat('/'))
109-
x /= parseFactor(); // division
110-
else
111-
return x;
112-
}
113-
}
11+
private final String input;
12+
private int pos = -1;
13+
private int currentChar;
14+
15+
@SuppressWarnings("unused")
16+
private Parser() {
17+
throw new IllegalStateException("Utility class");
18+
}
19+
20+
public Parser(String input) {
21+
this.input = input;
22+
moveToNextChar();
23+
}
24+
25+
private void moveToNextChar() {
26+
currentChar = (++pos < input.length()) ? input.charAt(pos) : -1;
27+
}
28+
29+
private boolean tryToEat(int charToEat) {
30+
while (currentChar == ' ') {
31+
moveToNextChar();
32+
}
33+
if (currentChar == charToEat) {
34+
moveToNextChar();
35+
return true;
36+
}
37+
return false;
38+
}
39+
40+
public double evaluate() throws ParseException {
41+
double result = parseExpression();
42+
if (pos < input.length()) {
43+
throw new ParseException("Unexpected character: " + (char) currentChar, pos);
44+
}
45+
return result;
46+
}
47+
48+
private double parseExpression() throws ParseException {
49+
double result = parseTerm();
50+
while (true) {
51+
if (tryToEat('+')) {
52+
result += parseTerm();
53+
} else if (tryToEat('-')) {
54+
result -= parseTerm();
55+
} else {
56+
return result;
57+
}
58+
}
59+
}
60+
61+
private double parseFactor() throws ParseException {
62+
if (tryToEat('+')) {
63+
return parseFactor(); // unary plus
64+
}
65+
if (tryToEat('-')) {
66+
return -parseFactor(); // unary minus
67+
}
68+
double x;
69+
int startPos = this.pos;
70+
if (tryToEat('(')) { // parentheses
71+
x = parseExpression();
72+
tryToEat(')');
73+
} else if ((currentChar >= '0' && currentChar <= '9') || currentChar == '.') { // numbers
74+
while ((currentChar >= '0' && currentChar <= '9') || currentChar == '.') {
75+
moveToNextChar();
76+
}
77+
x = Double.parseDouble(input.substring(startPos, this.pos));
78+
} else if (currentChar >= 'a' && currentChar <= 'z') { // functions
79+
while (currentChar >= 'a' && currentChar <= 'z') {
80+
moveToNextChar();
81+
}
82+
String func = input.substring(startPos, this.pos);
83+
x = parseFactor();
84+
x = switch (func) {
85+
case "sqrt" -> Math.sqrt(x);
86+
case "sin" -> Math.sin(Math.toRadians(x));
87+
case "cos" -> Math.cos(Math.toRadians(x));
88+
case "tan" -> Math.tan(Math.toRadians(x));
89+
case "log" -> Math.log(x);
90+
default -> throw new ParseException("Unknown function: " + func, startPos);
91+
};
92+
} else {
93+
throw new ParseException("Unexpected: " + (char) currentChar, startPos);
94+
}
95+
96+
if (tryToEat('^')) {
97+
x = Math.pow(x, parseFactor()); // exponentiation
98+
}
99+
100+
return x;
101+
}
102+
103+
private double parseTerm() throws ParseException {
104+
double x = parseFactor();
105+
for (;;) {
106+
if (tryToEat('*'))
107+
x *= parseFactor(); // multiplication
108+
else if (tryToEat('/'))
109+
x /= parseFactor(); // division
110+
else
111+
return x;
112+
}
113+
}
114114

115115
}
116116

117117
public static double eval(final String equation) throws ParseException {
118-
return new Parser(equation).evaluate();
118+
return new Parser(equation).evaluate();
119119
}
120120

121121
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package world.bentobox.level.calculators;
2+
3+
import static org.junit.Assert.assertEquals;
4+
5+
import java.text.ParseException;
6+
7+
import org.junit.Test;
8+
9+
/**
10+
* Test the equation evaluation
11+
*/
12+
public class EquationEvaluatorTest {
13+
14+
/**
15+
* Test method for {@link world.bentobox.level.calculators.EquationEvaluator#eval(java.lang.String)}.
16+
* @throws ParseException
17+
*/
18+
@Test
19+
public void testEval() throws ParseException {
20+
assertEquals(4D, EquationEvaluator.eval("2+2"), 0D);
21+
assertEquals(0D, EquationEvaluator.eval("2-2"), 0D);
22+
assertEquals(1D, EquationEvaluator.eval("2/2"), 0D);
23+
assertEquals(4D, EquationEvaluator.eval("2*2"), 0D);
24+
assertEquals(8D, EquationEvaluator.eval("2+2+2+2"), 0D);
25+
assertEquals(5D, EquationEvaluator.eval("2.5+2.5"), 0D);
26+
assertEquals(1.414, EquationEvaluator.eval("sqrt(2)"), 0.001D);
27+
assertEquals(3.414, EquationEvaluator.eval("2 + sqrt(2)"), 0.001D);
28+
assertEquals(0D, EquationEvaluator.eval("sin(0)"), 0.1D);
29+
assertEquals(1D, EquationEvaluator.eval("cos(0)"), 0.1D);
30+
assertEquals(0D, EquationEvaluator.eval("tan(0)"), 0.1D);
31+
assertEquals(0D, EquationEvaluator.eval("log(1)"), 0.1D);
32+
assertEquals(27D, EquationEvaluator.eval("3^3"), 0.D);
33+
assertEquals(84.70332D, EquationEvaluator.eval("3^3 + 2 + 2.65 * (3 / 4) - sin(45) * log(10) + 55.344"),
34+
0.0001D);
35+
36+
}
37+
}

0 commit comments

Comments
 (0)