// Simple Java Math Expression Evaluator
// Usage example:
// java Evaluator "1+-min(-33,+4)*sin(0.5-0.1e-7) -atan2(3,4)  +1/(0.051e-5)"
// 1+-min(-33,+4)*sin(0.5-0.1e-7)-atan2(3,4)+1/(0.051e-5) = 1960801.7782690835
//
// (c) 2008 Julian Bunn, Caltech. Julian.Bunn@caltech.edu
// You are free to use this code however you see fit. Please let me know if
// you find any bugs, or would like to suggest enhancements


import java.util.Vector;
public class Evaluator
{
	String expression;
	String[] Operators;
	int[] OperatorsArgs;
	boolean[] OperatorsLeftRight;
	int[] OperatorsPrecedence;
	int NOPERATORS;
	Vector tokenStrings;


    public static void main(String[] args)
    {
    	if ( args == null || args.length != 1)
        {
            System.exit(0);
        }

    	try
        {
		   	Evaluator m = new Evaluator(args[0]);
        }
        catch (Exception e)
        {
        }
    }

	public Evaluator(String s)
	{
		init();
		expression = new String(s);
		expression.trim();
		// remove all blanks from the string
		while (expression.indexOf(" ") > -1)
		{
			int i1 = expression.indexOf(" ");
			expression = expression.substring(0, i1) + expression.substring(i1 + 1);
		}
		double d = evaluate();
		System.out.println("\n\n"+expression+" = " + d);
	}

	private void init()
	{
		NOPERATORS = 16;
		Operators = new String[NOPERATORS];
		OperatorsArgs = new int[NOPERATORS];
		OperatorsLeftRight = new boolean[NOPERATORS];
		OperatorsPrecedence = new int[NOPERATORS];

		Operators[0] = "+";
		OperatorsArgs[0] = 2;
		OperatorsLeftRight[0] = true;
		OperatorsPrecedence[0] = 2;

		Operators[1] = "-";
		OperatorsArgs[1] = 2;
		OperatorsLeftRight[1] = true;
		OperatorsPrecedence[1] = 2;

		Operators[2] = "*";
		OperatorsArgs[2] = 2;
		OperatorsLeftRight[2] = true;
		OperatorsPrecedence[2] = 3;

		Operators[3] = "/";
		OperatorsArgs[3] = 2;
		OperatorsLeftRight[3] = true;
		OperatorsPrecedence[3] = 3;

		Operators[4] = "sin";
		OperatorsArgs[4] = 1;
		OperatorsLeftRight[4] = false;
		OperatorsPrecedence[4] = 10;

		Operators[5] = "cos";
		OperatorsArgs[5] = 1;
		OperatorsLeftRight[5] = false;
		OperatorsPrecedence[5] = 10;

		Operators[6] = "tan";
		OperatorsArgs[6] = 1;
		OperatorsLeftRight[6] = false;
		OperatorsPrecedence[6] = 10;

		Operators[7] = "exp";
		OperatorsArgs[7] = 1;
		OperatorsLeftRight[7] = false;
		OperatorsPrecedence[7] = 10;

		Operators[8] = "sqrt";
		OperatorsArgs[8] = 1;
		OperatorsLeftRight[8] = false;
		OperatorsPrecedence[8] = 10;

		Operators[9] = "asin";
		OperatorsArgs[9] = 1;
		OperatorsLeftRight[9] = false;
		OperatorsPrecedence[9] = 10;

		Operators[10] = "acos";
		OperatorsArgs[10] = 1;
		OperatorsLeftRight[10] = false;
		OperatorsPrecedence[10] = 10;

		Operators[11] = "atan";
		OperatorsArgs[11] = 1;
		OperatorsLeftRight[11] = false;
		OperatorsPrecedence[11] = 10;

		Operators[12] = "atan2";
		OperatorsArgs[12] = 2;
		OperatorsLeftRight[12] = false;
		OperatorsPrecedence[12] = 10;

		Operators[13] = "max";
		OperatorsArgs[13] = 2;
		OperatorsLeftRight[13] = false;
		OperatorsPrecedence[13] = 10;

		Operators[14] = "min";
		OperatorsArgs[14] = 2;
		OperatorsLeftRight[14] = false;
		OperatorsPrecedence[14] = 10;

		Operators[15] = ",";
		OperatorsArgs[15] = 2;
		OperatorsLeftRight[15] = true;
		OperatorsPrecedence[15] = 1;

	}


	private double evaluate()
	{
		return eval(expression);
	}

	private int getOperator(String op)
	{
		for (int i = 0; i < NOPERATORS; i++)
		{
			if (op.equals(Operators[i]) && op.length() == Operators[i].length()) return i;
		}
		return -1;
	}


