Last active
April 15, 2018 23:09
-
-
Save boxbeam/8f2e005909f2fa48d46eb45dc84e8ed8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package redempt.polishnotation; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.List; | |
import java.util.function.BiFunction; | |
import java.util.stream.Collectors; | |
public class Expression { | |
private String expression; | |
public Expression(String expression) { | |
this.expression = parse(expression); | |
} | |
private static String parse(String input) { | |
for (Constant constant : Constant.values()) { | |
input = input.replace(constant.getName(), constant.getValue() + ""); | |
} | |
input = input.replace(" ", ""); | |
input = input.replace("-x", "-1*x"); | |
input = input.replaceAll("([0-9.a-zA-Z)])-([0-9.a-zA-Z(])", "$1m$2"); | |
input = input.replaceAll("([0-9x)])x", "$1*x"); | |
input = input.replaceAll("\\)\\(", ")*("); | |
input = input.replaceAll("\\(([0-9x-]+)\\)", "$1"); | |
for (Operation operation : Operation.values()) { | |
if (operation.getAltName() != null) { | |
input = input.replace(operation.getAltName(), operation.getName() + ""); | |
} | |
} | |
char[] chars = input.toCharArray(); | |
String output = ""; | |
for (int i = 0; i < chars.length; i++) { | |
char c = chars[i]; | |
if (c == '(' || c == ')') { | |
output += c + " "; | |
continue; | |
} | |
for (Operation operation : Operation.values()) { | |
if (operation.getName() != c) { | |
continue; | |
} | |
output += c + " "; | |
} | |
} | |
output = output.trim(); | |
output += "`"; | |
String[] split = input.split("[^0-9-x.]"); | |
for (String num : split) { | |
if (num.equals("")) { | |
continue; | |
} | |
output += num + " "; | |
} | |
return output.trim(); | |
} | |
public double evaluate(double value) { | |
String[] split = expression.split("`"); | |
List<Operation> operations = Arrays.stream(split[0].split(" ")).map(Operation::byName).collect(Collectors.toList()); | |
List<Double> numbers = Arrays.stream(split[1].split(" ")).map(c -> c.replace("x", value + "")).map(Double::parseDouble).collect(Collectors.toList()); | |
return evaluate(operations, numbers); | |
} | |
public double evaluate() { | |
return evaluate(0); | |
} | |
private static double evaluate(List<Operation> operations, List<Double> numbers) { | |
stuff: | |
while (numbers.size() >= 1 && operations.size() > 0) { | |
int lowest = -1; | |
for (int i = 0; i < operations.size(); i++) { | |
Operation operation = operations.get(i); | |
if (lowest == -1 || operation.getOrder() < operations.get(lowest).getOrder()) { | |
lowest = i; | |
} | |
} | |
Operation operation = operations.get(lowest); | |
if (operation == null) { | |
return numbers.get(0); | |
} | |
if (operation == Operation.OPAREN) { | |
int depth = 1; | |
int amount = 0; | |
for (int x = lowest + 1; x < operations.size(); x++) { | |
amount++; | |
if (operations.get(x) == Operation.OPAREN) { | |
depth++; | |
} | |
if (operations.get(x) == Operation.CPAREN) { | |
depth--; | |
if (depth == 0) { | |
int start = 0; | |
for (int i = 0; i < lowest; i++) { | |
if (operations.get(i).takesTwoNumbers()) { | |
start++; | |
} | |
} | |
List<Operation> insideOperations = new ArrayList<>(operations.subList(lowest + 1, x)); | |
List<Double> insideNumbers = new ArrayList<>(numbers.subList(start, numbers.size())); | |
double num = evaluate(insideOperations, insideNumbers); | |
for (int i = 0; i < amount + 1 && lowest < operations.size(); i++) { | |
if (operations.remove(lowest).takesTwoNumbers()) { | |
numbers.remove(lowest); | |
} | |
} | |
numbers.set(start, num); | |
continue stuff; | |
} | |
} | |
} | |
} | |
if (operation == Operation.CPAREN) { | |
return 0; | |
} | |
if (operation.takesTwoNumbers()) { | |
double num = operation.apply(numbers.get(lowest), numbers.get(lowest + 1)); | |
operations.remove(lowest); | |
numbers.remove(lowest); | |
numbers.set(lowest, num); | |
} else { | |
double num = operation.apply(numbers.get(lowest), 0); | |
operations.remove(lowest); | |
numbers.set(lowest, num); | |
} | |
} | |
return numbers.get(0); | |
} | |
public static enum Constant { | |
E("e", Math.E), | |
PI("pi", Math.PI); | |
private String name; | |
private double value; | |
private Constant(String name, double value) { | |
this.name = name; | |
this.value = value; | |
} | |
public String getName() { | |
return name; | |
} | |
public double getValue() { | |
return value; | |
} | |
} | |
public static enum Operation { | |
ADD(null, '+', (a, b) -> a + b, true, 2), | |
SUBTRACT(null, 'm', (a, b) -> a - b, true, 2), | |
DIVIDE(null, '/', (a, b) -> a / b, true, 1), | |
MODULUS(null, '%', (a, b) -> a % b, true, 1), | |
MULTIPLY(null, '*', (a, b) -> a * b, true, 1), | |
EXPONENT(null, '^', (a, b) -> Math.pow(a, b), true, 0), | |
SINE("sin", 's', (a, b) -> Math.sin(a), false, 0), | |
ASIN("as", 'u', (a, b) -> Math.asin(a), false, 0), | |
COSINE("cos", 'c', (a, b) -> Math.cos(a), false, 0), | |
ACOS("ac", 'y', (a, b) -> Math.acos(a), false, 0), | |
TANGENT("tan", 't', (a, b) -> Math.tan(a), false, 0), | |
ATAN("at", 'i', (a, b) -> Math.atan(a), false, 0), | |
RAD("rad", 'r', (a, b) -> Math.toRadians(a), false, 0), | |
ABS("abs", 'a', (a, b) -> Math.abs(a), false, 0), | |
LOG("log", 'l', (a, b) -> Math.log10(a), false, 0), | |
LN("ln", 'n', (a, b) -> Math.log(a), false, 0), | |
OPAREN(null, '(', (a, b) -> a, false, -2), | |
CPAREN(null, ')', (a, b) -> a, false, -1); | |
private String altname; | |
private char name; | |
private BiFunction<Double, Double, Double> func; | |
private boolean twoNumbers; | |
private int order; | |
private Operation(String altname, char name, BiFunction<Double, Double, Double> func, boolean twoNumbers, int order) { | |
this.altname = altname; | |
this.name = name; | |
this.func = func; | |
this.twoNumbers = twoNumbers; | |
this.order = order; | |
} | |
public int getOrder() { | |
return order; | |
} | |
public String getAltName() { | |
return altname; | |
} | |
public char getName() { | |
return name; | |
} | |
public double apply(double num1, double num2) { | |
return func.apply(num1, num2); | |
} | |
public boolean takesTwoNumbers() { | |
return twoNumbers; | |
} | |
public static Operation byName(String name) { | |
if (name == null || name.length() == 0) { | |
return null; | |
} | |
for (Operation operation : values()) { | |
if (operation.getName() == name.charAt(0)) { | |
return operation; | |
} | |
} | |
return null; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment