Revert "Add support for implicit multiply in equations"
This reverts commit a218a8276b
.
This commit is contained in:
@@ -6,8 +6,6 @@ import java.util.NoSuchElementException;
|
|||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements evaluation for simple math expressions using the shunting yard
|
* Implements evaluation for simple math expressions using the shunting yard
|
||||||
* algorithm.
|
* algorithm.
|
||||||
@@ -15,62 +13,47 @@ import javax.annotation.Nullable;
|
|||||||
public class EquationEvaluator {
|
public class EquationEvaluator {
|
||||||
|
|
||||||
public static double evaluate(String input) {
|
public static double evaluate(String input) {
|
||||||
final StringTokenizer tokens = new StringTokenizer(input, "+-*/()", true);
|
StringTokenizer tokens = new StringTokenizer(input, "+-*/()", true);
|
||||||
final Deque<String> operators = new ArrayDeque<>();
|
Deque<String> operators = new ArrayDeque<>();
|
||||||
final Deque<Double> operands = new ArrayDeque<>();
|
Deque<Double> operands = new ArrayDeque<>();
|
||||||
String lastToken = null;
|
String lastToken = null;
|
||||||
while (tokens.hasMoreTokens()) {
|
while (tokens.hasMoreTokens()) {
|
||||||
String rawToken = tokens.nextToken().trim();
|
String rawToken = tokens.nextToken().trim();
|
||||||
if (rawToken.isEmpty())
|
if (rawToken.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
if (isUnaryMinus(rawToken, lastToken)) {
|
|
||||||
rawToken = "u";
|
boolean isUnaryMinus = rawToken.equals("-") && (lastToken == null
|
||||||
} else if (isImplicitMultiply(rawToken, lastToken)) {
|
|| (!lastToken.equals("u") && isOperator(lastToken)) || lastToken.equals("("));
|
||||||
lastToken = processToken(input, "*", operators, operands);
|
final String token = isUnaryMinus ? "u" : rawToken;
|
||||||
|
if (isOperator(token)) {
|
||||||
|
popOperators(input, operators, operands,
|
||||||
|
top -> (!top.equals("(") && getPrecedence(token) <= getPrecedence(top)));
|
||||||
|
operators.push(token);
|
||||||
|
} else if (token.equals("(")) {
|
||||||
|
operators.push(token);
|
||||||
|
} else if (token.equals(")")) {
|
||||||
|
popOperators(input, operators, operands, top -> !top.equals("("));
|
||||||
|
if (!operators.pop().equals("(")) {
|
||||||
|
throw new IllegalArgumentException("Unbalanced right parentheses in expression: " + input);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
operands.push(Double.parseDouble(token));
|
||||||
|
} catch (NumberFormatException exception) {
|
||||||
|
throw new IllegalArgumentException("Could not evaluate expression: " + input);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
lastToken = processToken(input, rawToken, operators, operands);
|
lastToken = token;
|
||||||
}
|
}
|
||||||
popOperators(input, operators, operands, top -> true);
|
popOperators(input, operators, operands, top -> true);
|
||||||
return operands.pop();
|
return operands.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isUnaryMinus(String rawToken, @Nullable String lastToken) {
|
|
||||||
return rawToken.equals("-")
|
|
||||||
&& (lastToken == null || (isOperator(lastToken) && !lastToken.equals("u")) || lastToken.equals("("));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isImplicitMultiply(String rawToken, @Nullable String lastToken) {
|
|
||||||
return rawToken.equals("(")
|
|
||||||
&& (lastToken != null && !isOperator(lastToken) && !lastToken.equals("(") && !lastToken.equals(")"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String processToken(String input, String token, Deque<String> operators, Deque<Double> operands) {
|
|
||||||
if (isOperator(token)) {
|
|
||||||
popOperators(input, operators, operands,
|
|
||||||
top -> (!top.equals("(") && getPrecedence(token) <= getPrecedence(top)));
|
|
||||||
operators.push(token);
|
|
||||||
} else if (token.equals("(")) {
|
|
||||||
operators.push(token);
|
|
||||||
} else if (token.equals(")")) {
|
|
||||||
popOperators(input, operators, operands, top -> !top.equals("("));
|
|
||||||
if (!operators.pop().equals("(")) {
|
|
||||||
throw new IllegalArgumentException("Unbalanced right parentheses in expression: " + input);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
operands.push(Double.parseDouble(token));
|
|
||||||
} catch (NumberFormatException exception) {
|
|
||||||
throw new IllegalArgumentException("Could not parse double from:" + token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void popOperators(String input, Deque<String> operators, Deque<Double> operands,
|
private static void popOperators(String input, Deque<String> operators, Deque<Double> operands,
|
||||||
Predicate<String> predicate) {
|
Predicate<String> predicate) {
|
||||||
try {
|
try {
|
||||||
while (!operators.isEmpty() && predicate.test(operators.peek())) {
|
while (!operators.isEmpty() && predicate.test(operators.peek())) {
|
||||||
final String op = operators.pop();
|
String op = operators.pop();
|
||||||
if (op.equals("u")) {
|
if (op.equals("u")) {
|
||||||
operands.push(-operands.pop());
|
operands.push(-operands.pop());
|
||||||
continue;
|
continue;
|
||||||
@@ -96,8 +79,8 @@ public class EquationEvaluator {
|
|||||||
throw new IllegalArgumentException("Unexpected arithmetic operation " + op);
|
throw new IllegalArgumentException("Unexpected arithmetic operation " + op);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isOperator(String token) {
|
private static boolean isOperator(String op) {
|
||||||
switch (token) {
|
switch (op) {
|
||||||
case "+":
|
case "+":
|
||||||
case "-":
|
case "-":
|
||||||
case "*":
|
case "*":
|
||||||
|
@@ -61,17 +61,4 @@ class EquationEvaluatorTest {
|
|||||||
assertFuzzyEquals(1, EquationEvaluator.evaluate("1 - 2 + 3 * 4 / 6"));
|
assertFuzzyEquals(1, EquationEvaluator.evaluate("1 - 2 + 3 * 4 / 6"));
|
||||||
assertFuzzyEquals(4.0 / 3, EquationEvaluator.evaluate("(((1 - 2) + 3) * 4) / 6"));
|
assertFuzzyEquals(4.0 / 3, EquationEvaluator.evaluate("(((1 - 2) + 3) * 4) / 6"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void testDivideByZero() {
|
|
||||||
assertFuzzyEquals(Double.POSITIVE_INFINITY, EquationEvaluator.evaluate("1 / 0"));
|
|
||||||
assertFuzzyEquals(Double.POSITIVE_INFINITY, EquationEvaluator.evaluate("1 / (1 - 1)"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testImplicitMultiply() {
|
|
||||||
assertFuzzyEquals(-2, EquationEvaluator.evaluate("2(3 + 6) - 5(6 - 2)"));
|
|
||||||
assertFuzzyEquals(1, EquationEvaluator.evaluate("6 / (2(4 - 1))"));
|
|
||||||
assertFuzzyEquals(6 * 5 * 4 * 3 * 2, EquationEvaluator.evaluate("6(5(4(3(2(1)))))"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user