package com.planet_ink.coffee_mud.core;
import java.util.*;
import java.io.*;

/*
   Copyright 2000-2011 Bo Zimmerman

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
/**
 * A core singleton class handling various mathematical operations and 
 * functions, especially dealing with explicit type conversions, and 
 * special string conversions and functions.
 */
public class CMath
{
    private CMath(){super();}
    private static final CMath inst=new CMath();
    public static final CMath instance(){return inst;}
    private static final String[] ROMAN_HUNDREDS={"C","CC","CCC","CD","D","DC","DCC","DCCC","CM","P"};
    private static final String[] ROMAN_TENS={"X","XX","XXX","XL","L","LX","LXX","LXXX","XC","C"};
    private static final String[] ROMAN_ONES={"I","II","III","IV","V","VI","VII","VIII","IX","X"};
    private static final String   ROMAN_ALL="CDMPXLIV";
    private static final java.text.DecimalFormat twoPlaces = new java.text.DecimalFormat("0.#####%");
    private static Random rand = new Random(System.currentTimeMillis());

    /** Convert an integer to its Roman Numeral equivalent
     *
     * Usage: Return=convertToRoman(Number)+".";
     * @param i Integer to convert
     *
     * @return String Converted integer
     */
    public final static String convertToRoman(int i)
    {
    	final StringBuffer roman=new StringBuffer("");
        if(i>1000)
        {
            roman.append("Y");
            i=i%1000;
        }
        if(i>=100)
        {
            int x=i%100;
            int y=(i-x)/100;
            if(y>0)
                roman.append(ROMAN_HUNDREDS[y-1]);
            i=x;
        }
        if(i>=10)
        {
            int x=i%10;
            int y=(i-x)/10;
            if(y>0)
                roman.append(ROMAN_TENS[y-1]);
        }
        i=i%10;
        if(i>0)
            roman.append(ROMAN_ONES[i-1]);
        return roman.toString();
    }

    /**
     * Convert a number from roman numeral to integer.
     * @param s the roman numeral string
     * @return the int
     */
    public final static int convertFromRoman(final String s)
    {
        int x=0;
        while(s.startsWith("Y"))
            x+=1000;
        for(int i=ROMAN_HUNDREDS.length-1;i>=0;i--)
            if(s.startsWith(ROMAN_HUNDREDS[i]))
            {
                x+=(100*(i+1));
                break;
            }
        for(int i=ROMAN_TENS.length-1;i>=0;i--)
            if(s.startsWith(ROMAN_TENS[i]))
            {
                x+=(10*(i+1));
                break;
            }
        for(int i=ROMAN_ONES.length-1;i>=0;i--)
            if(s.startsWith(ROMAN_ONES[i]))
            {
                x+=i+1;
                break;
            }
        return x;
    }

    /**
     * Return st,nd,rd for a number
     * @param num the number
     * @return the st,nd,rd appendage only
     */
    public final static String numAppendage(final int num)
	{
        if((num<11)||(num>13))
        {
        	final String strn=""+num;
            switch(strn.charAt(strn.length()-1))
            {
            case '1': return "st";
            case '2': return "nd";
            case '3': return "rd";
            }
        }
        return "th";
	}

    /**
     * Return true if the char is a roman numeral digit
     * @param c the char
     * @return true if is roman
     */
    public final static boolean isRomanDigit(final char c){ return ROMAN_ALL.indexOf(c)>=0;}

    /**
     * Returns true if the string is a roman numeral
     * @param s the string to test
     * @return true if a roman numeral, false otherwise
     */
    public final static boolean isRomanNumeral(final String s)
    {
        if(s==null) return false;
        final String ups=s.toUpperCase().trim();
        if(ups.length()==0) return false;
        for(int c=0;c<ups.length();c++)
            if(!isRomanDigit(ups.charAt(c)))
                return false;
        return true;
    }

    /**
     * Returns the absolute difference between two numbers
     * @param x the first number
     * @param y the second number
     * @return the absolute difference (x-y)*(-1 if <)
     */
    public final static long absDiff(final long x, final long y)
    {
    	final long d=x-y;
        if(d<0) return d*-1;
        return d;
    }

    /**
     * Returns which object in the object array is same as the
     * string, when cast to a string.
     * @param o array of objects
     * @param s the string to look
     * @return the object or null
     */
    public final static Object s_valueOf(Object[] o, String s)
    {
    	if(s==null) return null;
    	for(Object a : o)
    		if(a.toString().equalsIgnoreCase(s.trim()))
    			return a;
    	return null;
    }
    
    /**
     * Returns true if the string is a number (float or int)
     * @param s the string to test
     * @return true if a number, false otherwise
     */
    public final static boolean isNumber(final String s)
    {
        if(s==null) return false;
        final String ups=s.trim();
        if(ups.length()==0) return false;
        int start=0;
        if(ups.startsWith("-"))
            start=1;
        for(int i=start;i<ups.length();i++)
            if("0123456789.,".indexOf(ups.charAt(i))<0)
                return false;
        return true;
    }

    /**
     * Divide a by b, making sure both are cast to doubles
     * and that the return is precisely double.
     * @param a the dividend
     * @param b the divisor
     * @return the quotient
     */
    public final static double div(final double a, final double b)
    {
        return a/b;
    }
    /**
     * Divide a by b, making sure both are cast to doubles
     * and that the return is precisely double.
     * @param a the dividend
     * @param b the divisor
     * @return the quotient
     */
    public final static double div(final double a, final int b)
    {
        return a/((double)b);
    }
    /**
     * Divide a by b, making sure both are cast to doubles
     * and that the return is precisely double.
     * @param a the dividend
     * @param b the divisor
     * @return the quotient
     */
    public final static double div(final int a, final double b)
    {
        return ((double)a)/b;
    }
    /**
     * Divide a by b, making sure both are cast to doubles
     * and that the return is precisely double.
     * @param a the dividend
     * @param b the divisor
     * @return the quotient
     */
    public final static double div(final double a, final long b)
    {
        return a/((double)b);
    }
    /**
     * Divide a by b, making sure both are cast to doubles
     * and that the return is precisely double.
     * @param a the dividend
     * @param b the divisor
     * @return the quotient
     */
    public final static double div(final long a, final double b)
    {
        return ((double)a)/b;
    }
    /**
     * Multiply a and b, making sure both are cast to doubles
     * and that the return is precisely double.
     * @param a the first number
     * @param b the second number
     * @return the retult of multiplying a and b
     */
    public final static double mul(final double a, final double b)
    {
        return a*b;
    }
    /**
     * Multiply a and b, making sure both are cast to doubles
     * and that the return is precisely double.
     * @param a the first number
     * @param b the second number
     * @return the retult of multiplying a and b
     */
    public final static double mul(final double a, final int b)
    {
        return a*((double)b);
    }
    /**
     * Multiply a and b, making sure both are cast to doubles
     * and that the return is precisely double.
     * @param a the first number
     * @param b the second number
     * @return the retult of multiplying a and b
     */
    public final static double mul(final int a, final double b)
    {
        return ((double)a)*b;
    }
    /**
     * Multiply a and b, making sure both are cast to doubles
     * and that the return is precisely double.
     * @param a the first number
     * @param b the second number
     * @return the retult of multiplying a and b
     */
    public final static double mul(final double a, final long b)
    {
        return a*((double)b);
    }
    /**
     * Multiply a and b, making sure both are cast to doubles
     * and that the return is precisely double.
     * @param a the first number
     * @param b the second number
     * @return the retult of multiplying a and b
     */
    public final static double mul(final long a, final double b)
    {
        return ((double)a)*b;
    }
    /**
     * Multiply a and b, making sure both are cast to doubles
     * and that the return is precisely double.
     * @param a the first number
     * @param b the second number
     * @return the retult of multiplying a and b
     */
    public final static long mul(final long a, final long b)
    {
        return a*b;
    }
    /**
     * Multiply a and b, making sure both are cast to doubles
     * and that the return is precisely double.
     * @param a the first number
     * @param b the second number
     * @return the retult of multiplying a and b
     */
    public final static int mul(final int a, final int b)
    {
        return a*b;
    }
    /**
     * Divide a by b, making sure both are cast to doubles
     * and that the return is precisely double.
     * @param a the dividend
     * @param b the divisor
     * @return the quotient
     */
    public final static double div(final long a, final long b)
    {
        return ((double)a)/((double)b);
    }
    /**
     * Divide a by b, making sure both are cast to doubles
     * and that the return is precisely double.
     * @param a the dividend
     * @param b the divisor
     * @return the quotient
     */
    public final static double div(final int a, final int b)
    {
        return ((double)a)/((double)b);
    }
    /**
     * Raises x to the y power, making sure both are cast to doubles
     * and that the return is rounded off.
     * @param x the base number
     * @param y the power
     * @return x to the y power, rounded off
     */
    public final static long pow(final long x, final long y)
    {
        return Math.round(Math.pow(((double)x),((double)y)));
    }
    /**
     * Returns x, squared, after being case to a double
     * @param x the number to square
     * @return x, squared, and rounded off.
     */
    public final static int squared(final int x)
    {
        return (int)Math.round(Math.pow(((double)x),2.0));
    }
    /**
     * Returns true if the given number has the bits
     * represented by the given bitmask set.
     * @param num the number
     * @param bitmask the bit mask
     * @return true if the bits are set, false otherwise
     */
    public final static boolean bset(final short num, final short bitmask)
    {
        return ((num&bitmask)==bitmask);
    }
    /**
     * Returns true if the given number has the bits
     * represented by the given bitmask set.
     * @param num the number
     * @param bitmask the bit mask
     * @return true if the bits are set, false otherwise
     */
    public final static boolean bset(final int num, final int bitmask)
    {
        return ((num&bitmask)==bitmask);
    }
    /**
     * Returns true if the given number has the bits
     * represented by the given bitmask set.
     * @param num the number
     * @param bitmask the bit mask
     * @return true if the bits are set, false otherwise
     */
    public final static boolean bset(final long num, final long bitmask)
    {
        return ((num&bitmask)==bitmask);
    }
    /**
     * Returns true if the given number has the bits
     * represented by the given bitmask set.
     * @param num the number
     * @param bitmask the bit mask
     * @return true if the bits are set, false otherwise
     */
    public final static boolean bset(final long num, final int bitmask)
    {
        return ((num&bitmask)==bitmask);
    }
    /**
     * Returns the given number, after having set the
     * bits represented by the given bit mask.
     * @param num the number
     * @param bitmask the bitmask
     * @return the number | the bitmask
     */
    public final static int setb(final int num, final int bitmask)
    {
        return num|bitmask;
    }
    /**
     * Returns true if any of the bits represented
     * by the given bitmask are set in the given
     * number.
     * @param num the given number
     * @param bitmask the bitmask of bits to check
     * @return true if any bits from the mask are set
     */
    public final static boolean banyset(final int num, final int bitmask)
    {
        return ((num&bitmask)>0);
    }
    /**
     * Returns true if any of the bits represented
     * by the given bitmask are set in the given
     * number.
     * @param num the given number
     * @param bitmask the bitmask of bits to check
     * @return true if any bits from the mask are set
     */
    public final static boolean banyset(final long num, final long bitmask)
    {
        return ((num&bitmask)>0);
    }
    /**
     * Returns true if any of the bits represented
     * by the given bitmask are set in the given
     * number.
     * @param num the given number
     * @param bitmask the bitmask of bits to check
     * @return true if any bits from the mask are set
     */
    public final static boolean banyset(final long num, final int bitmask)
    {
        return ((num&bitmask)>0);
    }
    /**
     * Returns the given number, after having set the
     * bits represented by the given bit mask.
     * @param num the number
     * @param bitmask the bitmask
     * @return the number | the bitmask
     */
    public final static long setb(final long num, final int bitmask)
    {
        return num|bitmask;
    }
    /**
     * Returns the given number, after having set the
     * bits represented by the given bit mask.
     * @param num the number
     * @param bitmask the bitmask
     * @return the number | the bitmask
     */
    public final static long setb(final long num, final long bitmask)
    {
        return num|bitmask;
    }
    /**
     * Unsets those bits in the given number which are
     * turned ON in the given bitmask.
     * @param num the given number
     * @param bitmask the given bitmask
     * @return the number without the bitmasks bits turned on.
     */
    public final static int unsetb(final int num, final int bitmask) { return num & (~bitmask);}
    /**
     * Unsets those bits in the given number which are
     * turned ON in the given bitmask.
     * @param num the given number
     * @param bitmask the given bitmask
     * @return the number without the bitmasks bits turned on.
     */
    public final static long unsetb(final long num, final long bitmask) { return num & (~bitmask);}
    /**
     * Unsets those bits in the given number which are
     * turned ON in the given bitmask.
     * @param num the given number
     * @param bitmask the given bitmask
     * @return the number without the bitmasks bits turned on.
     */
    public final static long unsetb(final long num, final int bitmask) { return num & (~bitmask);}
    /**
     * Returns true if the bitnumberth bit (0...) is set 
     * in the given number
     * @param number the given number
     * @param bitnumber the bit to check (0,1,2...)
     * @return true if the given bitnumberth bit is set
     */
    public final static boolean isSet(final int number, final int bitnumber)
    {
    	int mask=(int)pow(2,bitnumber);
    	return ((number&mask)==mask);
    }
    /**
     * Returns true if the given string represents a 
     * percentage in the form X% where X is any real
     * number.
     * @param s the string to check
     * @return true if it is a percentage, false otherwise
     */
    public final static boolean isPct(final String s)
    {
        if(s==null) return false;
        final String ts=s.trim();
        if(!ts.endsWith("%")) return false;
        return CMath.isNumber(ts.substring(0,ts.length()-1));
    }
    /**
     * Converts the given string to a floating
     * point number, 1>=N>=0, representing 
     * the whole percentage of the string.  The
     * string format is either X or X%, where 100>=X>=0
     * If the format is bad, 0.0 is returned.
     * @param s the string to convert
     * @return the string converted to a real number 
     */
    public final static double s_pct(String s)
    {
    	if(s==null) return 0.0;
    	while(s.trim().endsWith("%")) s=s.trim().substring(0,s.length()-1).trim();
    	return s_double(s)/100.0;
    }
    
