double[] calculateValues() throws RrdException { TimeZone tz = TimeZone.getDefault(); for (int slot = 0; slot < timestamps.length; slot++) { resetStack(); int token_rpi = -1; for (int rpi = 0; rpi < tokens.length; rpi++) { Token token = tokens[rpi]; double x1, x2, x3; switch (token.id) { case TKN_NUM: push(token.number); break; case TKN_VAR: push(token.values[slot]); token_rpi = rpi; break; case TKN_COUNT: push(slot + 1); break; case TKN_PLUS: push(pop() + pop()); break; case TKN_MINUS: x2 = pop(); x1 = pop(); push(x1 - x2); break; case TKN_MULT: push(pop() * pop()); break; case TKN_DIV: x2 = pop(); x1 = pop(); push(x1 / x2); break; case TKN_MOD: x2 = pop(); x1 = pop(); push(x1 % x2); break; case TKN_SIN: push(Math.sin(pop())); break; case TKN_COS: push(Math.cos(pop())); break; case TKN_ATAN: push(Math.atan(pop())); break; case TKN_ATAN2: x2 = pop(); x1 = pop(); push(Math.atan2(x1, x2)); break; case TKN_LOG: push(Math.log(pop())); break; case TKN_EXP: push(Math.exp(pop())); break; case TKN_FLOOR: push(Math.floor(pop())); break; case TKN_CEIL: push(Math.ceil(pop())); break; case TKN_ROUND: push(Math.round(pop())); break; case TKN_POW: x2 = pop(); x1 = pop(); push(Math.pow(x1, x2)); break; case TKN_ABS: push(Math.abs(pop())); break; case TKN_SQRT: push(Math.sqrt(pop())); break; case TKN_RANDOM: push(Math.random()); break; case TKN_LT: x2 = pop(); x1 = pop(); push(x1 < x2 ? 1 : 0); break; case TKN_LE: x2 = pop(); x1 = pop(); push(x1 <= x2 ? 1 : 0); break; case TKN_GT: x2 = pop(); x1 = pop(); push(x1 > x2 ? 1 : 0); break; case TKN_GE: x2 = pop(); x1 = pop(); push(x1 >= x2 ? 1 : 0); break; case TKN_EQ: x2 = pop(); x1 = pop(); push(x1 == x2 ? 1 : 0); break; case TKN_NE: x2 = pop(); x1 = pop(); push(x1 != x2 ? 1 : 0); break; case TKN_IF: x3 = pop(); x2 = pop(); x1 = pop(); push(x1 != 0 ? x2 : x3); break; case TKN_MIN: push(Math.min(pop(), pop())); break; case TKN_MAX: push(Math.max(pop(), pop())); break; case TKN_LIMIT: x3 = pop(); x2 = pop(); x1 = pop(); push(x1 < x2 || x1 > x3 ? Double.NaN : x1); break; case TKN_DUP: push(peek()); break; case TKN_EXC: x2 = pop(); x1 = pop(); push(x2); push(x1); break; case TKN_POP: pop(); break; case TKN_UN: push(Double.isNaN(pop()) ? 1 : 0); break; case TKN_ISINF: push(Double.isInfinite(pop()) ? 1 : 0); break; case TKN_UNKN: push(Double.NaN); break; case TKN_NOW: push(Util.getTime()); break; case TKN_TIME: push(timestamps[slot]); break; case TKN_LTIME: push(timestamps[slot] + (tz.getOffset(timestamps[slot]) / 1000L)); break; case TKN_PI: push(Math.PI); break; case TKN_E: push(Math.E); break; case TKN_AND: x2 = pop(); x1 = pop(); push((x1 != 0 && x2 != 0) ? 1 : 0); break; case TKN_OR: x2 = pop(); x1 = pop(); push((x1 != 0 || x2 != 0) ? 1 : 0); break; case TKN_XOR: x2 = pop(); x1 = pop(); push(((x1 != 0 && x2 == 0) || (x1 == 0 && x2 != 0)) ? 1 : 0); break; case TKN_PREV: push((slot == 0) ? Double.NaN : token.values[slot - 1]); break; case TKN_INF: push(Double.POSITIVE_INFINITY); break; case TKN_NEGINF: push(Double.NEGATIVE_INFINITY); break; case TKN_STEP: push(timeStep); break; case TKN_YEAR: push(getCalendarField(pop(), Calendar.YEAR)); break; case TKN_MONTH: push(getCalendarField(pop(), Calendar.MONTH)); break; case TKN_DATE: push(getCalendarField(pop(), Calendar.DAY_OF_MONTH)); break; case TKN_HOUR: push(getCalendarField(pop(), Calendar.HOUR_OF_DAY)); break; case TKN_MINUTE: push(getCalendarField(pop(), Calendar.MINUTE)); break; case TKN_SECOND: push(getCalendarField(pop(), Calendar.SECOND)); break; case TKN_WEEK: push(getCalendarField(pop(), Calendar.WEEK_OF_YEAR)); break; case TKN_SIGN: x1 = pop(); push(Double.isNaN(x1) ? Double.NaN : x1 > 0 ? +1 : x1 < 0 ? -1 : 0); break; case TKN_RND: push(Math.floor(pop() * Math.random())); break; case TKN_ADDNAN: x2 = pop(); x1 = pop(); if (Double.isNaN(x1)) { push(x2); } else if (Double.isNaN(x2)) { push(x1); } else { push(x1 + x2); } break; case TKN_DEG2RAD: push(Math.toRadians(pop())); break; case TKN_RAD2DEG: push(Math.toDegrees(pop())); break; case TKN_SORT: { int n = (int) pop(); double[] array = new double[n]; for (int i = 0; i < n; i++) { array[i] = pop(); } Arrays.sort(array); for (int i = 0; i < n; i++) { push(array[i]); } } break; case TKN_REV: { int n = (int) pop(); double[] array = new double[n]; for (int i = 0; i < n; i++) { array[i] = pop(); } for (int i = 0; i < n; i++) { push(array[i]); } } break; case TKN_AVG: { int count = 0; int n = (int) pop(); double sum = 0.0; while (n > 0) { x1 = pop(); n--; if (Double.isNaN(x1)) { continue; } sum += x1; count++; } if (count > 0) { push(sum / count); } else { push(Double.NaN); } } break; case TKN_TREND: case TKN_TRENDNAN: { int dur = (int) pop(); pop(); /* * OK, so to match the output from rrdtool, we have to go *forward* 2 timeperiods. * So at t[59] we use the average of t[1]..t[61] * */ if ((slot + 1) < Math.ceil(dur / timeStep)) { push(Double.NaN); } else { double[] vals = dataProcessor.getValues(tokens[token_rpi].variable); boolean ignorenan = token.id == TKN_TRENDNAN; double accum = 0.0; int count = 0; int start = (int) (Math.ceil(dur / timeStep)); int row = 2; while ((slot + row) > vals.length) { row--; } for (; start > 0; start--) { double val = vals[slot + row - start]; if (ignorenan || !Double.isNaN(val)) { accum = Util.sum(accum, val); ++count; } } // System.err.printf("t[%d]: %1.10e / %d\n", slot, (count == 0) ? Double.NaN : // (accum / count), count); push((count == 0) ? Double.NaN : (accum / count)); } } break; case TKN_PREDICT: case TKN_PREDICTSIGMA: { pop(); // Clear the value of our variable /* the local averaging window (similar to trend, but better here, as we get better statistics thru numbers)*/ int locstepsize = (int) pop(); /* the number of shifts and range-checking*/ int num_shifts = (int) pop(); double[] multipliers; // handle negative shifts special if (num_shifts < 0) { multipliers = new double[1]; multipliers[0] = pop(); } else { multipliers = new double[num_shifts]; for (int i = 0; i < num_shifts; i++) { multipliers[i] = pop(); } } /* the real calculation */ double val = Double.NaN; /* the info on the datasource */ double[] vals = dataProcessor.getValues(tokens[rpi - 1].variable); int locstep = (int) Math.ceil((float) locstepsize / (float) timeStep); /* the sums */ double sum = 0; double sum2 = 0; int count = 0; /* now loop for each position */ int doshifts = Math.abs(num_shifts); for (int loop = 0; loop < doshifts; loop++) { /* calculate shift step */ int shiftstep = 1; if (num_shifts < 0) { shiftstep = loop * (int) multipliers[0]; } else { shiftstep = (int) multipliers[loop]; } if (shiftstep < 0) { throw new RrdException("negative shift step not allowed: " + shiftstep); } shiftstep = (int) Math.ceil((float) shiftstep / (float) timeStep); /* loop all local shifts */ for (int i = 0; i <= locstep; i++) { int offset = shiftstep + i; if ((offset >= 0) && (offset < slot)) { /* get the value */ val = vals[slot - offset]; /* and handle the non NAN case only*/ if (!Double.isNaN(val)) { sum = Util.sum(sum, val); sum2 = Util.sum(sum2, val * val); count++; } } } } /* do the final calculations */ val = Double.NaN; if (token.id == TKN_PREDICT) { /* the average */ if (count > 0) { val = sum / (double) count; } } else { if (count > 1) { /* the sigma case */ val = count * sum2 - sum * sum; if (val < 0) { val = Double.NaN; } else { val = Math.sqrt(val / ((float) count * ((float) count - 1.0))); } } } push(val); } break; default: throw new RrdException("Unexpected RPN token encountered, token.id=" + token.id); } } calculatedValues[slot] = pop(); // check if stack is empty only on the first try if (slot == 0 && !isStackEmpty()) { throw new RrdException( "Stack not empty at the end of calculation. " + "Probably bad RPN expression [" + rpnExpression + "]"); } } return calculatedValues; }
private Token createToken(String parsedText) throws RrdException { Token token = new Token(); if (Util.isDouble(parsedText)) { token.id = TKN_NUM; token.number = Util.parseDouble(parsedText); } else if (parsedText.equals("+")) { token.id = TKN_PLUS; } else if (parsedText.equals("-")) { token.id = TKN_MINUS; } else if (parsedText.equals("*")) { token.id = TKN_MULT; } else if (parsedText.equals("/")) { token.id = TKN_DIV; } else if (parsedText.equals("%")) { token.id = TKN_MOD; } else if (parsedText.equals("SIN")) { token.id = TKN_SIN; } else if (parsedText.equals("COS")) { token.id = TKN_COS; } else if (parsedText.equals("LOG")) { token.id = TKN_LOG; } else if (parsedText.equals("EXP")) { token.id = TKN_EXP; } else if (parsedText.equals("FLOOR")) { token.id = TKN_FLOOR; } else if (parsedText.equals("CEIL")) { token.id = TKN_CEIL; } else if (parsedText.equals("ROUND")) { token.id = TKN_ROUND; } else if (parsedText.equals("POW")) { token.id = TKN_POW; } else if (parsedText.equals("ABS")) { token.id = TKN_ABS; } else if (parsedText.equals("SQRT")) { token.id = TKN_SQRT; } else if (parsedText.equals("RANDOM")) { token.id = TKN_RANDOM; } else if (parsedText.equals("LT")) { token.id = TKN_LT; } else if (parsedText.equals("LE")) { token.id = TKN_LE; } else if (parsedText.equals("GT")) { token.id = TKN_GT; } else if (parsedText.equals("GE")) { token.id = TKN_GE; } else if (parsedText.equals("EQ")) { token.id = TKN_EQ; } else if (parsedText.equals("IF")) { token.id = TKN_IF; } else if (parsedText.equals("MIN")) { token.id = TKN_MIN; } else if (parsedText.equals("MAX")) { token.id = TKN_MAX; } else if (parsedText.equals("LIMIT")) { token.id = TKN_LIMIT; } else if (parsedText.equals("DUP")) { token.id = TKN_DUP; } else if (parsedText.equals("EXC")) { token.id = TKN_EXC; } else if (parsedText.equals("POP")) { token.id = TKN_POP; } else if (parsedText.equals("UN")) { token.id = TKN_UN; } else if (parsedText.equals("UNKN")) { token.id = TKN_UNKN; } else if (parsedText.equals("NOW")) { token.id = TKN_NOW; } else if (parsedText.equals("TIME")) { token.id = TKN_TIME; } else if (parsedText.equals("LTIME")) { token.id = TKN_LTIME; } else if (parsedText.equals("PI")) { token.id = TKN_PI; } else if (parsedText.equals("E")) { token.id = TKN_E; } else if (parsedText.equals("AND")) { token.id = TKN_AND; } else if (parsedText.equals("OR")) { token.id = TKN_OR; } else if (parsedText.equals("XOR")) { token.id = TKN_XOR; } else if (parsedText.equals("PREV")) { token.id = TKN_PREV; token.variable = sourceName; token.values = calculatedValues; } else if (parsedText.startsWith("PREV(") && parsedText.endsWith(")")) { token.id = TKN_PREV; token.variable = parsedText.substring(5, parsedText.length() - 1); token.values = dataProcessor.getValues(token.variable); } else if (parsedText.equals("INF")) { token.id = TKN_INF; } else if (parsedText.equals("NEGINF")) { token.id = TKN_NEGINF; } else if (parsedText.equals("STEP")) { token.id = TKN_STEP; } else if (parsedText.equals("YEAR")) { token.id = TKN_YEAR; } else if (parsedText.equals("MONTH")) { token.id = TKN_MONTH; } else if (parsedText.equals("DATE")) { token.id = TKN_DATE; } else if (parsedText.equals("HOUR")) { token.id = TKN_HOUR; } else if (parsedText.equals("MINUTE")) { token.id = TKN_MINUTE; } else if (parsedText.equals("SECOND")) { token.id = TKN_SECOND; } else if (parsedText.equals("WEEK")) { token.id = TKN_WEEK; } else if (parsedText.equals("SIGN")) { token.id = TKN_SIGN; } else if (parsedText.equals("RND")) { token.id = TKN_RND; } else if (parsedText.equals("ADDNAN")) { token.id = TKN_ADDNAN; } else if (parsedText.equals("NE")) { token.id = TKN_NE; } else if (parsedText.equals("ISINF")) { token.id = TKN_ISINF; } else if (parsedText.equals("ATAN")) { token.id = TKN_ATAN; } else if (parsedText.equals("ATAN2")) { token.id = TKN_ATAN2; } else if (parsedText.equals("DEG2RAD")) { token.id = TKN_DEG2RAD; } else if (parsedText.equals("RAD2DEG")) { token.id = TKN_RAD2DEG; } else if (parsedText.equals("COUNT")) { token.id = TKN_COUNT; } else if (parsedText.equals("SORT")) { token.id = TKN_SORT; } else if (parsedText.equals("REV")) { token.id = TKN_REV; } else if (parsedText.equals("AVG")) { token.id = TKN_AVG; } else if (parsedText.equals("TREND")) { token.id = TKN_TREND; } else if (parsedText.equals("TRENDNAN")) { token.id = TKN_TRENDNAN; } else if (parsedText.equals("PREDICT")) { token.id = TKN_PREDICT; } else if (parsedText.equals("PREDICTSIGMA")) { token.id = TKN_PREDICTSIGMA; } else { token.id = TKN_VAR; token.variable = parsedText; token.values = dataProcessor.getValues(token.variable); } return token; }