plugins/base1/se.lu.onk/trunk/QPackage/src/qpackage/calculator/Calculator.java

Code
Comments
Other
Rev Date Author Line
66 09 Feb 06 enell 1 /*
66 09 Feb 06 enell 2  * Created on 26-May-2004
66 09 Feb 06 enell 3  * 
66 09 Feb 06 enell 4  * Calculator.java is a part of WeightedMerge
66 09 Feb 06 enell 5  * 
66 09 Feb 06 enell 6  * Copyright (C) 2004 Johan Enell, Dept Oncology, Lund University
66 09 Feb 06 enell 7  * 
66 09 Feb 06 enell 8  * This program is free software; you can redistribute it and/or modify it under
66 09 Feb 06 enell 9  * the terms of the GNU General Public License as published by the Free Software
66 09 Feb 06 enell 10  * Foundation; either version 2 of the License, or (at your option) any later
66 09 Feb 06 enell 11  * version.
66 09 Feb 06 enell 12  * 
66 09 Feb 06 enell 13  * This program is distributed in the hope that it will be useful, but WITHOUT
66 09 Feb 06 enell 14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
66 09 Feb 06 enell 15  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
66 09 Feb 06 enell 16  * details.
66 09 Feb 06 enell 17  * 
66 09 Feb 06 enell 18  * You should have received a copy of the GNU General Public License along with
66 09 Feb 06 enell 19  * this program; if not, write toX the Free Software Foundation, Inc., 59 Temple
66 09 Feb 06 enell 20  * Place - Suite 330, Boston, MA 02111-1307, USA.
66 09 Feb 06 enell 21  */
67 10 Feb 06 enell 22 package qpackage.calculator;
66 09 Feb 06 enell 23
66 09 Feb 06 enell 24 import java.util.HashMap;
66 09 Feb 06 enell 25 import java.util.HashSet;
66 09 Feb 06 enell 26 import java.util.Stack;
66 09 Feb 06 enell 27
66 09 Feb 06 enell 28 /**
66 09 Feb 06 enell 29  * <p>The Calculator class is the main class in the calc package. It constructs
66 09 Feb 06 enell 30  * a calculation tree from a text string and supports various arithmetic
66 09 Feb 06 enell 31  * operators. These operators are + (addition), - (subtraction), *
66 09 Feb 06 enell 32  * (multiplication), / (division), % (modulo) and ^ (power). You can also use
66 09 Feb 06 enell 33  * parenthesis to control the calculation order. The parser will interpret All
66 09 Feb 06 enell 34  * tokens in the string that's not a literal or a operator is interpreted as a
66 09 Feb 06 enell 35  * variable. A variable is initially assigned the value <code>Float.NaN</code>
66 09 Feb 06 enell 36  * but can later be set using a set method.</p>
66 09 Feb 06 enell 37  * 
66 09 Feb 06 enell 38  * <table><tr><th>Operator</th><th>Type</th><th>Order</th><th>
66 09 Feb 06 enell 39  * Example</th></tr><tr><td>+</td><td>addition</td><td>1</td><td>
66 09 Feb 06 enell 40  * 2+3=5</td></tr><tr><td>-</td><td>subtraction</td><td>1</td><td>
66 09 Feb 06 enell 41  * 3-2=1</td></tr><tr><td>*</td><td>multiplication</td><td>2</td>
66 09 Feb 06 enell 42  * <td>2*3=6</td></tr><tr><td>/</td><td>division</td><td>2</td>
66 09 Feb 06 enell 43  * <td>3/2=1.5</td></tr><tr><td>%</td><td>modulo</td><td>2</td>
66 09 Feb 06 enell 44  * <td>=3%2=1</td></tr><tr><td>^</td><td>power</td><td>3</td><td>
66 09 Feb 06 enell 45  * 3^2=9</td></tr><tr><td>()</td><td>parenthesis</td><td>+3</td>
66 09 Feb 06 enell 46  * <td>(1+2)*3=9</td></tr> </table>
66 09 Feb 06 enell 47  * 
66 09 Feb 06 enell 48  * <p>Exampeles of calculation trees.</p>
66 09 Feb 06 enell 49  * 
66 09 Feb 06 enell 50  * <table><tr><td>
66 09 Feb 06 enell 51  * 
66 09 Feb 06 enell 52  * <pre>
66 09 Feb 06 enell 53  *  (1+2)*3
66 09 Feb 06 enell 54  * 
66 09 Feb 06 enell 55  *     *
66 09 Feb 06 enell 56  *    / \
66 09 Feb 06 enell 57  *   +   3
66 09 Feb 06 enell 58  *  / \
66 09 Feb 06 enell 59  * 1   2
66 09 Feb 06 enell 60  * </pre>
66 09 Feb 06 enell 61  * 
66 09 Feb 06 enell 62  * </td><td>
66 09 Feb 06 enell 63  * 
66 09 Feb 06 enell 64  * <pre>
66 09 Feb 06 enell 65  *   1*2+3*4
66 09 Feb 06 enell 66  * 
66 09 Feb 06 enell 67  *      +
66 09 Feb 06 enell 68  *    /   \
66 09 Feb 06 enell 69  *   *     *
66 09 Feb 06 enell 70  *  / \   / \
66 09 Feb 06 enell 71  * 1   2 3   4
66 09 Feb 06 enell 72  * </pre>
66 09 Feb 06 enell 73  * 
66 09 Feb 06 enell 74  * </td><td>
66 09 Feb 06 enell 75  * 
66 09 Feb 06 enell 76  * <pre>
66 09 Feb 06 enell 77  *   1*(2+3)*4
66 09 Feb 06 enell 78  * 
66 09 Feb 06 enell 79  *      *
66 09 Feb 06 enell 80  *    /   \
66 09 Feb 06 enell 81  *   1     *
66 09 Feb 06 enell 82  *        / \
66 09 Feb 06 enell 83  *       +   4
66 09 Feb 06 enell 84  *      / \
66 09 Feb 06 enell 85  *     2   3
66 09 Feb 06 enell 86  * </pre>
66 09 Feb 06 enell 87  * 
66 09 Feb 06 enell 88  * </td></tr> </table>
66 09 Feb 06 enell 89  * 
66 09 Feb 06 enell 90  * @author Johan Enell, johan.enell@onk.lu.se, Dept Oncology, Lund University,
66 09 Feb 06 enell 91  *         S-221 85 Lund, Sweden
66 09 Feb 06 enell 92  */
66 09 Feb 06 enell 93 public class Calculator
66 09 Feb 06 enell 94 {
66 09 Feb 06 enell 95
66 09 Feb 06 enell 96   /**
66 09 Feb 06 enell 97    * This is the field that holds the equation. It is never null.
66 09 Feb 06 enell 98    */
66 09 Feb 06 enell 99   private Token theEquation = null;
66 09 Feb 06 enell 100
66 09 Feb 06 enell 101   /**
66 09 Feb 06 enell 102    * An array with the variabls that the equation contains. It is null until
66 09 Feb 06 enell 103    * the method <code>Calculator.getVariables()</code> is called.
66 09 Feb 06 enell 104    */
66 09 Feb 06 enell 105   private Variable[] variables;
66 09 Feb 06 enell 106
66 09 Feb 06 enell 107   /**
66 09 Feb 06 enell 108    * Constructs a new Calculator from the given equation.
66 09 Feb 06 enell 109    * 
66 09 Feb 06 enell 110    * @param equation the equation to be parsed.
66 09 Feb 06 enell 111    * @throws EquationException thrown if the constructor finds any error in
66 09 Feb 06 enell 112    *         the equation.
66 09 Feb 06 enell 113    */
66 09 Feb 06 enell 114   public Calculator(String equation) throws EquationException
66 09 Feb 06 enell 115   {
66 09 Feb 06 enell 116     variables = null;
66 09 Feb 06 enell 117
66 09 Feb 06 enell 118     equation = equation.trim();
66 09 Feb 06 enell 119     equation = equation.replace(',', '.');
66 09 Feb 06 enell 120     equation = equation.replaceAll("\\s", "");
66 09 Feb 06 enell 121
66 09 Feb 06 enell 122     StringBuffer eq = new StringBuffer(equation);
66 09 Feb 06 enell 123
66 09 Feb 06 enell 124     Stack<Token> operands = new Stack<Token>();
66 09 Feb 06 enell 125     Stack<Operator> operators = new Stack<Operator>();
66 09 Feb 06 enell 126
66 09 Feb 06 enell 127     int n = 0;//start of variable
66 09 Feb 06 enell 128     int m = 0;//end of variable
66 09 Feb 06 enell 129     int addOrder = 0;
66 09 Feb 06 enell 130     for (int i = 0; i < eq.length(); i++)
66 09 Feb 06 enell 131     {
66 09 Feb 06 enell 132       Operator operator = null;
66 09 Feb 06 enell 133       Operand operand = null;
66 09 Feb 06 enell 134
66 09 Feb 06 enell 135       switch (eq.charAt(i))
66 09 Feb 06 enell 136       {
66 09 Feb 06 enell 137         case '%':
66 09 Feb 06 enell 138           operator = new Modulo();
66 09 Feb 06 enell 139           break;
66 09 Feb 06 enell 140         case '^':
66 09 Feb 06 enell 141           operator = new Power();
66 09 Feb 06 enell 142           break;
66 09 Feb 06 enell 143         case '+':
66 09 Feb 06 enell 144           operator = new Addition();
66 09 Feb 06 enell 145           break;
66 09 Feb 06 enell 146         case '-':
66 09 Feb 06 enell 147           if (i > 0 && Character.isDigit(eq.charAt(i - 1)))
66 09 Feb 06 enell 148           {
66 09 Feb 06 enell 149             operator = new Subtraction();
66 09 Feb 06 enell 150           }
66 09 Feb 06 enell 151           break;
66 09 Feb 06 enell 152         case '*':
66 09 Feb 06 enell 153           operator = new Multiplication();
66 09 Feb 06 enell 154           break;
66 09 Feb 06 enell 155         case '/':
66 09 Feb 06 enell 156           operator = new Division();
66 09 Feb 06 enell 157           break;
66 09 Feb 06 enell 158         case '(':
66 09 Feb 06 enell 159           addOrder += 3;
66 09 Feb 06 enell 160           n = i + 1;
66 09 Feb 06 enell 161           break;
66 09 Feb 06 enell 162         case ')':
66 09 Feb 06 enell 163           m--;
66 09 Feb 06 enell 164           addOrder -= 3;
66 09 Feb 06 enell 165           if (addOrder < 0)
66 09 Feb 06 enell 166           {
66 09 Feb 06 enell 167             throw new EquationException("End parenthesis on position " + i + " has no matching start");
66 09 Feb 06 enell 168           }
66 09 Feb 06 enell 169           break;
66 09 Feb 06 enell 170         default:
66 09 Feb 06 enell 171           break;
66 09 Feb 06 enell 172       }
66 09 Feb 06 enell 173       if (operator != null)
66 09 Feb 06 enell 174       {
66 09 Feb 06 enell 175         operator.addOrder(addOrder);
66 09 Feb 06 enell 176
66 09 Feb 06 enell 177         m += i;
66 09 Feb 06 enell 178         try
66 09 Feb 06 enell 179         {
66 09 Feb 06 enell 180           operand = new Literal(Float.parseFloat(eq.substring(n, m)));
66 09 Feb 06 enell 181         }
66 09 Feb 06 enell 182         catch (NumberFormatException e)
66 09 Feb 06 enell 183         {
66 09 Feb 06 enell 184           operand = new Variable(eq.substring(n, m));
66 09 Feb 06 enell 185         }
66 09 Feb 06 enell 186         operands.push(operand);
66 09 Feb 06 enell 187         n = i + 1;
66 09 Feb 06 enell 188         m = 0;
66 09 Feb 06 enell 189
66 09 Feb 06 enell 190         if (!operators.isEmpty())
66 09 Feb 06 enell 191         {
66 09 Feb 06 enell 192           Operator tmpOp = operators.peek();
66 09 Feb 06 enell 193
66 09 Feb 06 enell 194           while (tmpOp.getOrder() >= operator.getOrder())
66 09 Feb 06 enell 195           {
66 09 Feb 06 enell 196             if (operands.size() >= 2)
66 09 Feb 06 enell 197             {
66 09 Feb 06 enell 198               operators.pop();
66 09 Feb 06 enell 199
66 09 Feb 06 enell 200               tmpOp.setTok2(operands.pop());
66 09 Feb 06 enell 201               tmpOp.setTok1(operands.pop());
66 09 Feb 06 enell 202               operands.push(tmpOp);
66 09 Feb 06 enell 203
66 09 Feb 06 enell 204               if (!operators.isEmpty())
66 09 Feb 06 enell 205               {
66 09 Feb 06 enell 206                 tmpOp = operators.peek();
66 09 Feb 06 enell 207               }
66 09 Feb 06 enell 208               else
66 09 Feb 06 enell 209               {
66 09 Feb 06 enell 210                 break;
66 09 Feb 06 enell 211               }
66 09 Feb 06 enell 212             }
66 09 Feb 06 enell 213           }
66 09 Feb 06 enell 214         }
66 09 Feb 06 enell 215         operators.push(operator);
66 09 Feb 06 enell 216       }
66 09 Feb 06 enell 217     }
66 09 Feb 06 enell 218     if (addOrder > 0) throw new EquationException("Parenthes missmatch");
66 09 Feb 06 enell 219
66 09 Feb 06 enell 220     if (n < eq.length())
66 09 Feb 06 enell 221     {
66 09 Feb 06 enell 222       m += eq.length();
66 09 Feb 06 enell 223       Operand operand;
66 09 Feb 06 enell 224       try
66 09 Feb 06 enell 225       {
66 09 Feb 06 enell 226         operand = new Literal(Float.parseFloat(eq.substring(n, m)));
66 09 Feb 06 enell 227       }
66 09 Feb 06 enell 228       catch (NumberFormatException e)
66 09 Feb 06 enell 229       {
66 09 Feb 06 enell 230         operand = new Variable(eq.substring(n, m));
66 09 Feb 06 enell 231       }
66 09 Feb 06 enell 232       operands.add(operand);
66 09 Feb 06 enell 233
66 09 Feb 06 enell 234       while (operands.size() != 1)
66 09 Feb 06 enell 235       {
66 09 Feb 06 enell 236         Operator tmpOp = operators.pop();
66 09 Feb 06 enell 237
66 09 Feb 06 enell 238         tmpOp.setTok2(operands.pop());
66 09 Feb 06 enell 239         tmpOp.setTok1(operands.pop());
66 09 Feb 06 enell 240
66 09 Feb 06 enell 241         operands.push(tmpOp);
66 09 Feb 06 enell 242       }
66 09 Feb 06 enell 243       theEquation = operands.pop();
66 09 Feb 06 enell 244     }
66 09 Feb 06 enell 245   }
66 09 Feb 06 enell 246
66 09 Feb 06 enell 247   /**
66 09 Feb 06 enell 248    * Returns a string representation of this Calculator. The string
66 09 Feb 06 enell 249    * representation is the string representation of the equation.
66 09 Feb 06 enell 250    */
66 09 Feb 06 enell 251   @Override
66 09 Feb 06 enell 252   public String toString()
66 09 Feb 06 enell 253   {
66 09 Feb 06 enell 254     String ret = "";
66 09 Feb 06 enell 255     if (theEquation != null)
66 09 Feb 06 enell 256     {
66 09 Feb 06 enell 257       ret = theEquation.toString();
66 09 Feb 06 enell 258     }
66 09 Feb 06 enell 259     return ret;
66 09 Feb 06 enell 260   }
66 09 Feb 06 enell 261
66 09 Feb 06 enell 262   /**
66 09 Feb 06 enell 263    * This mathod cals the setVariable(Variable, float) method in the equation.
66 09 Feb 06 enell 264    * 
66 09 Feb 06 enell 265    * @see net.sf.basedb.plugin.qpackage.util.calc.Token#setVariable(calc.Variable, float)
66 09 Feb 06 enell 266    * @param variable the variable to be set.
66 09 Feb 06 enell 267    * @param value the value of the variable.
66 09 Feb 06 enell 268    */
66 09 Feb 06 enell 269   public void setVariable(Variable variable, float value)
66 09 Feb 06 enell 270   {
66 09 Feb 06 enell 271     theEquation.setVariable(variable, value);
66 09 Feb 06 enell 272   }
66 09 Feb 06 enell 273
66 09 Feb 06 enell 274   /**
66 09 Feb 06 enell 275    * Finds the variables in the equation and return them as an array.
66 09 Feb 06 enell 276    * 
66 09 Feb 06 enell 277    * @return an array of variables.
66 09 Feb 06 enell 278    */
66 09 Feb 06 enell 279   public Variable[] getVariables()
66 09 Feb 06 enell 280   {
66 09 Feb 06 enell 281     if (variables == null)
66 09 Feb 06 enell 282     {
66 09 Feb 06 enell 283       HashSet<Variable> var = theEquation.getVariables();
66 09 Feb 06 enell 284
66 09 Feb 06 enell 285       variables = new Variable[var.size()];
66 09 Feb 06 enell 286       var.toArray(variables);
66 09 Feb 06 enell 287     }
66 09 Feb 06 enell 288     return variables;
66 09 Feb 06 enell 289   }
66 09 Feb 06 enell 290
66 09 Feb 06 enell 291   /**
66 09 Feb 06 enell 292    * Calculates the value of the equation.
66 09 Feb 06 enell 293    * 
66 09 Feb 06 enell 294    * @return the value of the equation.
66 09 Feb 06 enell 295    */
66 09 Feb 06 enell 296   public float getValue()
66 09 Feb 06 enell 297   {
66 09 Feb 06 enell 298     return theEquation.getValue();
66 09 Feb 06 enell 299   }
66 09 Feb 06 enell 300
66 09 Feb 06 enell 301   public float getValue(HashMap variables)
66 09 Feb 06 enell 302   {
66 09 Feb 06 enell 303     return theEquation.getValue(variables);
66 09 Feb 06 enell 304   }
66 09 Feb 06 enell 305 }