    /**
     * Converts a percentage 1>d>0 to a string.
     * @param d the number to convert
     * @return the percentage string.
     */
    public final static String toPct(final double d)
    {
    	final String s=twoPlaces.format(d);
        if(s.endsWith("%%")) return s.substring(0,s.length()-1);
        return s;
    }
    
    /**
     * Converts the string to a double percentage and then
     * converts that back to a percentage.
     * @param s the string number
     * @return the percentage %
     */
    public final static String toPct(final String s) { return toPct(s_pct(s)); }

    /**
     * Returns true if the bitnumberth bit (0...) is set 
     * in the given number
     * @param number the given number
     * @param bitnumber the bit to check (0,1,2...)
     * @return true if the given bitnumberth bit is set
     */
    public final static boolean isSet(final long number, final int bitnumber)
    {
        if((number&(pow(2,bitnumber)))==(pow(2,bitnumber)))
            return true;
        return false;
    }
    
    /**
     * Returns whether the given string is a valid
     * math expression (5 + 7)/2, etc. Does this
     * by evaluating the expression and returning
     * false if an error is found.  No variables
     * are allowed.
     * @param st the possible math expression
     * @return true if it is a math expression
     */
    public final static boolean isMathExpression(final String st){
        if((st==null)||(st.length()==0)) return false;
        try{ parseMathExpression(st);}catch(Exception e){ return false;}
        return true;
    }
    /**
     * Returns whether the given string is a valid
     * math expression (@x1 + 7)/2, etc. Does this
     * by evaluating the expression and returning
     * false if an error is found.  All necessary
     * variables MUST be included (@x1=vars[0])
     * @param st the possible math expression
     * @param vars the 0 based variables
     * @return true if it is a math expression
     */
    public final static boolean isMathExpression(final String st, final double[] vars){
        if((st==null)||(st.length()==0)) return false;
        try{ parseMathExpression(st,vars);}catch(Exception e){ return false;}
        return true;
    }
    /**
     * Returns the result of evaluating the given math
     * expression.  An expression can be a double or int
     * number, or a full expression using ()+-/*?<>.
     * Variable @xx will refer to current computed value.
     * Returns 0.0 on any parsing error
     * @param st a full math expression string
     * @return the result of the expression
     */
    public final static double s_parseMathExpression(final String st)
    { try{ return parseMathExpression(st);}catch(Exception e){ return 0.0;}}
    /**
     * Returns the result of evaluating the given math
     * expression.  An expression can be a double or int
     * number, or a full expression using ()+-/*?<>.
     * Variables are included as @x1, etc.. The given
     * variable values list is 0 based, so @x1 = vars[0].
     * Variable @xx will refer to current computed value.
     * Returns 0.0 on any parsing error
     * @param st a full math expression string
     * @param vars the 0 based variables
     * @return the result of the expression
     */
    public final static double s_parseMathExpression(final String st, final double[] vars)
    { try{ return parseMathExpression(st,vars);}catch(Exception e){ return 0.0;}}
    /**
     * Returns the result of evaluating the given math
     * expression.  An expression can be a double or int
     * number, or a full expression using ()+-/*?<>.
     * Variable @xx will refer to current computed value.
     * Rounds the result to a long.
     * Returns 0 on any parsing error
     * @param st a full math expression string
     * @return the result of the expression
     */
    public final static long s_parseLongExpression(final String st)
    { try{ return parseLongExpression(st);}catch(Exception e){ return 0;}}
    /**
     * Returns the result of evaluating the given math
     * expression.  An expression can be a double or int
     * number, or a full expression using ()+-/*?<>.
     * Variables are included as @x1, etc.. The given
     * variable values list is 0 based, so @x1 = vars[0].
     * Variable @xx will refer to current computed value.
     * Rounds the result to a long.
     * Returns 0 on any parsing error
     * @param st a full math expression string
     * @param vars the 0 based variables
     * @return the result of the expression
     */
    public final static long s_parseLongExpression(final String st, final double[] vars)
    { try{ return parseLongExpression(st,vars);}catch(Exception e){ return 0;}}
    /**
     * Returns the result of evaluating the given math
     * expression.  An expression can be a double or int
     * number, or a full expression using ()+-/*?<>.  
     * Variable @xx will refer to current computed value.
     * Round the result to an integer.
     * Returns 0 on any parsing error
     * @param st a full math expression string
     * @return the result of the expression
     */
    public final static int s_parseIntExpression(final String st)
    { try{ return parseIntExpression(st);}catch(Exception e){ return 0;}}
    /**
     * Returns the result of evaluating the given math
     * expression.  An expression can be a double or int
     * number, or a full expression using ()+-/*?<>.
     * Variables are included as @x1, etc.. The given
     * variable values list is 0 based, so @x1 = vars[0].
     * Variable @xx will refer to current computed value.
     * Rounds the result to an integer.
     * Returns 0 on any parsing error
     * @param st a full math expression string
     * @param vars the 0 based variables
     * @return the result of the expression
     */
    public final static int s_parseIntExpression(final String st, final double[] vars)
    { try{ return parseIntExpression(st,vars);}catch(Exception e){ return 0;}}

