create changelog entry
[debian/openrocket] / core / src / de / congrace / exp4j / OperatorToken.java
1 /*
2    Copyright 2011 frank asseg
3
4    Licensed under the Apache License, Version 2.0 (the "License");
5    you may not use this file except in compliance with the License.
6    You may obtain a copy of the License at
7
8        http://www.apache.org/licenses/LICENSE-2.0
9
10    Unless required by applicable law or agreed to in writing, software
11    distributed under the License is distributed on an "AS IS" BASIS,
12    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13    See the License for the specific language governing permissions and
14    limitations under the License.
15
16  */
17 package de.congrace.exp4j;
18
19 import java.util.Stack;
20
21 /**
22  * {@link Token} for Operations like +,-,*,/,% and ^
23  * 
24  * @author fas@congrace.de
25  */
26 class OperatorToken extends CalculationToken {
27
28         /**
29          * the valid {@link Operation}s for the {@link OperatorToken}
30          * 
31          * @author fas@congrace.de
32          */
33         enum Operation {
34                 ADDITION(1, true), SUBTRACTION(1, true), MULTIPLICATION(2, true), DIVISION(2, true), MODULO(2, true), EXPONENTIATION(3, false), UNARY_MINUS(4, false), UNARY_PLUS(
35                                 4, false);
36                 private final int precedence;
37                 private final boolean leftAssociative;
38
39                 private Operation(int precedence, boolean leftAssociative) {
40                         this.precedence = precedence;
41                         this.leftAssociative = leftAssociative;
42                 }
43         }
44
45         /**
46          * return a corresponding {@link Operation} for a symbol
47          * 
48          * @param c
49          *            the symbol of the operation
50          * @return the corresponding {@link Operation}
51          */
52         static Operation getOperation(char c) {
53                 switch (c) {
54                 case '+':
55                         return Operation.ADDITION;
56                 case '-':
57                         return Operation.SUBTRACTION;
58                 case '*':
59                         return Operation.MULTIPLICATION;
60                 case '/':
61                         return Operation.DIVISION;
62                 case '^':
63                         return Operation.EXPONENTIATION;
64                 case '#':
65                         return Operation.UNARY_MINUS;
66                 case '%':
67                         return Operation.MODULO;
68                 default:
69                         return null;
70                 }
71         }
72
73         static boolean isOperator(char c) {
74                 return getOperation(c) != null;
75         }
76
77         private final Operation operation;
78
79         /**
80          * construct a new {@link OperatorToken}
81          * 
82          * @param value
83          *            the symbol (e.g.: '+')
84          * @param operation
85          *            the {@link Operation} of this {@link Token}
86          */
87         OperatorToken(String value, Operation operation) {
88                 super(value);
89                 this.operation = operation;
90         }
91         
92         /**
93          * apply the {@link Operation}
94          * 
95          * @param values
96          *            the doubles to operate on
97          * @return the result of the {@link Operation}
98          * @throws UnparsableExpressionException 
99          */
100         public Variable applyOperation(Variable... values) {
101                 
102                 values = expandVariables(values);
103                 
104                 double[] inputs = new double[values.length];
105                 switch (values[0].getPrimary()){
106                         
107                         case DOUBLE:
108                                 for (int i = 0; i<values.length; i++){
109                                         inputs[i] = values[i].getDoubleValue();
110                                 }
111                                 double result = applyOperation(inputs);
112                                 return new Variable("double " + operation.name()+" result, ", result);
113                         
114                         case ARRAY:
115                                 int maxLength = values[0].getArrayValue().length;
116                                 double[] results = new double[maxLength];
117                                 for (int i = 0; i< maxLength; i++){
118                                         // assemble the array of input values
119                                         for (int j = 0; j<values.length; j++){
120                                                 inputs[j] = values[j].getArrayValue()[i];
121                                         }
122                                         results[i] = applyOperation(inputs);
123                                 }
124                                 //System.out.println("Done applying operation "+operation.name());
125                                 return new Variable("array " + operation.name()+" result, ", results);
126                                 
127                         default:
128                                 return new Variable("Invalid");
129                 }
130         }
131         
132         private double applyOperation(double[] values){
133                                 
134                 //System.out.println("Applying "+operation.toString()+" to values starting "+values[0]);
135                 
136                 switch (operation) {
137                 case ADDITION:
138                         return values[0] + values[1];
139                 case SUBTRACTION:
140                         return values[0] - values[1];
141                 case MULTIPLICATION:
142                         return values[0] * values[1];
143                 case EXPONENTIATION:
144                         return Math.pow(values[0], values[1]);
145                 case DIVISION:
146                         return values[0] / values[1];
147                 case UNARY_MINUS:
148                         return -values[0];
149                 case UNARY_PLUS:
150                         return values[0];
151                 case MODULO:
152                         return values[0] % values[1];
153                 default:
154                         return 0;
155                 }
156         }
157
158         @Override
159         public boolean equals(Object obj) {
160                 if (obj instanceof OperatorToken) {
161                         final OperatorToken t = (OperatorToken) obj;
162                         return t.getValue().equals(this.getValue());
163                 }
164                 return false;
165         }
166
167         int getOperandCount() {
168                 switch (operation) {
169                 case ADDITION:
170                 case SUBTRACTION:
171                 case MULTIPLICATION:
172                 case DIVISION:
173                 case EXPONENTIATION:
174                 case MODULO:
175                         return 2;
176                 case UNARY_MINUS:
177                 case UNARY_PLUS:
178                         return 1;
179                 default:
180                         return 0;
181                 }
182         }
183
184         /**
185          * get the {@link Operation} of this {@link Token}
186          * 
187          * @return the {@link Operation}
188          */
189         Operation getOperation() {
190                 return operation;
191         }
192
193         int getPrecedence() {
194                 return operation.precedence;
195         }
196
197         @Override
198         public int hashCode() {
199                 return getValue().hashCode();
200         }
201
202         /**
203          * check if the operation is left associative
204          * 
205          * @return true if left associative, otherwise false
206          */
207         boolean isLeftAssociative() {
208                 return operation.leftAssociative;
209         }
210
211         @Override
212         void mutateStackForCalculation(Stack<Variable> stack, VariableSet variables) {
213                 if (this.getOperandCount() == 2) {
214                         final Variable n2 = stack.pop();
215                         final Variable n1 = stack.pop();
216                         stack.push(this.applyOperation(n1, n2));
217                 } else if (this.getOperandCount() == 1) {
218                         final Variable n1 = stack.pop();
219                         stack.push(this.applyOperation(n1));
220                 }
221         }
222
223         @Override
224         void mutateStackForInfixTranslation(Stack<Token> operatorStack, StringBuilder output) {
225                 Token before;
226                 while (!operatorStack.isEmpty() && (before = operatorStack.peek()) != null && (before instanceof OperatorToken || before instanceof FunctionToken)) {
227                         if (before instanceof FunctionToken) {
228                                 operatorStack.pop();
229                                 output.append(before.getValue()).append(" ");
230                         } else {
231                                 final OperatorToken stackOperator = (OperatorToken) before;
232                                 if (this.isLeftAssociative() && this.getPrecedence() <= stackOperator.getPrecedence()) {
233                                         output.append(operatorStack.pop().getValue()).append(" ");
234                                 } else if (!this.isLeftAssociative() && this.getPrecedence() < stackOperator.getPrecedence()) {
235                                         output.append(operatorStack.pop().getValue()).append(" ");
236                                 } else {
237                                         break;
238                                 }
239                         }
240                 }
241                 operatorStack.push(this);
242         }
243 }