	private double doOp(String op, double arg1, double arg2)
	{
		if (op.equals("+")) return (arg1 + arg2);
		if (op.equals("-")) return (arg1 - arg2);
		if (op.equals("*")) return (arg1 * arg2);
		if (op.equals("/")) return (arg1 / arg2);
		if (op.equals("sin")) return Math.sin(arg1);
		if (op.equals("cos")) return Math.cos(arg1);
		if (op.equals("tan")) return Math.tan(arg1);
		if (op.equals("exp")) return Math.exp(arg1);
		if (op.equals("sqrt")) return Math.sqrt(arg1); 
		if (op.equals("asin")) return Math.asin(arg1);
		if (op.equals("acos")) return Math.acos(arg1);
		if (op.equals("atan")) return Math.atan(arg1);
		if (op.equals("atan2")) return Math.atan2(arg1,arg2);
		if (op.equals("max")) return (Math.max(arg1, arg2));
		if (op.equals("min")) return (Math.min(arg1, arg2));
		System.err.println("Error: no operator " + op + " implemented");
		return 0;
	}

	private boolean inOperand(char c) {
		return (c >= '0' && c <= '9') || c == '.';
	}

	private Vector toTokens(String s)
	{
		Vector tokenStrings = new Vector(1);
		String operand = "";
		String operator = "";
		int nd = 0;
		int nt = 0;
		int i=0;
		while(i < s.length()) {
			char c = s.charAt(i);
			char cnext = ' ';
			//System.out.println("Looking at " + c);
			if (i < s.length() - 1) cnext = s.charAt(i + 1);

			if(nt > 0 && operator.equals("atan") && c == '2') {
				//System.out.println("    1");
				tokenStrings.addElement("atan2");
				operator = "";
				nt = 0;
				i++;
			} 
			else if (inOperand(c))
			{
				//System.out.println("    2");
				operand += s.charAt(i);
				nd++;
				if (nt > 0)
				{
					tokenStrings.addElement(operator);
				}
				nt = 0;
				operator = "";
				i++;
			}
			else if (c == 'e' && nd > 0 && i < s.length() - 2 &&
				((cnext == '+' || cnext == '-') || inOperand(cnext)))
			{
				//System.out.println("    3");
				operand += c;
				operand += cnext;
				nd += 2;
				nt = 0;
				i+=2;
			}
			else if (c == '(' || c == ')')
			{
				//System.out.println("    4");
				if (nd > 0)
				{
					tokenStrings.addElement(operand);
				}
				nd = 0;
				operand = "";
				if (nt > 0)
				{
					tokenStrings.addElement(operator);
				}
				operator = ""+c;
				tokenStrings.addElement(operator);
				nt = 0;
				operator = "";
				i++;
			}
			else
			{
				//System.out.println("    5");
				if (nd > 0)
				{
					tokenStrings.addElement(operand);
				}
				nd = 0;
				operand = "";
				operator += c;
				if (operator.equals("+") || operator.equals("-") || operator.equals("*") || operator.equals("/") || operator.equals(","))
				{
					tokenStrings.addElement(operator);
					nt = 0;
					operator = "";
				}
				else
				{
					nt++;
				}
				i++;
			}
		}
		if (nd > 0)
		{
			tokenStrings.addElement(operand);
			nt = 0;
		}
		if (nt > 0)
		{
			tokenStrings.addElement(operator);
		}
		return tokenStrings;
	}

	private double eval(String s)
	{
		Double D = new Double(0);
		System.out.println("Evaluating " + s);

		Vector tokens = toTokens(s);

		System.out.print("Tokenized: ");
		for (int it = 0; it < tokens.size(); it++)
		{
			System.out.print((String)tokens.elementAt(it)+" ");

		}
		System.out.println("");

		while (tokens.size() > 1)
		{
			tokens = reduceTokens(tokens);
		}

		return D.parseDouble((String)tokens.elementAt(0));


	}