    private final static double parseMathExpression(final StreamTokenizer st, final boolean inParen, final double[] vars, final double previous)
        throws ArithmeticException
    {
        if(!inParen) {
            st.ordinaryChar('/');
            st.ordinaryChar('x');
            st.ordinaryChar('X');
        }
        double finalValue=0;
        try{
            int c=st.nextToken();
            char lastOperation='+';
            while(c!=StreamTokenizer.TT_EOF)
            {
                double curValue=0.0;
                switch(c)
                {
                case StreamTokenizer.TT_NUMBER:
                    curValue=st.nval;
                    break;
                case '(':
                    curValue=parseMathExpression(st,true,vars,finalValue);
                    break;
                case ')':
                    if(!inParen)
                        throw new ArithmeticException("')' is an unexpected token.");
                    return finalValue;
                case '@':
                {
                    c=st.nextToken();
                    if((c!='x')&&(c!='X'))
                        throw new ArithmeticException("'"+c+"' is an unexpected token after @.");
                    c=st.nextToken();
                    if((c=='x')||(c=='X'))
                    	curValue=previous;
                    else
                    {
	                    if(c!=StreamTokenizer.TT_NUMBER)
	                        throw new ArithmeticException("'"+c+"' is an unexpected token after @x.");
	                    if(vars==null)
	                        throw new ArithmeticException("vars have not been defined for @x"+st.nval);
	                    if((st.nval>vars.length)||(st.nval<1.0))
	                        throw new ArithmeticException("'"+st.nval+"/"+vars.length+"' is an illegal variable reference.");
	                    curValue=vars[((int)st.nval)-1];
                    }
                    break;
                }
                case '+':
                case '<':
                case '>':
                case '-':
                case '%':
                case '*':
                case '\\':
                case '/':
                case '?':
                {
                    lastOperation=(char)c;
                    c=st.nextToken();
                    continue;
                }
                default:
                    throw new ArithmeticException("'"+c+"' is an illegal expression.");
                }
                switch(lastOperation)
                {
                case '<': finalValue = finalValue < curValue? finalValue : curValue; break;
                case '>': finalValue = finalValue > curValue? finalValue : curValue; break;
                case '+': finalValue+=curValue; break;
                case '-': finalValue-=curValue; break;
                case '*': finalValue*=curValue; break;
                case '%': finalValue%=curValue; break;
                case '/':
                case '\\': finalValue/=curValue; break;
                case '?': finalValue= ((curValue-finalValue+0.5) * rand.nextDouble()) + finalValue; break;
                }
                c=st.nextToken();
            }
        }
        catch(IOException e){}
        if(inParen)
            throw new ArithmeticException("')' was missing from this expression");
        return finalValue;
    }

