66 |
09 Feb 06 |
enell |
1 |
/* |
66 |
09 Feb 06 |
enell |
* Created on 26-May-2004 |
66 |
09 Feb 06 |
enell |
3 |
* |
66 |
09 Feb 06 |
enell |
* Calculator.java is a part of WeightedMerge |
66 |
09 Feb 06 |
enell |
5 |
* |
66 |
09 Feb 06 |
enell |
* Copyright (C) 2004 Johan Enell, Dept Oncology, Lund University |
66 |
09 Feb 06 |
enell |
7 |
* |
66 |
09 Feb 06 |
enell |
* This program is free software; you can redistribute it and/or modify it under |
66 |
09 Feb 06 |
enell |
* the terms of the GNU General Public License as published by the Free Software |
66 |
09 Feb 06 |
enell |
* Foundation; either version 2 of the License, or (at your option) any later |
66 |
09 Feb 06 |
enell |
* version. |
66 |
09 Feb 06 |
enell |
12 |
* |
66 |
09 Feb 06 |
enell |
* This program is distributed in the hope that it will be useful, but WITHOUT |
66 |
09 Feb 06 |
enell |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
66 |
09 Feb 06 |
enell |
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
66 |
09 Feb 06 |
enell |
* details. |
66 |
09 Feb 06 |
enell |
17 |
* |
66 |
09 Feb 06 |
enell |
* You should have received a copy of the GNU General Public License along with |
66 |
09 Feb 06 |
enell |
* this program; if not, write toX the Free Software Foundation, Inc., 59 Temple |
66 |
09 Feb 06 |
enell |
* 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 |
* <p>The Calculator class is the main class in the calc package. It constructs |
66 |
09 Feb 06 |
enell |
* a calculation tree from a text string and supports various arithmetic |
66 |
09 Feb 06 |
enell |
* operators. These operators are + (addition), - (subtraction), * |
66 |
09 Feb 06 |
enell |
* (multiplication), / (division), % (modulo) and ^ (power). You can also use |
66 |
09 Feb 06 |
enell |
* parenthesis to control the calculation order. The parser will interpret All |
66 |
09 Feb 06 |
enell |
* tokens in the string that's not a literal or a operator is interpreted as a |
66 |
09 Feb 06 |
enell |
* variable. A variable is initially assigned the value <code>Float.NaN</code> |
66 |
09 Feb 06 |
enell |
* but can later be set using a set method.</p> |
66 |
09 Feb 06 |
enell |
37 |
* |
66 |
09 Feb 06 |
enell |
* <table><tr><th>Operator</th><th>Type</th><th>Order</th><th> |
66 |
09 Feb 06 |
enell |
* Example</th></tr><tr><td>+</td><td>addition</td><td>1</td><td> |
66 |
09 Feb 06 |
enell |
* 2+3=5</td></tr><tr><td>-</td><td>subtraction</td><td>1</td><td> |
66 |
09 Feb 06 |
enell |
* 3-2=1</td></tr><tr><td>*</td><td>multiplication</td><td>2</td> |
66 |
09 Feb 06 |
enell |
* <td>2*3=6</td></tr><tr><td>/</td><td>division</td><td>2</td> |
66 |
09 Feb 06 |
enell |
* <td>3/2=1.5</td></tr><tr><td>%</td><td>modulo</td><td>2</td> |
66 |
09 Feb 06 |
enell |
* <td>=3%2=1</td></tr><tr><td>^</td><td>power</td><td>3</td><td> |
66 |
09 Feb 06 |
enell |
* 3^2=9</td></tr><tr><td>()</td><td>parenthesis</td><td>+3</td> |
66 |
09 Feb 06 |
enell |
* <td>(1+2)*3=9</td></tr> </table> |
66 |
09 Feb 06 |
enell |
47 |
* |
66 |
09 Feb 06 |
enell |
* <p>Exampeles of calculation trees.</p> |
66 |
09 Feb 06 |
enell |
49 |
* |
66 |
09 Feb 06 |
enell |
* <table><tr><td> |
66 |
09 Feb 06 |
enell |
51 |
* |
66 |
09 Feb 06 |
enell |
* <pre> |
66 |
09 Feb 06 |
enell |
* (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 |
* + 3 |
66 |
09 Feb 06 |
enell |
58 |
* / \ |
66 |
09 Feb 06 |
enell |
* 1 2 |
66 |
09 Feb 06 |
enell |
* </pre> |
66 |
09 Feb 06 |
enell |
61 |
* |
66 |
09 Feb 06 |
enell |
* </td><td> |
66 |
09 Feb 06 |
enell |
63 |
* |
66 |
09 Feb 06 |
enell |
* <pre> |
66 |
09 Feb 06 |
enell |
* 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 |
* 1 2 3 4 |
66 |
09 Feb 06 |
enell |
* </pre> |
66 |
09 Feb 06 |
enell |
73 |
* |
66 |
09 Feb 06 |
enell |
* </td><td> |
66 |
09 Feb 06 |
enell |
75 |
* |
66 |
09 Feb 06 |
enell |
* <pre> |
66 |
09 Feb 06 |
enell |
* 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 |
* 1 * |
66 |
09 Feb 06 |
enell |
82 |
* / \ |
66 |
09 Feb 06 |
enell |
* + 4 |
66 |
09 Feb 06 |
enell |
84 |
* / \ |
66 |
09 Feb 06 |
enell |
* 2 3 |
66 |
09 Feb 06 |
enell |
* </pre> |
66 |
09 Feb 06 |
enell |
87 |
* |
66 |
09 Feb 06 |
enell |
* </td></tr> </table> |
66 |
09 Feb 06 |
enell |
89 |
* |
66 |
09 Feb 06 |
enell |
* @author Johan Enell, johan.enell@onk.lu.se, Dept Oncology, Lund University, |
66 |
09 Feb 06 |
enell |
* 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 |
* 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 |
* An array with the variabls that the equation contains. It is null until |
66 |
09 Feb 06 |
enell |
* 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 |
* Constructs a new Calculator from the given equation. |
66 |
09 Feb 06 |
enell |
109 |
* |
66 |
09 Feb 06 |
enell |
* @param equation the equation to be parsed. |
66 |
09 Feb 06 |
enell |
* @throws EquationException thrown if the constructor finds any error in |
66 |
09 Feb 06 |
enell |
* 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 |
* Returns a string representation of this Calculator. The string |
66 |
09 Feb 06 |
enell |
* 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 |
* This mathod cals the setVariable(Variable, float) method in the equation. |
66 |
09 Feb 06 |
enell |
264 |
* |
66 |
09 Feb 06 |
enell |
* @see net.sf.basedb.plugin.qpackage.util.calc.Token#setVariable(calc.Variable, float) |
66 |
09 Feb 06 |
enell |
* @param variable the variable to be set. |
66 |
09 Feb 06 |
enell |
* @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 |
* Finds the variables in the equation and return them as an array. |
66 |
09 Feb 06 |
enell |
276 |
* |
66 |
09 Feb 06 |
enell |
* @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 |
* Calculates the value of the equation. |
66 |
09 Feb 06 |
enell |
293 |
* |
66 |
09 Feb 06 |
enell |
* @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 |
} |