package br.ufpr.c3sl.virtualkeyboard.mathevaluator;
import java.util.HashMap;
import java.lang.Double;
/************************************************************************
* <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.<br>
* When the getValue() is called, a Double 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 HashMap<String, Double> variables = new HashMap<String, Double>();
/***
* Main. To run the program in command line.
* Usage: java MathEvaluator.main [your math expression]
*/
public static void main(String[] args)
{
if ( args == null || args.length != 1)
{
System.err.println("Math Expression Evaluator by The-Son LAI Lts@writeme.com C(2001):");
System.err.println("Usage: java MathEvaluator.main [your math expression]");
System.exit(0);
}
try
{
//MathEvaluator m = new MathEvaluator(args[0]);
new MathEvaluator(args[0]);
}
catch (Exception e)
{
e.printStackTrace();
}
}
/***
* creates an empty MathEvaluator. You need to use setExpression(String s) to assign a math expression string to it.
*/
public MathEvaluator()
{
init();
}
/***
* creates a MathEvaluator and assign the math expression string.
*/
public MathEvaluator(String s)
{
init();
setExpression(s);
}
private void init()
{
if ( operators == null ) initializeOperators();
}
/***
* adds a variable and its value in the MathEvaluator
*/
public void addVariable(String v, double val)
{
addVariable(v, new Double(val));
}
/***
* adds a variable and its value in the MathEvaluator
*/
public void addVariable(String v, Double val)
{
variables.put(v, val);
}
/***
* sets the expression
*/
public void setExpression(String s)
{
expression = s;
}
/***
* resets the evaluator
*/
public void reset()
{
node = null;
expression = null;
variables = new HashMap<String, Double>();
}
/***
* trace the binary tree for debug
*/
public void trace()
{
try
{
node = new Node(expression);
node.trace();
}
catch (Exception e)
{
e.printStackTrace();
}
}
/***
* evaluates and returns the value of the expression
*/
public Double getValue()
{
if (expression == null) return null;
try
{
node = new Node(expression);
return evaluate(node);
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
private static Double evaluate(Node n)
{
if ( n.hasOperator() && n.hasChild() )
{
if ( n.getOperator().getType() == 1 )
n.setValue ( evaluateExpression( n.getOperator(), evaluate( n.getLeft() ), null ) );
else if ( n.getOperator().getType() == 2 )
n.setValue( evaluateExpression( n.getOperator(), evaluate( n.getLeft() ), evaluate( n.getRight() ) ) );
}
return n.getValue();
}
private static Double evaluateExpression(Operator o, Double f1, Double f2)
{
String op = o.getOperator();
Double res = null;
if ( "+".equals(op) ) res = new Double( f1.doubleValue() + f2.doubleValue() );
else if ( "-".equals(op) ) res = new Double( f1.doubleValue() - f2.doubleValue() );
else if ( "*".equals(op) ) res = new Double( f1.doubleValue() * f2.doubleValue() );
else if ( "/".equals(op) ) res = new Double( f1.doubleValue() / f2.doubleValue() );
else if ( "^".equals(op) ) res = new Double( Math.pow(f1.doubleValue(), f2.doubleValue()) );
else if ( "%".equals(op) ) res = new Double( f1.doubleValue() % f2.doubleValue() );
else if ( "&".equals(op) ) res = new Double( f1.doubleValue() + f2.doubleValue() ); // todo
else if ( "|".equals(op) ) res = new Double( f1.doubleValue() + f2.doubleValue() ); // todo
else if ( "cos".equals(op) ) res = new Double( Math.cos(f1.doubleValue()) );
else if ( "sin".equals(op) ) res = new Double( Math.sin(f1.doubleValue()) );
else if ( "tan".equals(op) ) res = new Double( Math.tan(f1.doubleValue()) );
else if ( "acos".equals(op) ) res = new Double( Math.acos(f1.doubleValue()) );
else if ( "asin".equals(op) ) res = new Double( Math.asin(f1.doubleValue()) );
else if ( "atan".equals(op) ) res = new Double( Math.atan(f1.doubleValue()) );
else if ( "sqr".equals(op) ) res = new Double( f1.doubleValue() * f1.doubleValue() );
else if ( "sqrt".equals(op) ) res = new Double( Math.sqrt(f1.doubleValue()) );
else if ( "log".equals(op) ) res = new Double( Math.log(f1.doubleValue()) );
else if ( "min".equals(op) ) res = new Double( Math.min(f1.doubleValue(), f2.doubleValue()) );
else if ( "max".equals(op) ) res = new Double( Math.max(f1.doubleValue(), f2.doubleValue()) );
else if ( "exp".equals(op) ) res = new Double( Math.exp(f1.doubleValue()) );
else if ( "floor".equals(op) ) res = new Double( Math.floor(f1.doubleValue()) );
else if ( "ceil".equals(op) ) res = new Double( Math.ceil(f1.doubleValue()) );
else if ( "abs".equals(op) ) res = new Double( Math.abs(f1.doubleValue()) );
else if ( "neg".equals(op) ) res = new Double( - f1.doubleValue() );
else if ( "rnd".equals(op) ) res = new Double( Math.random() * f1.doubleValue() );
return res;
}
private void initializeOperators()
{
operators = new Operator[25];
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, 20);
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);
}
/***
* gets the variable's value that was assigned previously
*/
public Double getVariable(String s)
{
return (Double) variables.get(s);
}
private Double getDouble(String s)
{
if ( s == null ) return null;
Double res = null;
try
{
res = new Double(Double.parseDouble(s));
}
catch(Exception e)
{
return getVariable(s);
}
return res;
}
protected Operator[] getOperators()
{
return operators;
}
protected 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 void setOperator(String o)
{
op = o;
}
public int getType()
{
return type;
}
public int getPriority()
{
return priority;
}
}
protected class Node
{
public String nString = null;
public Operator nOperator = null;
public Node nLeft = null;
public Node nRight = null;
public Node nParent = null;
public int nLevel = 0;
public Double nValue = null;
public Node(String s) throws Exception
{
init(null, s, 0);
}
public Node(Node parent, String s, int level) throws Exception
{
init(parent, s, level);
}
private void init(Node parent, String s, int level) throws Exception
{
s = removeIllegalCharacters(s);
s = removeBrackets(s);
s = addZero(s);
if ( checkBrackets(s) != 0 ) throw new Exception("Wrong number of brackets in [" + s + "]");
nParent = parent;
nString = s;
nValue = getDouble(s);
nLevel = level;
int sLength = s.length();
int inBrackets = 0;
int startOperator = 0;
for (int i=0; i<sLength; i++)
{
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;
}
}
}
}
}
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 = new Node( 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 )
{
//nOperator = nOperator;
nLeft = new Node( this, s.substring(0, startOperator), nLevel + 1 );
nRight = new Node( 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 int getLevel()
{
return nLevel;
}
protected Double getValue()
{
return nValue;
}
protected void setValue(Double 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 += " ";
}
}
protected static void _D(String s)
{
System.err.println(s);
}
}