    /**
     * A class representing a single piece of a compiled operation.  Optomized for
     * speed of execution rather than the obvious wastefulness of storage.
     */
    public static final class CompiledOperation
    {
    	public static final int OPERATION_VARIABLE=0;
    	public static final int OPERATION_VALUE=1;
    	public static final int OPERATION_OPERATION=2;
    	public static final int OPERATION_LIST=3;
    	public static final int OPERATION_PREVIOUSVALUE=4;
    	public final int type;
    	public int variableIndex = 0;
    	public double value = 0.0;
    	public char operation = ' ';
    	public LinkedList<CompiledOperation> list = null;
    	public CompiledOperation(int variableIndex) { type = OPERATION_VARIABLE; this.variableIndex = variableIndex;}  
    	public CompiledOperation(double value) { type = OPERATION_VALUE; this.value = value;}  
    	public CompiledOperation(LinkedList<CompiledOperation> list) { type = OPERATION_LIST; this.list = list;}  
    	public CompiledOperation(char operation) { type = OPERATION_OPERATION; this.operation = operation;}  
    	public CompiledOperation() { type = OPERATION_PREVIOUSVALUE;}  
    }
    
    /**
     * Pre-compiles an expression for faster evaluation later on.
     * @see CMath#parseMathExpression(LinkedList, double[], double)
     * @param formula the math expression as a string
     * @return the pre-compiled expression
     * @throws ArithmeticException
     */
    public final static LinkedList<CompiledOperation> compileMathExpression(final String formula)
    {return compileMathExpression(new StreamTokenizer(new InputStreamReader(new ByteArrayInputStream(formula.getBytes()))),false);}
    
    /**
     * Pre-compiles an expression for faster evaluation later on.
     * @see CMath#parseMathExpression(LinkedList, double[])
     * @param st the tokenized expression
     * @param inParen whether or not you are in parenthesis mode 
     * @return the pre-compiled expression
     * @throws ArithmeticException
     */
    private final static LinkedList<CompiledOperation> compileMathExpression(final StreamTokenizer st, final boolean inParen)
    	throws ArithmeticException
	{
	    if(!inParen) {
	        st.ordinaryChar('/');
	        st.ordinaryChar('x');
	        st.ordinaryChar('X');
	    }
	    final LinkedList<CompiledOperation> list = new LinkedList<CompiledOperation>();
	    
	    try{
	        int c=st.nextToken();
	        char lastOperation='+';
	        while(c!=StreamTokenizer.TT_EOF)
	        {
	            switch(c)
	            {
	            case StreamTokenizer.TT_NUMBER:
	            	list.add(new CompiledOperation(st.nval));
	                break;
	            case '(':
	            	list.add(new CompiledOperation(compileMathExpression(st,true)));
	                break;
	            case ')':
	                if(!inParen)
	                    throw new ArithmeticException("')' is an unexpected token.");
	                return list;
	            case '@':
	            {
	                c=st.nextToken();
	                if((c!='x')&&(c!='X'))
	                    throw new ArithmeticException("'"+c+"' is an unexpected token after @.");
	                c=st.nextToken();
                    if((c=='x')||(c=='X'))
		                list.add(new CompiledOperation());
                    else
                    {
		                if(c!=StreamTokenizer.TT_NUMBER)
		                    throw new ArithmeticException("'"+c+"' is an unexpected token after @x.");
		                if((st.nval>11)||(st.nval<1.0))
		                    throw new ArithmeticException("'"+st.nval+"/11' is an illegal variable reference.");
		                list.add(new CompiledOperation(((int)st.nval)-1));
                    }
	                break;
	            }
	            case '+':
	            case '-':
                case '%':
	            case '*':
	            case '\\':
	            case '/':
	            case '?':
                case '<':
                case '>':
	            {
	                lastOperation=(char)c;
	                c=st.nextToken();
	                continue;
	            }
	            default:
	                throw new ArithmeticException("'"+(char)c+"' ("+(int)c+") is an illegal expression.");
	            }
	            switch(lastOperation)
	            {
	            case '+':
	            case '-':
                case '%':
	            case '*':
	            case '?':
                case '<':
                case '>':
	            	list.add(new CompiledOperation(lastOperation));
	            	break;
	            case '/':
	            case '\\':
	            	list.add(new CompiledOperation('/'));
	            	break;
	            }
	            c=st.nextToken();
	        }
	    }
	    catch(IOException e){}
	    if(inParen)
	        throw new ArithmeticException("')' was missing from this expression");
	    return list;
	}

