/** * eAdventure (formerly <e-Adventure> and <e-Game>) is a research project of the * <e-UCM> research group. * * Copyright 2005-2010 <e-UCM> research group. * * You can access a list of all the contributors to eAdventure at: * http://e-adventure.e-ucm.es/contributors * * <e-UCM> is a research group of the Department of Software Engineering * and Artificial Intelligence at the Complutense University of Madrid * (School of Computer Science). * * C Profesor Jose Garcia Santesmases sn, * 28040 Madrid (Madrid), Spain. * * For more info please visit: <http://e-adventure.e-ucm.es> or * <http://www.e-ucm.es> * * **************************************************************************** * * This file is part of eAdventure, version 2.0 * * eAdventure is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * eAdventure is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with eAdventure. If not, see <http://www.gnu.org/licenses/>. */ package es.eucm.ead.tools; import es.eucm.ead.model.elements.extra.EAdList; import es.eucm.ead.model.elements.operations.Operation; /************************************************************************ * <i>Mathematic expression evaluator.</i> Supports the following functions: +, * -, *, /, ^, %, cos, sin, tan, acos, asin, atan, sqrt, sqr, log, min, max, * ceil, floor, abs, neg, rndr, deg.<br> * When the getValue() is called, a float object is returned. If it returns * null, an error occured. * <p> * * <pre> * Sample: * MathEvaluator m = new MathEvaluator("-5-6/(-2) + sqr(15+x)"); * m.addVariable("x", 15.1d); * System.out.println( m.getValue() ); * </pre> * * @version 1.1 * @author The-Son LAI, <a href="mailto:Lts@writeme.com">Lts@writeme.com</a> * @date April 2001 ************************************************************************/ public class MathEvaluator { protected static Operator[] operators = null; private Node node = null; private String expression = null; private OperationResolver operationResolver; private EAdList<Operation> operationsList; private Pool<Node> pool = new Pool<Node>() { @Override protected Node newObject() { return new Node(); } }; /*** * creates an empty MathEvaluator. You need to use setExpression(String s) * to assign a math expression string to it. */ public MathEvaluator() { init(); node = new Node(); } /*** * creates a MathEvaluator and assign the math expression string. */ public MathEvaluator(String s, OperationResolver variables, EAdList<Operation> varList) { init(); setExpression(s, variables, varList); } private void init() { if (operators == null) initializeOperators(); } /*** * sets the expression * */ public void setExpression(String s, OperationResolver operationResolver, EAdList<Operation> varList) { expression = s; this.operationResolver = operationResolver; this.operationsList = varList; } /*** * resets the evaluator */ public void reset() { node = null; expression = null; } /*** * trace the binary tree for debug */ public void trace() { try { node = pool.obtain(); node.init(null, expression, 0); node.trace(); } catch (Exception e) { e.printStackTrace(); } } /*** * evaluates and returns the value of the expression */ public Float getValue() { if (expression == null) return Float.valueOf(0); try { node.free(); node = pool.obtain(); node.init(null, expression, 0); return evaluate(node); } catch (Exception e) { e.printStackTrace(); return Float.valueOf(0); } } private static float evaluate(Node n) { if (n.hasOperator() && n.hasChild()) { if (n.getOperator().getType() == 1) n.setValue(evaluateExpression(n.getOperator(), evaluate(n .getLeft()), 0)); else if (n.getOperator().getType() == 2) n.setValue(evaluateExpression(n.getOperator(), evaluate(n .getLeft()), evaluate(n.getRight()))); } return n.getValue(); } private static float evaluateExpression(Operator o, float f1, float f2) { String op = o.getOperator(); float res = 0; if ("+".equals(op)) res = f1 + f2; else if ("-".equals(op)) res = f1 - f2; else if ("*".equals(op)) res = f1 * f2; else if ("/".equals(op)) res = f1 / f2; else if ("^".equals(op)) res = (float) Math.pow(f1, f2); else if ("%".equals(op)) res = f1 % f2; else if ("&".equals(op)) res = f1 + f2; else if ("|".equals(op)) res = f1 + f2; else if ("cos".equals(op)) res = (float) Math.cos(f1); else if ("sin".equals(op)) res = (float) Math.sin(f1); else if ("tan".equals(op)) res = (float) Math.tan(f1); else if ("acos".equals(op)) res = (float) Math.acos(f1); else if ("asin".equals(op)) res = (float) Math.asin(f1); else if ("atan".equals(op)) res = (float) Math.atan(f1); else if ("sqr".equals(op)) res = f1 * f1; else if ("sqrt".equals(op)) res = (float) Math.sqrt(f1); else if ("log".equals(op)) res = (float) Math.log(f1); else if ("min".equals(op)) res = Math.min(f1, f2); else if ("max".equals(op)) res = Math.max(f1, f2); else if ("exp".equals(op)) res = (float) Math.exp(f1); else if ("floor".equals(op)) res = (float) Math.floor(f1); else if ("ceil".equals(op)) res = (float) Math.ceil(f1); else if ("abs".equals(op)) res = Math.abs(f1); else if ("neg".equals(op)) res = -f1; else if ("rnd".equals(op)) res = (float) (Math.random() * f1); else if ("deg".equals(op)) { res = (float) Math.toDegrees(f1); } return res; } private void initializeOperators() { operators = new Operator[26]; operators[0] = new Operator("+", 2, 0); operators[1] = new Operator("-", 2, 0); operators[2] = new Operator("*", 2, 10); operators[3] = new Operator("/", 2, 10); operators[4] = new Operator("^", 2, 10); operators[5] = new Operator("%", 2, 10); operators[6] = new Operator("&", 2, 0); operators[7] = new Operator("|", 2, 0); operators[8] = new Operator("cos", 1, 20); operators[9] = new Operator("sin", 1, 20); operators[10] = new Operator("tan", 1, 20); operators[11] = new Operator("acos", 1, 20); operators[12] = new Operator("asin", 1, 20); operators[13] = new Operator("atan", 1, 20); operators[14] = new Operator("sqrt", 1, 20); operators[15] = new Operator("sqr", 1, 20); operators[16] = new Operator("log", 1, 20); operators[17] = new Operator("min", 2, 0); operators[18] = new Operator("max", 2, 0); operators[19] = new Operator("exp", 1, 20); operators[20] = new Operator("floor", 1, 20); operators[21] = new Operator("ceil", 1, 20); operators[22] = new Operator("abs", 1, 20); operators[23] = new Operator("neg", 1, 20); operators[24] = new Operator("rnd", 1, 20); operators[25] = new Operator("deg", 1, 20); } /*** * gets the variable's value that was assigned previously */ public float getVariable(String id) { try { id = id.replace("[", ""); id = id.replace("]", ""); int index = Integer.parseInt(id); Operation number = operationsList.get(index); Object o = operationResolver.operate(number); if (o instanceof Number) { return ((Number) o).floatValue(); } else return 0.0f; } catch (NumberFormatException e) { return 0.0f; } } private float getfloat(String s) { if (s == null) return 0; float res = 0; try { res = Float.parseFloat(s); } catch (Exception e) { return getVariable(s); } return res; } protected Operator[] getOperators() { return operators; } private class Operator { private String op; private int type; private int priority; public Operator(String o, int t, int p) { op = o; type = t; priority = p; } public String getOperator() { return op; } public int getType() { return type; } public int getPriority() { return priority; } } private class Node { public String nString = null; public Operator nOperator = null; public Node nLeft = null; public Node nRight = null; public int nLevel = 0; public float nValue = 0; public Node() { } public void free() { if (nLeft != null) { nLeft.free(); nLeft = null; } if (nRight != null) { nRight.free(); nRight = null; } pool.free(this); } private void init(Node parent, String s, int level) throws Exception { nString = null; nOperator = null; nLeft = null; nRight = null; nLevel = 0; nValue = 0; s = removeIllegalCharacters(s); s = removeBrackets(s); s = addZero(s); if (checkBrackets(s) != 0) throw new Exception("Wrong number of brackets in [" + s + "]"); nString = s; nValue = getfloat(s); nLevel = level; int sLength = s.length(); int inBrackets = 0; int startOperator = 0; int i = 0; while (i < sLength) { if (s.charAt(i) == '(') inBrackets++; else if (s.charAt(i) == ')') inBrackets--; else { // the expression must be at "root" level if (inBrackets == 0) { Operator o = getOperator(nString, i); if (o != null) { // if first operator or lower priority operator if (nOperator == null || nOperator.getPriority() >= o .getPriority()) { nOperator = o; startOperator = i; i += nOperator.op.length() - 1; } } } } i++; } if (nOperator != null) { // one operand, should always be at the beginning if (startOperator == 0 && nOperator.getType() == 1) { // the brackets must be ok if (checkBrackets(s.substring(nOperator.getOperator() .length())) == 0) { nLeft = pool.obtain(); nLeft.init(this, s.substring(nOperator.getOperator() .length()), nLevel + 1); nRight = null; return; } else throw new Exception( "Error during parsing... missing brackets in [" + s + "]"); } // two operands else if (startOperator > 0 && nOperator.getType() == 2) { nLeft = pool.obtain(); nLeft.init(this, s.substring(0, startOperator), nLevel + 1); nRight = pool.obtain(); nRight.init(this, s.substring(startOperator + nOperator.getOperator().length()), nLevel + 1); } } } private Operator getOperator(String s, int start) { Operator[] operators = getOperators(); String temp = s.substring(start); temp = getNextWord(temp); for (int i = 0; i < operators.length; i++) { if (temp.startsWith(operators[i].getOperator())) return operators[i]; } return null; } private String getNextWord(String s) { int sLength = s.length(); for (int i = 1; i < sLength; i++) { char c = s.charAt(i); if ((c > 'z' || c < 'a') && (c > '9' || c < '0')) return s.substring(0, i); } return s; } /*** * checks if there is any missing brackets * * @return true if s is valid */ protected int checkBrackets(String s) { int sLength = s.length(); int inBracket = 0; for (int i = 0; i < sLength; i++) { if (s.charAt(i) == '(' && inBracket >= 0) inBracket++; else if (s.charAt(i) == ')') inBracket--; } return inBracket; } /*** * returns a string that doesnt start with a + or a - */ protected String addZero(String s) { if (s.startsWith("+") || s.startsWith("-")) { int sLength = s.length(); for (int i = 0; i < sLength; i++) { if (getOperator(s, i) != null) return "0" + s; } } return s; } /*** * displays the tree of the expression */ public void trace() { String op = getOperator() == null ? " " : getOperator() .getOperator(); _D(op + " : " + getString()); if (this.hasChild()) { if (hasLeft()) getLeft().trace(); if (hasRight()) getRight().trace(); } } protected boolean hasChild() { return (nLeft != null || nRight != null); } protected boolean hasOperator() { return (nOperator != null); } protected boolean hasLeft() { return (nLeft != null); } protected Node getLeft() { return nLeft; } protected boolean hasRight() { return (nRight != null); } protected Node getRight() { return nRight; } protected Operator getOperator() { return nOperator; } protected float getValue() { return nValue; } protected void setValue(float f) { nValue = f; } protected String getString() { return nString; } /*** * Removes spaces, tabs and brackets at the begining */ public String removeBrackets(String s) { String res = s; if (s.length() > 2 && res.startsWith("(") && res.endsWith(")") && checkBrackets(s.substring(1, s.length() - 1)) == 0) { res = res.substring(1, res.length() - 1); } if (res != s) return removeBrackets(res); else return res; } /*** * Removes illegal characters */ public String removeIllegalCharacters(String s) { char[] illegalCharacters = { ' ' }; String res = s; for (int j = 0; j < illegalCharacters.length; j++) { int i = res.lastIndexOf(illegalCharacters[j], res.length()); while (i != -1) { String temp = res; res = temp.substring(0, i); res += temp.substring(i + 1); i = res.lastIndexOf(illegalCharacters[j], s.length()); } } return res; } protected void _D(String s) { String nbSpaces = ""; for (int i = 0; i < nLevel; i++) nbSpaces += " "; System.out.println(nbSpaces + "|" + s); } } protected static void _D(String s) { System.err.println(s); } public interface OperationResolver { /** * <p> * Calculates the result of the given {@link es.eucm.ead.model.elements.operations.Operation} with the current * game state * </p> * * @param <T> the operation class * @param operation operation to be done * @return operation's result. If operation is {@code null}, a null is * returned. */ <T extends Operation, S> S operate(T operation); } }