	public Vector reduceTokens(Vector tokens)
	{
		Double D = new Double(0);
		Vector rightTokens = new Vector();
		double leftValue = 0, rightValue1, rightValue2;
		System.out.print("Simplify Tokens :");
		for (int i = 0; i < tokens.size(); i++) System.out.print(" " + (String)tokens.elementAt(i));
		System.out.println("");
		while (tokens.indexOf("(") != -1)
		{
			int ib = tokens.indexOf("(");
			Vector bracketTokens = new Vector();
			int brackets = 1;
			for (int it = ib + 1; it < tokens.size(); it++)
			{
				String st = (String)tokens.elementAt(it);
				if (st.equals(")"))
				{
					brackets--;
				}
				else if (st.equals("("))
				{
					brackets++;
				}
				if (brackets == 0)
				{
					//System.out.println("Start brackets: " + ib + " end brackets " + it);
					for (int i3 = ib + 1; i3 < it; i3++)
					{
						bracketTokens.addElement(tokens.elementAt(i3));
					}
					int startsize = bracketTokens.size();
					//System.out.println("call reduceTokens with");
					//for (int i = 0; i < bracketTokens.size(); i++) System.out.println(" " + (String)bracketTokens.elementAt(i));
					bracketTokens = reduceTokens(bracketTokens);
					int endsize = bracketTokens.size();
					//System.out.println("After reduce tokens Diff "+(startsize-endsize));
					//for (int i = 0; i < bracketTokens.size(); i++) System.out.println(" " + (String)bracketTokens.elementAt(i));
					int ip = ib;
					for (int i3 = 0; i3 < bracketTokens.size(); i3++)
					{
						tokens.setElementAt(bracketTokens.elementAt(i3), ip++);
					}
					for(int i=0;i<startsize-endsize+2;i++) tokens.removeElementAt(ip);
					//System.out.println("Replaced tokens ");
					//for (int i = 0; i < tokens.size(); i++) System.out.println(" " + (String)tokens.elementAt(i));
					break;
				}
			}
			System.out.print("After bracket reduction: ");
			for (int i = 0; i < tokens.size(); i++) System.out.print(" " + (String)tokens.elementAt(i));
			System.out.println("");
		}

		// treat the operators in order of precedence


		while(tokens.size() > 1) {

			System.out.print("Iterating expression: ");
			for (int i = 0; i < tokens.size(); i++) System.out.print(" " + (String)tokens.elementAt(i));
			System.out.println("");

			int maxprec = 0;
			int ipos = -1;
			for (int it = 0; it < tokens.size(); it++)
			{
				String st = (String)tokens.elementAt(it);
				int iop = getOperator(st);
				if (iop == -1) continue;
				if (OperatorsPrecedence[iop] >= maxprec)
				{
					maxprec = OperatorsPrecedence[iop];
					ipos = it;
				}
			}

			if (ipos == -1) return tokens; // for a simple list of operands

			int it = ipos;

			String st = (String)tokens.elementAt(it);
			int iop = getOperator(st);
			//System.out.println("     Precedence Operator: "+it+" " + st);

			if (OperatorsLeftRight[iop])
				{
					if (!st.equals(","))
					{
						int ipt = it-1;
						if (it > 0)
						{
							String stleft = (String)tokens.elementAt(it - 1);
							if (getOperator(stleft) != -1)
							{
								ipt = it;
								leftValue = 0;
							}
							else
							{
								leftValue = D.parseDouble(stleft);
							}
						}
						else
						{
							ipt = 0;
							leftValue = 0.0;
						}
						String stright = (String)tokens.elementAt(it + 1);
						rightValue1 = D.parseDouble(stright);
						//System.out.print("DoOp " + leftValue + st + rightValue1);
						double value = doOp(st, leftValue, rightValue1);
						stright = "" + value;
						//System.out.println(" = " + stright);
						tokens.setElementAt(stright, ipt);
						//System.out.println("After Op:");
						tokens.removeElementAt(ipt+1);
						if(it > 0 && ipt != it) tokens.removeElementAt(ipt+1);
						//for (int i = 0; i < tokens.size(); i++) System.out.println(" " + (String)tokens.elementAt(i));
						continue;
					}
					else 
					{
						tokens.removeElementAt(it);
						continue;
					}
				}
				else
				{
					int nargs = OperatorsArgs[iop];
					String stright = (String)tokens.elementAt(it + 1);
					rightValue1 = D.parseDouble(stright);
					rightValue2 = 0;
					if (nargs > 1)
					{
						stright = (String)tokens.elementAt(it + 2); 
						rightValue2 = D.parseDouble(stright);
					}
					//System.out.print("DoOp " + st + " "+ rightValue1+" "+rightValue2);
					double value = doOp(st, rightValue1, rightValue2);
					stright = "" + value;
					//System.out.println(" = " + stright);
					tokens.setElementAt(stright, it);

					//System.out.println("After Op:");
					tokens.removeElementAt(it+1);
					if (nargs > 1)
					{
						tokens.removeElementAt(it+1);
					}
					//for (int i = 0; i < tokens.size(); i++) System.out.println(" " + (String)tokens.elementAt(i));
					continue;
				}
		}

		//System.out.print("Return from reduceTokens with: ");
		//for (int i = 0; i < tokens.size(); i++) System.out.print(" " + (String)tokens.elementAt(i));
		//System.out.println("");

		return tokens;
	}


}