    /**
     * Parse a pre-compiled expression.  Requires a vars variable of at least 10 entries
     * to ensure NO exceptions (other than /0).
     * @see CMath#compileMathExpression(StreamTokenizer, boolean)
     * @param list the pre-compiled expression
     * @param vars the variable values
     * @return the final value
     */
    public final static double parseMathExpression(final LinkedList<CompiledOperation> list, final double[] vars, final double previous)
	{
        double finalValue=0.0;
        double curValue=0.0;
    	for(final Iterator<CompiledOperation> i=list.iterator();i.hasNext();)
    	{
    		final CompiledOperation o=i.next();
    		switch(o.type)
    		{
    			case CompiledOperation.OPERATION_VALUE: 
    				curValue = o.value; break;
    			case CompiledOperation.OPERATION_VARIABLE: 
    				curValue = vars[o.variableIndex]; break;
    			case CompiledOperation.OPERATION_LIST: 
    				curValue = parseMathExpression(o.list,vars,finalValue); break;
    			case CompiledOperation.OPERATION_PREVIOUSVALUE: 
    				curValue = previous; break;
    			case CompiledOperation.OPERATION_OPERATION:
    				switch(o.operation)
    				{
    	                case '+': finalValue+=curValue; break;
    	                case '-': finalValue-=curValue; break;
    	                case '%': finalValue%=curValue; break;
    	                case '*': finalValue*=curValue; break;
    	                case '/': finalValue/=curValue; break;
    	                case '?': finalValue= ((curValue-finalValue+0.5) * rand.nextDouble()) + finalValue; break;
    	                case '<': finalValue = finalValue < curValue? finalValue : curValue; break;
    	                case '>': finalValue = finalValue > curValue? finalValue : curValue; break;
    				}
    				break;
    		}
    	}
    	return finalValue;
	}
    
	
    /**
     * Returns the result of evaluating the given math
     * expression.  An expression can be a double or int
     * number, or a full expression using ()+-/*?<>.
     * Variable @xx will refer to current computed value.
     * Rounds the result to a long
     * Throws an exception on any parsing error
     * @param formula a full math expression string
     * @return the result of the expression
     */
    public final static long parseLongExpression(final String formula)
    {return Math.round(parseMathExpression(new StreamTokenizer(new InputStreamReader(new ByteArrayInputStream(formula.getBytes()))),false,null,0));}
    /**
     * Returns the result of evaluating the given math
     * expression.  An expression can be a double or int
     * number, or a full expression using ()+-/*?<>.
     * Variables are included as @x1, etc.. The given
     * variable values list is 0 based, so @x1 = vars[0].
     * Variable @xx will refer to current computed value.
     * Rounds the result to a long
     * Throws an exception on any parsing error
     * @param formula a full math expression string
     * @param vars the 0 based variables
     * @return the result of the expression
     */
    public final static long parseLongExpression(final String formula, final double[] vars)
    {return Math.round(parseMathExpression(new StreamTokenizer(new InputStreamReader(new ByteArrayInputStream(formula.getBytes()))),false,vars,0));}

    /**
     * Returns the result of evaluating the given math
     * expression.  An expression can be a double or int
     * number, or a full expression using ()+-/*?<>.
     * Variable @xx will refer to current computed value.
     * Rounds the result to an integer.
     * Throws an exception on any parsing error
     * @param formula a full math expression string
     * @return the result of the expression
     */
    public final static int parseIntExpression(final String formula) throws ArithmeticException
    {return (int)Math.round(parseMathExpression(new StreamTokenizer(new InputStreamReader(new ByteArrayInputStream(formula.getBytes()))),false,null,0));}
    /**
     * Returns the result of evaluating the given math
     * expression.  An expression can be a double or int
     * number, or a full expression using ()+-/*?<>.
     * Variables are included as @x1, etc.. The given
     * variable values list is 0 based, so @x1 = vars[0].
     * Variable @xx will refer to current computed value.
     * Rounds the result to an integer.
     * Throws an exception on any parsing error
     * @param formula a full math expression string
     * @param vars the 0 based variables
     * @return the result of the expression
     */
    public final static int parseIntExpression(final String formula, final double[] vars) throws ArithmeticException
    {return (int)Math.round(parseMathExpression(new StreamTokenizer(new InputStreamReader(new ByteArrayInputStream(formula.getBytes()))),false,vars,0));}
    /**
     * Returns the result of evaluating the given math
     * expression.  An expression can be a double or int
     * number, or a full expression using ()+-/*?<>.
     * Variable @xx will refer to current computed value.
     * Throws an exception on any parsing error
     * @param formula a full math expression string
     * @return the result of the expression
     */
    public final static double parseMathExpression(String formula) throws ArithmeticException
    {return parseMathExpression(new StreamTokenizer(new InputStreamReader(new ByteArrayInputStream(formula.getBytes()))),false,null,0);}
    /**
     * Returns the result of evaluating the given math
     * expression.  An expression can be a double or int
     * number, or a full expression using ()+-/*?<>.
     * Variables are included as @x1, etc.. The given
     * variable values list is 0 based, so @x1 = vars[0].
     * Variable @xx will refer to current computed value.
     * Throws an exception on any parsing error
     * @param formula a full math expression string
     * @param vars the 0 based variables
     * @return the result of the expression
     */
    public final static double parseMathExpression(final String formula, final double[] vars) throws ArithmeticException
    {return parseMathExpression(new StreamTokenizer(new InputStreamReader(new ByteArrayInputStream(formula.getBytes()))),false,vars,0);}


