the number of COMPUTE rpn nodes is architecture dependent. calculate the right number...
[rrdtool.git] / src / rrd_rpncalc.c
index 2fc6ca7..2bd1474 100644 (file)
@@ -1,14 +1,16 @@
 /****************************************************************************
- * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
  ****************************************************************************
  * rrd_rpncalc.c  RPN calculator functions
  ****************************************************************************/
 
-#include "rrd_tool.h"
-#include "rrd_rpncalc.h"
-#include "rrd_graph.h"
 #include <limits.h>
 #include <locale.h>
+#include <stdlib.h>
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+// #include "rrd_graph.h"
 
 short     addop2str(
     enum op_en op,
@@ -31,8 +33,8 @@ short rpn_compact(
     while (rpnp[*count].op != OP_END)
         (*count)++;
     if (++(*count) > DS_CDEF_MAX_RPN_NODES) {
-        rrd_set_error("Maximum %d RPN nodes permitted",
-                      DS_CDEF_MAX_RPN_NODES);
+        rrd_set_error("Maximum %d RPN nodes permitted. Got %d RPN nodes at present.",
+                      DS_CDEF_MAX_RPN_NODES-1,(*count)-1);
         return -1;
     }
 
@@ -70,10 +72,12 @@ rpnp_t   *rpn_expand(
     /* DS_CDEF_MAX_RPN_NODES is small, so at the expense of some wasted
      * memory we avoid any reallocs */
     rpnp = (rpnp_t *) calloc(DS_CDEF_MAX_RPN_NODES, sizeof(rpnp_t));
-    if (rpnp == NULL)
+    if (rpnp == NULL) {
+        rrd_set_error("failed allocating rpnp array");
         return NULL;
+    }
     for (i = 0; rpnc[i].op != OP_END; ++i) {
-        rpnp[i].op = (long) rpnc[i].op;
+        rpnp[i].op = (enum op_en)rpnc[i].op;
         if (rpnp[i].op == OP_NUMBER) {
             rpnp[i].val = (double) rpnc[i].val;
         } else if (rpnp[i].op == OP_VARIABLE || rpnp[i].op == OP_PREV_OTHER) {
@@ -106,7 +110,7 @@ void rpn_compact2str(
             (*str)[offset++] = ',';
 
 #define add_op(VV,VVV) \
-         if (addop2str(rpnc[i].op, VV, VVV, str, &offset) == 1) continue;
+         if (addop2str((enum op_en)(rpnc[i].op), VV, VVV, str, &offset) == 1) continue;
 
         if (rpnc[i].op == OP_NUMBER) {
             /* convert a short into a string */
@@ -132,7 +136,7 @@ void rpn_compact2str(
 #undef add_op
 
 #define add_op(VV,VVV) \
-         if (addop2str(rpnc[i].op, VV, #VVV, str, &offset) == 1) continue;
+         if (addop2str((enum op_en)rpnc[i].op, VV, #VVV, str, &offset) == 1) continue;
 
         add_op(OP_ADD, +)
             add_op(OP_SUB, -)
@@ -174,10 +178,13 @@ void rpn_compact2str(
             add_op(OP_REV, REV)
             add_op(OP_TREND, TREND)
             add_op(OP_TRENDNAN, TRENDNAN)
+            add_op(OP_PREDICT, PREDICT)
+            add_op(OP_PREDICTSIGMA, PREDICTSIGMA)
             add_op(OP_RAD2DEG, RAD2DEG)
             add_op(OP_DEG2RAD, DEG2RAD)
             add_op(OP_AVG, AVG)
             add_op(OP_ABS, ABS)
+            add_op(OP_ADDNAN, ADDNAN)
 #undef add_op
     }
     (*str)[offset] = '\0';
@@ -229,9 +236,11 @@ void parseCDEF_DS(
      * occur too often. */
     for (i = 0; rpnp[i].op != OP_END; i++) {
         if (rpnp[i].op == OP_TIME || rpnp[i].op == OP_LTIME ||
-            rpnp[i].op == OP_PREV || rpnp[i].op == OP_COUNT) {
+            rpnp[i].op == OP_PREV || rpnp[i].op == OP_COUNT ||
+            rpnp[i].op == OP_TREND || rpnp[i].op == OP_TRENDNAN ||
+            rpnp[i].op == OP_PREDICT || rpnp[i].op ==  OP_PREDICTSIGMA ) {
             rrd_set_error
-                ("operators time, ltime, prev and count not supported with DS COMPUTE");
+                ("operators TIME, LTIME, PREV COUNT TREND TRENDNAN PREDICT PREDICTSIGMA are not supported with DS COMPUTE");
             free(rpnp);
             return;
         }
@@ -288,8 +297,10 @@ rpnp_t   *rpn_parse(
     long      steps = -1;
     rpnp_t   *rpnp;
     char      vname[MAX_VNAME_LEN + 10];
-    char *old_locale;
-    old_locale = setlocale(LC_NUMERIC,"C");
+    char     *old_locale;
+
+    old_locale = setlocale(LC_NUMERIC, NULL);
+    setlocale(LC_NUMERIC, "C");
 
     rpnp = NULL;
     expr = (char *) expr_const;
@@ -297,10 +308,10 @@ rpnp_t   *rpn_parse(
     while (*expr) {
         if ((rpnp = (rpnp_t *) rrd_realloc(rpnp, (++steps + 2) *
                                            sizeof(rpnp_t))) == NULL) {
-            setlocale(LC_NUMERIC,old_locale);
+            setlocale(LC_NUMERIC, old_locale);
             return NULL;
         }
-    
+
         else if ((sscanf(expr, "%lf%n", &rpnp[steps].val, &pos) == 1)
                  && (expr[pos] == ',')) {
             rpnp[steps].op = OP_NUMBER;
@@ -369,10 +380,13 @@ rpnp_t   *rpn_parse(
             match_op(OP_REV, REV)
             match_op(OP_TREND, TREND)
             match_op(OP_TRENDNAN, TRENDNAN)
+            match_op(OP_PREDICT, PREDICT)
+            match_op(OP_PREDICTSIGMA, PREDICTSIGMA)
             match_op(OP_RAD2DEG, RAD2DEG)
             match_op(OP_DEG2RAD, DEG2RAD)
             match_op(OP_AVG, AVG)
             match_op(OP_ABS, ABS)
+            match_op(OP_ADDNAN, ADDNAN)
 #undef match_op
             else if ((sscanf(expr, DEF_NAM_FMT "%n", vname, &pos) == 1)
                      && ((rpnp[steps].ptr = (*lookup) (key_hash, vname)) !=
@@ -382,7 +396,7 @@ rpnp_t   *rpn_parse(
         }
 
         else {
-            setlocale(LC_NUMERIC,old_locale);
+            setlocale(LC_NUMERIC, old_locale);
             free(rpnp);
             return NULL;
         }
@@ -392,13 +406,13 @@ rpnp_t   *rpn_parse(
         if (*expr == ',')
             expr++;
         else {
-            setlocale(LC_NUMERIC,old_locale);
+            setlocale(LC_NUMERIC, old_locale);
             free(rpnp);
             return NULL;
         }
     }
     rpnp[steps + 1].op = OP_END;
-    setlocale(LC_NUMERIC,old_locale);
+    setlocale(LC_NUMERIC, old_locale);
     return rpnp;
 }
 
@@ -458,7 +472,7 @@ short rpn_calc(
         if (stptr + 5 > rpnstack->dc_stacksize) {
             /* could move this to a separate function */
             rpnstack->dc_stacksize += rpnstack->dc_stackblock;
-            rpnstack->s = rrd_realloc(rpnstack->s,
+            rpnstack->s = (double*)rrd_realloc(rpnstack->s,
                                       (rpnstack->dc_stacksize) *
                                       sizeof(*(rpnstack->s)));
             if (rpnstack->s == NULL) {
@@ -542,6 +556,20 @@ short rpn_calc(
                 + rpnstack->s[stptr];
             stptr--;
             break;
+        case OP_ADDNAN:
+            stackunderflow(1);
+            if (isnan(rpnstack->s[stptr - 1])) {
+                rpnstack->s[stptr - 1] = rpnstack->s[stptr];
+            } else if (isnan(rpnstack->s[stptr])) {
+                /* NOOP */
+                /* rpnstack->s[stptr - 1] = rpnstack->s[stptr - 1]; */
+            } else {
+                rpnstack->s[stptr - 1] = rpnstack->s[stptr - 1]
+                    + rpnstack->s[stptr];
+            }
+
+            stptr--;
+            break;
         case OP_SUB:
             stackunderflow(1);
             rpnstack->s[stptr - 1] = rpnstack->s[stptr - 1]
@@ -689,8 +717,10 @@ short rpn_calc(
             break;
         case OP_IF:
             stackunderflow(2);
-            rpnstack->s[stptr - 2] = rpnstack->s[stptr - 2] != 0.0 ?
-                rpnstack->s[stptr - 1] : rpnstack->s[stptr];
+            rpnstack->s[stptr - 2] = (isnan(rpnstack->s[stptr - 2])
+                                      || rpnstack->s[stptr - 2] ==
+                                      0.0) ? rpnstack->s[stptr] : rpnstack->
+                s[stptr - 1];
             stptr--;
             stptr--;
             break;
@@ -765,6 +795,84 @@ short rpn_calc(
                 }
             }
             break;
+        case OP_PREDICT:
+        case OP_PREDICTSIGMA:
+            stackunderflow(2);
+           {
+               /* the local averaging window (similar to trend, but better here, as we get better statistics thru numbers)*/
+               int   locstepsize = rpnstack->s[--stptr];
+               /* the number of shifts and range-checking*/
+               int     shifts = rpnstack->s[--stptr];
+                stackunderflow(shifts);
+               // handle negative shifts special
+               if (shifts<0) {
+                   stptr--;
+               } else {
+                   stptr-=shifts;
+               }
+               /* the real calculation */
+               double val=DNAN;
+               /* the info on the datasource */
+               time_t  dsstep = (time_t) rpnp[rpi - 1].step;
+               int    dscount = rpnp[rpi - 1].ds_cnt;
+               int   locstep = (int)ceil((float)locstepsize/(float)dsstep);
+
+               /* the sums */
+                double    sum = 0;
+               double    sum2 = 0;
+                int       count = 0;
+               /* now loop for each position */
+               int doshifts=shifts;
+               if (shifts<0) { doshifts=-shifts; }
+               for(int loop=0;loop<doshifts;loop++) {
+                   /* calculate shift step */
+                   int shiftstep=1;
+                   if (shifts<0) {
+                       shiftstep = loop*rpnstack->s[stptr];
+                   } else { 
+                       shiftstep = rpnstack->s[stptr+loop]; 
+                   }
+                   if(shiftstep <0) {
+                       rrd_set_error("negative shift step not allowed: %i",shiftstep);
+                       return -1;
+                   }
+                   shiftstep=(int)ceil((float)shiftstep/(float)dsstep);
+                   /* loop all local shifts */
+                   for(int i=0;i<=locstep;i++) {
+                       /* now calculate offset into data-array - relative to output_idx*/
+                       int offset=shiftstep+i;
+                       /* and process if we have index 0 of above */
+                       if ((offset>=0)&&(offset<output_idx)) {
+                           /* get the value */
+                           val =rpnp[rpi - 1].data[-dscount * offset];
+                           /* and handle the non NAN case only*/
+                           if (! isnan(val)) {
+                               sum+=val;
+                               sum2+=val*val;
+                               count++;
+                           }
+                       }
+                   }
+               }
+               /* do the final calculations */
+               val=DNAN;
+               if (rpnp[rpi].op == OP_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=DNAN;
+                       } else {
+                           val=sqrt(val/((float)count*((float)count-1.0)));
+                       }
+                   }
+               }
+               rpnstack->s[stptr] = val;
+           }
+            break;
         case OP_TREND:
         case OP_TRENDNAN:
             stackunderflow(1);
@@ -775,7 +883,7 @@ short rpn_calc(
                 time_t    dur = (time_t) rpnstack->s[stptr];
                 time_t    step = (time_t) rpnp[rpi - 2].step;
 
-                if (output_idx > (int) ceil((float) dur / (float) step)) {
+                if (output_idx + 1 >= (int) ceil((float) dur / (float) step)) {
                     int       ignorenan = (rpnp[rpi].op == OP_TREND);
                     double    accum = 0.0;
                     int       i = 0;