Skip to content

Commit

Permalink
Added test class for EquationEvaluator
Browse files Browse the repository at this point in the history
  • Loading branch information
tastybento committed Jan 13, 2024
1 parent 2b373f6 commit 76e0bad
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 105 deletions.
210 changes: 105 additions & 105 deletions src/main/java/world/bentobox/level/calculators/EquationEvaluator.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,119 +3,119 @@
import java.text.ParseException;

/**
* @author tastybento
* Utility class to evaluate equations
*/
public class EquationEvaluator {

private static class Parser {
private final String input;
private int pos = -1;
private int currentChar;

public Parser(String input) {
this.input = input;
moveToNextChar();
}

private void moveToNextChar() {
currentChar = (++pos < input.length()) ? input.charAt(pos) : -1;
}

private boolean tryToEat(int charToEat) {
while (currentChar == ' ')
moveToNextChar();
if (currentChar == charToEat) {
moveToNextChar();
return true;
}
return false;
}

public double evaluate() throws ParseException {
double result = parseExpression();
if (pos < input.length()) {
throw new ParseException("Unexpected character: " + (char) currentChar, pos);
}
return result;
}

private double parseExpression() throws ParseException {
double result = parseTerm();
while (true) {
if (tryToEat('+'))
result += parseTerm();
else if (tryToEat('-'))
result -= parseTerm();
else
return result;
}
}

private double parseFactor() throws ParseException {
if (tryToEat('+'))
return parseFactor(); // unary plus
if (tryToEat('-'))
return -parseFactor(); // unary minus

double x;
int startPos = this.pos;
if (tryToEat('(')) { // parentheses
x = parseExpression();
tryToEat(')');
} else if ((currentChar >= '0' && currentChar <= '9') || currentChar == '.') { // numbers
while ((currentChar >= '0' && currentChar <= '9') || currentChar == '.')
moveToNextChar();
x = Double.parseDouble(input.substring(startPos, this.pos));
} else if (currentChar >= 'a' && currentChar <= 'z') { // functions
while (currentChar >= 'a' && currentChar <= 'z')
moveToNextChar();
String func = input.substring(startPos, this.pos);
x = parseFactor();
switch (func) {
case "sqrt":
x = Math.sqrt(x);
break;
case "sin":
x = Math.sin(Math.toRadians(x));
break;
case "cos":
x = Math.cos(Math.toRadians(x));
break;
case "tan":
x = Math.tan(Math.toRadians(x));
break;
case "log":
x = Math.log(x);
break;
default:
throw new ParseException("Unknown function: " + func, startPos);
}
} else {
throw new ParseException("Unexpected: " + (char) currentChar, startPos);
}

if (tryToEat('^'))
x = Math.pow(x, parseFactor()); // exponentiation

return x;
}

private double parseTerm() throws ParseException {
double x = parseFactor();
for (;;) {
if (tryToEat('*'))
x *= parseFactor(); // multiplication
else if (tryToEat('/'))
x /= parseFactor(); // division
else
return x;
}
}
private final String input;
private int pos = -1;
private int currentChar;

@SuppressWarnings("unused")
private Parser() {
throw new IllegalStateException("Utility class");
}

public Parser(String input) {
this.input = input;
moveToNextChar();
}

private void moveToNextChar() {
currentChar = (++pos < input.length()) ? input.charAt(pos) : -1;
}

private boolean tryToEat(int charToEat) {
while (currentChar == ' ') {
moveToNextChar();
}
if (currentChar == charToEat) {
moveToNextChar();
return true;
}
return false;
}

public double evaluate() throws ParseException {
double result = parseExpression();
if (pos < input.length()) {
throw new ParseException("Unexpected character: " + (char) currentChar, pos);
}
return result;
}

private double parseExpression() throws ParseException {
double result = parseTerm();
while (true) {
if (tryToEat('+')) {
result += parseTerm();
} else if (tryToEat('-')) {
result -= parseTerm();
} else {
return result;
}
}
}

private double parseFactor() throws ParseException {
if (tryToEat('+')) {
return parseFactor(); // unary plus
}
if (tryToEat('-')) {
return -parseFactor(); // unary minus
}
double x;
int startPos = this.pos;
if (tryToEat('(')) { // parentheses
x = parseExpression();
tryToEat(')');
} else if ((currentChar >= '0' && currentChar <= '9') || currentChar == '.') { // numbers
while ((currentChar >= '0' && currentChar <= '9') || currentChar == '.') {
moveToNextChar();
}
x = Double.parseDouble(input.substring(startPos, this.pos));
} else if (currentChar >= 'a' && currentChar <= 'z') { // functions
while (currentChar >= 'a' && currentChar <= 'z') {
moveToNextChar();
}
String func = input.substring(startPos, this.pos);
x = parseFactor();
x = switch (func) {
case "sqrt" -> Math.sqrt(x);
case "sin" -> Math.sin(Math.toRadians(x));
case "cos" -> Math.cos(Math.toRadians(x));
case "tan" -> Math.tan(Math.toRadians(x));
case "log" -> Math.log(x);
default -> throw new ParseException("Unknown function: " + func, startPos);
};
} else {
throw new ParseException("Unexpected: " + (char) currentChar, startPos);
}

if (tryToEat('^')) {
x = Math.pow(x, parseFactor()); // exponentiation
}

return x;
}

private double parseTerm() throws ParseException {
double x = parseFactor();
for (;;) {
if (tryToEat('*'))
x *= parseFactor(); // multiplication
else if (tryToEat('/'))
x /= parseFactor(); // division
else
return x;
}
}

}

public static double eval(final String equation) throws ParseException {
return new Parser(equation).evaluate();
return new Parser(equation).evaluate();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package world.bentobox.level.calculators;

import static org.junit.Assert.assertEquals;

import java.text.ParseException;

import org.junit.Test;

/**
* Test the equation evaluation
*/
public class EquationEvaluatorTest {

/**
* Test method for {@link world.bentobox.level.calculators.EquationEvaluator#eval(java.lang.String)}.
* @throws ParseException
*/
@Test
public void testEval() throws ParseException {
assertEquals(4D, EquationEvaluator.eval("2+2"), 0D);
assertEquals(0D, EquationEvaluator.eval("2-2"), 0D);
assertEquals(1D, EquationEvaluator.eval("2/2"), 0D);
assertEquals(4D, EquationEvaluator.eval("2*2"), 0D);
assertEquals(8D, EquationEvaluator.eval("2+2+2+2"), 0D);
assertEquals(5D, EquationEvaluator.eval("2.5+2.5"), 0D);
assertEquals(1.414, EquationEvaluator.eval("sqrt(2)"), 0.001D);
assertEquals(3.414, EquationEvaluator.eval("2 + sqrt(2)"), 0.001D);
assertEquals(0D, EquationEvaluator.eval("sin(0)"), 0.1D);
assertEquals(1D, EquationEvaluator.eval("cos(0)"), 0.1D);
assertEquals(0D, EquationEvaluator.eval("tan(0)"), 0.1D);
assertEquals(0D, EquationEvaluator.eval("log(1)"), 0.1D);
assertEquals(27D, EquationEvaluator.eval("3^3"), 0.D);
assertEquals(84.70332D, EquationEvaluator.eval("3^3 + 2 + 2.65 * (3 / 4) - sin(45) * log(10) + 55.344"),
0.0001D);

}
}

0 comments on commit 76e0bad

Please sign in to comment.