    /**
     * Returns the long value of a string without crashing
     *
     * <br><br><b>Usage:</b> lSize = WebIQBase.s_long(WebIQBase.getRes(AttStatsRes,"BlobSize"));
     * @param LONG String to convert
     * @return long Long value of the string
     */
    public final static long s_long(final String LONG)
    {
        try{ return Long.parseLong(LONG); }
        catch(Exception e){ return 0;}
    }

    /**
     * Returns the floating point value of a string without crashing
     *
     * <br><br><b>Usage:</b> lSize = WebIQBase.s_float(WebIQBase.getRes(AttStatsRes,"BlobSize"));
     * @param FLOAT String to convert
     * @return Float value of the string
     */
    public final static float s_float(final String FLOAT)
    {
        try{ return Float.parseFloat(FLOAT); }
        catch(Exception e){ return 0;}
    }

    /**
     * Returns the double value of a string without crashing
     *
     * <br><br><b>Usage:</b> dSize = WebIQBase.s_double(WebIQBase.getRes(AttStatsRes,"BlobSize"));
     * @param DOUBLE String to convert
     * @return double Double value of the string
     */
    public final static double s_double(final String DOUBLE)
    {
        try{ return Double.parseDouble(DOUBLE); }
        catch(Exception e){ return 0.0;}
    }


    /**
     * Returns the absolute value (X>=0) of the
     * given number
     * @param val the number
     * @return the absolute value of the number
     */
    public final static int abs(final int val)
    {
        if(val>=0) return val;
        return val*-1;
    }

    /**
     * Returns the first set bit number of the bitmask given
     * @param mask the bit mask given.
     * @return the first set bit number of the bitmask given
     */
    public final static int bitNumber(final long mask)
    {
    	if(mask<=0) return 0;
    	for(int i=0;i<64;i++)
    		if((mask&CMath.pow(2,i))>0)
    			return i+1;
    	return 0;
    }
    
    /**
     * Returns the absolute value (X>=0) of the
     * given number
     * @param val the number
     * @return the absolute value of the number
     */
    public final static long abs(final long val)
    {
        if(val>=0) return val;
        return val*-1;
    }

    /**
     * Returns the boolean value of a string without crashing
     *
     * <br><br><b>Usage:</b> int num=s_bool(CMD.substring(14));
     * @param BOOL Boolean value of string
     * @return int Boolean value of the string
     */
    public final static boolean s_bool(final String BOOL)
    {
        return Boolean.valueOf(BOOL).booleanValue();
    }

    /**
     * Returns whether the given string is a boolean value
     *
     * <br><br><b>Usage:</b> if(isBool(CMD.substring(14)));
     * @param BOOL Boolean value of string
     * @return whether it is a boolean
     */
    public final static boolean isBool(final String BOOL)
    {
        return BOOL.equalsIgnoreCase("true")||BOOL.equalsIgnoreCase("false");
    }

    /**
     * Returns the integer value of a string without crashing
     *
     * <br><br><b>Usage:</b> int num=s_int(CMD.substring(14));
     * @param INT Integer value of string
     * @return int Integer value of the string
     */
    public final static int s_int(final String INT)
    {
        try{ return Integer.parseInt(INT); }
        catch(Exception e){ return 0;}
    }
    /**
     * Returns the short value of a string without crashing
     *
     * <br><br><b>Usage:</b> int num=s_short(CMD.substring(14));
     * @param SHORT Short value of string
     * @return short Short value of the string
     */
    public final static short s_short(final String SHORT)
    {
        try{ return Short.parseShort(SHORT); }
        catch(Exception e){ return 0;}
    }

    /**
     * Returns whether the given string is a long value
     *
     * <br><br><b>Usage:</b> if(isLong(CMD.substring(14)));
     * @param LONG Long value of string
     * @return whether it is a long
     */
    public final static boolean isLong(final String LONG){return isInteger(LONG);}
    
    /**
     * Returns whether the given string is a int value
     *
     * <br><br><b>Usage:</b> if(isInteger(CMD.substring(14)));
     * @param INT Integer value of string
     * @return whether it is a int
     */
    public final static boolean isInteger(final String INT)
    {
        if(INT==null) return false;
        if(INT.length()==0) return false;
        int i=0;
        if(INT.charAt(0)=='-')
            if(INT.length()>1)
                i++;
            else
                return false;
        for(;i<INT.length();i++)
            if(!Character.isDigit(INT.charAt(i)))
                return false;
        return true;
    }
    
    /**
     * Returns whether the given string is a float value
     *
     * <br><br><b>Usage:</b> if(isFloat(CMD.substring(14)));
     * @param DBL float value of string
     * @return whether it is a float
     */
    public final static boolean isFloat(final String DBL){return isDouble(DBL);}
    
    /**
     * Returns a int representing either the given value, or 
     * the 2^ power of the comma separated values in the order
     * they appear in the given string list.
     *
     * <br><br><b>Usage:</b> if(s_parseBitIntExpression(CMDS,CMD.substring(14)));
     * @param bits the ordered string values from 0-whatever.
     * @param val the expression, or list of string values
     * @return the int value, or 0
     */
    public final static int s_parseBitIntExpression(final String[] bits, final String val)
    {
    	return (int)s_parseBitLongExpression(bits,val);
    }
    
