1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package org.jrobin.data;
27
28 import org.jrobin.core.RrdException;
29 import org.jrobin.core.Util;
30
31 import java.util.Calendar;
32 import java.util.StringTokenizer;
33
34 class RpnCalculator {
35 private static final byte TKN_VAR = 0;
36 private static final byte TKN_NUM = 1;
37 private static final byte TKN_PLUS = 2;
38 private static final byte TKN_MINUS = 3;
39 private static final byte TKN_MULT = 4;
40 private static final byte TKN_DIV = 5;
41 private static final byte TKN_MOD = 6;
42 private static final byte TKN_SIN = 7;
43 private static final byte TKN_COS = 8;
44 private static final byte TKN_LOG = 9;
45 private static final byte TKN_EXP = 10;
46 private static final byte TKN_FLOOR = 11;
47 private static final byte TKN_CEIL = 12;
48 private static final byte TKN_ROUND = 13;
49 private static final byte TKN_POW = 14;
50 private static final byte TKN_ABS = 15;
51 private static final byte TKN_SQRT = 16;
52 private static final byte TKN_RANDOM = 17;
53 private static final byte TKN_LT = 18;
54 private static final byte TKN_LE = 19;
55 private static final byte TKN_GT = 20;
56 private static final byte TKN_GE = 21;
57 private static final byte TKN_EQ = 22;
58 private static final byte TKN_IF = 23;
59 private static final byte TKN_MIN = 24;
60 private static final byte TKN_MAX = 25;
61 private static final byte TKN_LIMIT = 26;
62 private static final byte TKN_DUP = 27;
63 private static final byte TKN_EXC = 28;
64 private static final byte TKN_POP = 29;
65 private static final byte TKN_UN = 30;
66 private static final byte TKN_UNKN = 31;
67 private static final byte TKN_NOW = 32;
68 private static final byte TKN_TIME = 33;
69 private static final byte TKN_PI = 34;
70 private static final byte TKN_E = 35;
71 private static final byte TKN_AND = 36;
72 private static final byte TKN_OR = 37;
73 private static final byte TKN_XOR = 38;
74 private static final byte TKN_PREV = 39;
75 private static final byte TKN_INF = 40;
76 private static final byte TKN_NEGINF = 41;
77 private static final byte TKN_STEP = 42;
78 private static final byte TKN_YEAR = 43;
79 private static final byte TKN_MONTH = 44;
80 private static final byte TKN_DATE = 45;
81 private static final byte TKN_HOUR = 46;
82 private static final byte TKN_MINUTE = 47;
83 private static final byte TKN_SECOND = 48;
84 private static final byte TKN_WEEK = 49;
85 private static final byte TKN_SIGN = 50;
86 private static final byte TKN_RND = 51;
87
88 private String rpnExpression;
89 private String sourceName;
90 private DataProcessor dataProcessor;
91
92 private Token[] tokens;
93 private RpnStack stack = new RpnStack();
94 private double[] calculatedValues;
95 private long[] timestamps;
96 private double timeStep;
97
98 RpnCalculator(String rpnExpression, String sourceName, DataProcessor dataProcessor) throws RrdException {
99 this.rpnExpression = rpnExpression;
100 this.sourceName = sourceName;
101 this.dataProcessor = dataProcessor;
102 this.timestamps = dataProcessor.getTimestamps();
103 this.timeStep = this.timestamps[1] - this.timestamps[0];
104 this.calculatedValues = new double[this.timestamps.length];
105 StringTokenizer st = new StringTokenizer(rpnExpression, ", ");
106 tokens = new Token[st.countTokens()];
107 for (int i = 0; st.hasMoreTokens(); i++) {
108 tokens[i] = createToken(st.nextToken());
109 }
110 }
111
112 private Token createToken(String parsedText) throws RrdException {
113 Token token = new Token();
114 if (Util.isDouble(parsedText)) {
115 token.id = TKN_NUM;
116 token.number = Util.parseDouble(parsedText);
117 }
118 else if (parsedText.equals("+")) {
119 token.id = TKN_PLUS;
120 }
121 else if (parsedText.equals("-")) {
122 token.id = TKN_MINUS;
123 }
124 else if (parsedText.equals("*")) {
125 token.id = TKN_MULT;
126 }
127 else if (parsedText.equals("/")) {
128 token.id = TKN_DIV;
129 }
130 else if (parsedText.equals("%")) {
131 token.id = TKN_MOD;
132 }
133 else if (parsedText.equals("SIN")) {
134 token.id = TKN_SIN;
135 }
136 else if (parsedText.equals("COS")) {
137 token.id = TKN_COS;
138 }
139 else if (parsedText.equals("LOG")) {
140 token.id = TKN_LOG;
141 }
142 else if (parsedText.equals("EXP")) {
143 token.id = TKN_EXP;
144 }
145 else if (parsedText.equals("FLOOR")) {
146 token.id = TKN_FLOOR;
147 }
148 else if (parsedText.equals("CEIL")) {
149 token.id = TKN_CEIL;
150 }
151 else if (parsedText.equals("ROUND")) {
152 token.id = TKN_ROUND;
153 }
154 else if (parsedText.equals("POW")) {
155 token.id = TKN_POW;
156 }
157 else if (parsedText.equals("ABS")) {
158 token.id = TKN_ABS;
159 }
160 else if (parsedText.equals("SQRT")) {
161 token.id = TKN_SQRT;
162 }
163 else if (parsedText.equals("RANDOM")) {
164 token.id = TKN_RANDOM;
165 }
166 else if (parsedText.equals("LT")) {
167 token.id = TKN_LT;
168 }
169 else if (parsedText.equals("LE")) {
170 token.id = TKN_LE;
171 }
172 else if (parsedText.equals("GT")) {
173 token.id = TKN_GT;
174 }
175 else if (parsedText.equals("GE")) {
176 token.id = TKN_GE;
177 }
178 else if (parsedText.equals("EQ")) {
179 token.id = TKN_EQ;
180 }
181 else if (parsedText.equals("IF")) {
182 token.id = TKN_IF;
183 }
184 else if (parsedText.equals("MIN")) {
185 token.id = TKN_MIN;
186 }
187 else if (parsedText.equals("MAX")) {
188 token.id = TKN_MAX;
189 }
190 else if (parsedText.equals("LIMIT")) {
191 token.id = TKN_LIMIT;
192 }
193 else if (parsedText.equals("DUP")) {
194 token.id = TKN_DUP;
195 }
196 else if (parsedText.equals("EXC")) {
197 token.id = TKN_EXC;
198 }
199 else if (parsedText.equals("POP")) {
200 token.id = TKN_POP;
201 }
202 else if (parsedText.equals("UN")) {
203 token.id = TKN_UN;
204 }
205 else if (parsedText.equals("UNKN")) {
206 token.id = TKN_UNKN;
207 }
208 else if (parsedText.equals("NOW")) {
209 token.id = TKN_NOW;
210 }
211 else if (parsedText.equals("TIME")) {
212 token.id = TKN_TIME;
213 }
214 else if (parsedText.equals("PI")) {
215 token.id = TKN_PI;
216 }
217 else if (parsedText.equals("E")) {
218 token.id = TKN_E;
219 }
220 else if (parsedText.equals("AND")) {
221 token.id = TKN_AND;
222 }
223 else if (parsedText.equals("OR")) {
224 token.id = TKN_OR;
225 }
226 else if (parsedText.equals("XOR")) {
227 token.id = TKN_XOR;
228 }
229 else if (parsedText.equals("PREV")) {
230 token.id = TKN_PREV;
231 token.variable = sourceName;
232 token.values = calculatedValues;
233 }
234 else if (parsedText.startsWith("PREV(") && parsedText.endsWith(")")) {
235 token.id = TKN_PREV;
236 token.variable = parsedText.substring(5, parsedText.length() - 1);
237 token.values = dataProcessor.getValues(token.variable);
238 }
239 else if (parsedText.equals("INF")) {
240 token.id = TKN_INF;
241 }
242 else if (parsedText.equals("NEGINF")) {
243 token.id = TKN_NEGINF;
244 }
245 else if (parsedText.equals("STEP")) {
246 token.id = TKN_STEP;
247 }
248 else if (parsedText.equals("YEAR")) {
249 token.id = TKN_YEAR;
250 }
251 else if (parsedText.equals("MONTH")) {
252 token.id = TKN_MONTH;
253 }
254 else if (parsedText.equals("DATE")) {
255 token.id = TKN_DATE;
256 }
257 else if (parsedText.equals("HOUR")) {
258 token.id = TKN_HOUR;
259 }
260 else if (parsedText.equals("MINUTE")) {
261 token.id = TKN_MINUTE;
262 }
263 else if (parsedText.equals("SECOND")) {
264 token.id = TKN_SECOND;
265 }
266 else if (parsedText.equals("WEEK")) {
267 token.id = TKN_WEEK;
268 }
269 else if (parsedText.equals("SIGN")) {
270 token.id = TKN_SIGN;
271 }
272 else if (parsedText.equals("RND")) {
273 token.id = TKN_RND;
274 }
275 else {
276 token.id = TKN_VAR;
277 token.variable = parsedText;
278 token.values = dataProcessor.getValues(token.variable);
279 }
280 return token;
281 }
282
283 double[] calculateValues() throws RrdException {
284 for (int slot = 0; slot < timestamps.length; slot++) {
285 resetStack();
286 for (Token token : tokens) {
287 double x1, x2, x3;
288 switch (token.id) {
289 case TKN_NUM:
290 push(token.number);
291 break;
292 case TKN_VAR:
293 push(token.values[slot]);
294 break;
295 case TKN_PLUS:
296 push(pop() + pop());
297 break;
298 case TKN_MINUS:
299 x2 = pop();
300 x1 = pop();
301 push(x1 - x2);
302 break;
303 case TKN_MULT:
304 push(pop() * pop());
305 break;
306 case TKN_DIV:
307 x2 = pop();
308 x1 = pop();
309 push(x1 / x2);
310 break;
311 case TKN_MOD:
312 x2 = pop();
313 x1 = pop();
314 push(x1 % x2);
315 break;
316 case TKN_SIN:
317 push(Math.sin(pop()));
318 break;
319 case TKN_COS:
320 push(Math.cos(pop()));
321 break;
322 case TKN_LOG:
323 push(Math.log(pop()));
324 break;
325 case TKN_EXP:
326 push(Math.exp(pop()));
327 break;
328 case TKN_FLOOR:
329 push(Math.floor(pop()));
330 break;
331 case TKN_CEIL:
332 push(Math.ceil(pop()));
333 break;
334 case TKN_ROUND:
335 push(Math.round(pop()));
336 break;
337 case TKN_POW:
338 x2 = pop();
339 x1 = pop();
340 push(Math.pow(x1, x2));
341 break;
342 case TKN_ABS:
343 push(Math.abs(pop()));
344 break;
345 case TKN_SQRT:
346 push(Math.sqrt(pop()));
347 break;
348 case TKN_RANDOM:
349 push(Math.random());
350 break;
351 case TKN_LT:
352 x2 = pop();
353 x1 = pop();
354 push(x1 < x2 ? 1 : 0);
355 break;
356 case TKN_LE:
357 x2 = pop();
358 x1 = pop();
359 push(x1 <= x2 ? 1 : 0);
360 break;
361 case TKN_GT:
362 x2 = pop();
363 x1 = pop();
364 push(x1 > x2 ? 1 : 0);
365 break;
366 case TKN_GE:
367 x2 = pop();
368 x1 = pop();
369 push(x1 >= x2 ? 1 : 0);
370 break;
371 case TKN_EQ:
372 x2 = pop();
373 x1 = pop();
374 push(x1 == x2 ? 1 : 0);
375 break;
376 case TKN_IF:
377 x3 = pop();
378 x2 = pop();
379 x1 = pop();
380 push(x1 != 0 ? x2 : x3);
381 break;
382 case TKN_MIN:
383 push(Math.min(pop(), pop()));
384 break;
385 case TKN_MAX:
386 push(Math.max(pop(), pop()));
387 break;
388 case TKN_LIMIT:
389 x3 = pop();
390 x2 = pop();
391 x1 = pop();
392 push(x1 < x2 || x1 > x3 ? Double.NaN : x1);
393 break;
394 case TKN_DUP:
395 push(peek());
396 break;
397 case TKN_EXC:
398 x2 = pop();
399 x1 = pop();
400 push(x2);
401 push(x1);
402 break;
403 case TKN_POP:
404 pop();
405 break;
406 case TKN_UN:
407 push(Double.isNaN(pop()) ? 1 : 0);
408 break;
409 case TKN_UNKN:
410 push(Double.NaN);
411 break;
412 case TKN_NOW:
413 push(Util.getTime());
414 break;
415 case TKN_TIME:
416 push((long) Math.round(timestamps[slot]));
417 break;
418 case TKN_PI:
419 push(Math.PI);
420 break;
421 case TKN_E:
422 push(Math.E);
423 break;
424 case TKN_AND:
425 x2 = pop();
426 x1 = pop();
427 push((x1 != 0 && x2 != 0) ? 1 : 0);
428 break;
429 case TKN_OR:
430 x2 = pop();
431 x1 = pop();
432 push((x1 != 0 || x2 != 0) ? 1 : 0);
433 break;
434 case TKN_XOR:
435 x2 = pop();
436 x1 = pop();
437 push(((x1 != 0 && x2 == 0) || (x1 == 0 && x2 != 0)) ? 1 : 0);
438 break;
439 case TKN_PREV:
440 push((slot == 0) ? Double.NaN : token.values[slot - 1]);
441 break;
442 case TKN_INF:
443 push(Double.POSITIVE_INFINITY);
444 break;
445 case TKN_NEGINF:
446 push(Double.NEGATIVE_INFINITY);
447 break;
448 case TKN_STEP:
449 push(timeStep);
450 break;
451 case TKN_YEAR:
452 push(getCalendarField(pop(), Calendar.YEAR));
453 break;
454 case TKN_MONTH:
455 push(getCalendarField(pop(), Calendar.MONTH));
456 break;
457 case TKN_DATE:
458 push(getCalendarField(pop(), Calendar.DAY_OF_MONTH));
459 break;
460 case TKN_HOUR:
461 push(getCalendarField(pop(), Calendar.HOUR_OF_DAY));
462 break;
463 case TKN_MINUTE:
464 push(getCalendarField(pop(), Calendar.MINUTE));
465 break;
466 case TKN_SECOND:
467 push(getCalendarField(pop(), Calendar.SECOND));
468 break;
469 case TKN_WEEK:
470 push(getCalendarField(pop(), Calendar.WEEK_OF_YEAR));
471 break;
472 case TKN_SIGN:
473 x1 = pop();
474 push(Double.isNaN(x1) ? Double.NaN : x1 > 0 ? +1 : x1 < 0 ? -1 : 0);
475 break;
476 case TKN_RND:
477 push(Math.floor(pop() * Math.random()));
478 break;
479 default:
480 throw new RrdException("Unexpected RPN token encountered, token.id=" + token.id);
481 }
482 }
483 calculatedValues[slot] = pop();
484
485 if (slot == 0 && !isStackEmpty()) {
486 throw new RrdException("Stack not empty at the end of calculation. " +
487 "Probably bad RPN expression [" + rpnExpression + "]");
488 }
489 }
490 return calculatedValues;
491 }
492
493 private double getCalendarField(double timestamp, int field) {
494 Calendar calendar = Util.getCalendar((long) (timestamp * 1000));
495 return calendar.get(field);
496 }
497
498 private void push(double x) throws RrdException {
499 stack.push(x);
500 }
501
502 private double pop() throws RrdException {
503 return stack.pop();
504 }
505
506 private double peek() throws RrdException {
507 return stack.peek();
508 }
509
510 private void resetStack() {
511 stack.reset();
512 }
513
514 private boolean isStackEmpty() {
515 return stack.isEmpty();
516 }
517
518 private class RpnStack {
519 private static final int MAX_STACK_SIZE = 1000;
520 private double[] stack = new double[MAX_STACK_SIZE];
521 private int pos = 0;
522
523 void push(double x) throws RrdException {
524 if (pos >= MAX_STACK_SIZE) {
525 throw new RrdException("PUSH failed, RPN stack full [" + MAX_STACK_SIZE + "]");
526 }
527 stack[pos++] = x;
528 }
529
530 double pop() throws RrdException {
531 if (pos <= 0) {
532 throw new RrdException("POP failed, RPN stack is empty ");
533 }
534 return stack[--pos];
535 }
536
537 double peek() throws RrdException {
538 if (pos <= 0) {
539 throw new RrdException("PEEK failed, RPN stack is empty ");
540 }
541 return stack[pos - 1];
542 }
543
544 void reset() {
545 pos = 0;
546 }
547
548 boolean isEmpty() {
549 return pos <= 0;
550 }
551 }
552
553 private class Token {
554 byte id = -1;
555 double number = Double.NaN;
556 String variable = null;
557 double[] values = null;
558 }
559 }