    /**
     * Returns a long representing either the given value, or 
     * the 2^ power of the comma separated values in the order
     * they appear in the given string list.
     *
     * <br><br><b>Usage:</b> if(s_parseBitLongExpression(CMDS,CMD.substring(14)));
     * @param bits the ordered string values from 0-whatever.
     * @param val the expression, or list of string values
     * @return the long value, or 0
     */
    public final static long s_parseBitLongExpression(final String[] bits, String val)
    {
    	if((val==null)||(val.trim().length()==0)||(CMath.isMathExpression(val)))
    		return CMath.s_parseLongExpression(val);
    	final StringTokenizer tokens=new StringTokenizer(val,",");
    	long l=0;
    	while(tokens.hasMoreElements())
    	{
    		val=tokens.nextToken().trim();
    		if((val.length()==0)||(CMath.isMathExpression(val)))
    			l|=CMath.s_parseLongExpression(val);
    		else
    		for(int x=0;x<bits.length;x++)
    			if(bits[x].equalsIgnoreCase(val))
	    		{
    				l+=pow(2,x-1);
	    			break;
	    		}
    	}
    	return l;
    }
    
    /**
     * Replaces the internal Random object with the one
     * passed in.  Intended to be used for debugging purposes
     * only.
     * @param rand the random object to use
     */
    public final static void setRand(final Random rand)
    {
    	CMath.rand = rand;
    }
    
    /**
     * Returns a long representing either the given value, or 
     * the index of the value in the order
     * they appear in the given string list.
     *
     * <br><br><b>Usage:</b> if(s_parseListLongExpression(CMDS,CMD.substring(14)));
     * @param descs the ordered string values from 0-whatever.
     * @param val the expression, or list of string values
     * @return the long value, or 0
     */
    public final static long s_parseListLongExpression(final String[] descs, final String val)
    {
    	if((val==null)||(val.trim().length()==0)||(CMath.isMathExpression(val)))
    		return CMath.s_parseLongExpression(val);
		for(int x=0;x<descs.length;x++)
			if(descs[x].equalsIgnoreCase(val))
				return x;
		return 0;
    }
    
    /**
     * Returns a int representing either the given value, or 
     * the index of the value in the order
     * they appear in the given string list.
     *
     * <br><br><b>Usage:</b> if(s_parseListIntExpression(CMDS,CMD.substring(14)));
     * @param descs the ordered string values from 0-whatever.
     * @param val the expression, or list of string values
     * @return the int value, or 0
     */
    public final static int s_parseListIntExpression(final String[] descs, final String val)
    { return (int)s_parseListLongExpression(descs,val);}
    
    /**
     * Returns whether the given string is a double value
     *
     * <br><br><b>Usage:</b> if(isDouble(CMD.substring(14)));
     * @param DBL double value of string
     * @return whether it is a double
     */
    public final static boolean isDouble(final String DBL)
    {
        if(DBL==null) return false;
        if(DBL.length()==0) return false;
        int i=0;
        if(DBL.charAt(0)=='-')
            if(DBL.length()>1)
                i++;
            else
                return false;
        boolean alreadyDot=false;
        for(;i<DBL.length();i++)
            if(!Character.isDigit(DBL.charAt(i)))
            {
                if(DBL.charAt(i)=='.')
                {
                    if(alreadyDot)
                        return false;
                    alreadyDot=true;
                }
                else
                    return false;
            }
        return alreadyDot;
    }

    /**
     * @see java.lang.Math#round(double)
     * @param d the real number
     * @return the rounded number as a long
     */
    public final static long round(final double d){return Math.round(d);}
    /**
     * @see java.lang.Math#round(float)
     * @param d the real number
     * @return the rounded number as a long
     */
    public final static long round(final float d){return Math.round(d);}
    /**
     * @see java.lang.Math#abs(double)
     * @param d the real number
     * @return the absolute value of the number
     */
    public final static double abs(final double d){return Math.abs(d);}
    /**
     * @see java.lang.Math#abs(float)
     * @param d the real number
     * @return the absolute value of the number
     */
    public final static float abs(final float d){return Math.abs(d);}
    /**
     * @see java.lang.Math#random()
     * @return a random number
     */
    public final static double random(){return rand.nextDouble();}
    /**
     * @see java.lang.Math#floor(double)
     * @see CMath#ceiling(double)
     * @param d the number to get the floor of
     * @return the floor of the given number
     */
    public final static double floor(final double d){return Math.floor(d);}
    /**
     * @see java.lang.Math#floor(double)
     * @see CMath#ceiling(double)
     * @param d the number to get the floor of
     * @return the floor of the given number
     */
    public final static float floor(final float d){return (float)Math.floor(d);}
    /**
     * @see java.lang.Math#ceil(double)
     * @see CMath#floor(double)
     * @param d the number to get the ceiling of
     * @return the ceiling of the given number
     */
    public final static double ceiling(final double d){return Math.ceil(d);}
    /**
     * @see java.lang.Math#ceil(double)
     * @see CMath#floor(float)
     * @param d the number to get the ceiling of
     * @return the ceiling of the given number
     */
    public final static float ceiling(final float d){return (float)Math.ceil(d);}
    /**
     * @see java.lang.Math#sqrt(double)
     * @param d the number to get the square root of
     * @return the square root of the given number
     */
    public final static double sqrt(final double d){return Math.sqrt(d);}
    /**
     * @see java.lang.Math#sqrt(double)
     * @param d the number to get the square root of
     * @return the square root of the given number
     */
    public final static float sqrt(final float d){return (float)Math.sqrt(d);}

    /**
     * Returns greater of two numbers
     * @param a first number
     * @param b second number
     * @return greater of the two
     */
    public final static double greater(final double a, final double b)
    {
        return a<b?b:a;
    }
}
