prepare for the release of rrdtool-1.2.16
[rrdtool.git] / src / rrd_rpncalc.c
index 94796d1..bf74e39 100644 (file)
@@ -1,5 +1,5 @@
 /****************************************************************************
- * RRDtool 1.0.28  Copyright Tobias Oetiker, 1997 - 2002
+ * RRDtool 1.2.16  Copyright by Tobi Oetiker, 1997-2006
  ****************************************************************************
  * rrd_rpncalc.c  RPN calculator functions
  ****************************************************************************/
@@ -97,7 +97,7 @@ void rpn_compact2str(rpn_cdefds_t *rpnc,ds_def_t *ds_def,char **str)
         
         if (rpnc[i].op == OP_NUMBER) {
             /* convert a short into a string */
-#ifdef WIN32
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
             _itoa(rpnc[i].val,buffer,10);
 #else
             sprintf(buffer,"%d",rpnc[i].val);
@@ -153,8 +153,15 @@ void rpn_compact2str(rpn_cdefds_t *rpnc,ds_def_t *ds_def,char **str)
          add_op(OP_NOW,NOW)
          add_op(OP_LTIME,LTIME)
          add_op(OP_TIME,TIME)
+         add_op(OP_ATAN2,ATAN2)
          add_op(OP_ATAN,ATAN)
-
+         add_op(OP_SQRT,SQRT)
+         add_op(OP_SORT,SORT)
+         add_op(OP_REV,REV)
+         add_op(OP_TREND,TREND)
+         add_op(OP_RAD2DEG,RAD2DEG)
+         add_op(OP_DEG2RAD,DEG2RAD)
+         add_op(OP_AVG,AVG)
 #undef add_op
               }
     (*str)[offset] = '\0';
@@ -188,7 +195,7 @@ void parseCDEF_DS(char *def,rrd_t *rrd, int ds_idx)
     
     rpnp = rpn_parse((void*) rrd, def, &lookup_DS);
     if (rpnp == NULL) {
-        rrd_set_error("failed to parse computed data source %s", def);
+        rrd_set_error("failed to parse computed data source");
         return;
     }
     /* Check for OP nodes not permitted in COMPUTE DS.
@@ -246,13 +253,15 @@ long lookup_DS(void *rrd_vptr,char *ds_name)
  * lookup(): a function that retrieves a numeric key given a variable name
  */
 rpnp_t * 
-rpn_parse(void *key_hash,char *expr,long (*lookup)(void *,char*)){
+rpn_parse(void *key_hash,const char *const expr_const,long (*lookup)(void *,char*)){
     int pos=0;
+    char *expr;
     long steps=-1;    
     rpnp_t  *rpnp;
-    char vname[30];
+    char vname[MAX_VNAME_LEN+10];
     
     rpnp=NULL;
+    expr=(char *)expr_const;
     
     while(*expr){
        if ((rpnp = (rpnp_t *) rrd_realloc(rpnp, (++steps + 2)* 
@@ -266,9 +275,9 @@ rpn_parse(void *key_hash,char *expr,long (*lookup)(void *,char*)){
        } 
        
 #define match_op(VV,VVV) \
-        else if (strncmp(expr, #VVV, strlen(#VVV))==0){ \
-           rpnp[steps].op = VV; \
-           expr+=strlen(#VVV); \
+        else if (strncmp(expr, #VVV, strlen(#VVV))==0 && ( expr[strlen(#VVV)] == ',' || expr[strlen(#VVV)] == '\0' )){ \
+            rpnp[steps].op = VV; \
+            expr+=strlen(#VVV); \
        }
 
 
@@ -316,14 +325,21 @@ rpn_parse(void *key_hash,char *expr,long (*lookup)(void *,char*)){
        match_op(OP_NEGINF,NEGINF)
        match_op(OP_NE,NE)
        match_op(OP_COUNT,COUNT)
-         match_op_param(OP_PREV_OTHER,PREV)
+       match_op_param(OP_PREV_OTHER,PREV)
        match_op(OP_PREV,PREV)
        match_op(OP_INF,INF)
        match_op(OP_ISINF,ISINF)
        match_op(OP_NOW,NOW)
        match_op(OP_TIME,TIME)
+       match_op(OP_ATAN2,ATAN2)
        match_op(OP_ATAN,ATAN)
-
+       match_op(OP_SQRT,SQRT)
+       match_op(OP_SORT,SORT)
+       match_op(OP_REV,REV)
+       match_op(OP_TREND,TREND)
+       match_op(OP_RAD2DEG,RAD2DEG)
+       match_op(OP_DEG2RAD,DEG2RAD)
+       match_op(OP_AVG,AVG)
 #undef match_op
 
 
@@ -367,6 +383,14 @@ rpnstack_free(rpnstack_t *rpnstack)
    rpnstack -> dc_stacksize = 0;
 }
 
+static int
+rpn_compare_double(const void *x, const void *y)
+{
+       double  diff = *((const double *)x) - *((const double *)y);
+       
+       return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
+}
+
 /* rpn_calc: run the RPN calculator; also performs variable substitution;
  * moved and modified from data_calc() originally included in rrd_graph.c 
  * arguments:
@@ -414,7 +438,8 @@ rpn_calc(rpnp_t *rpnp, rpnstack_t *rpnstack, long data_idx,
                rpnstack -> s[++stptr] = rpnp[rpi].val;
                break;
            case OP_VARIABLE:
-               /* Sanity check: VDEFs shouldn't make it here */
+            case OP_PREV_OTHER:
+           /* Sanity check: VDEFs shouldn't make it here */
                if (rpnp[rpi].ds_cnt == 0) {
                    rrd_set_error("VDEF made it into rpn_calc... aborting");
                    return -1;
@@ -425,7 +450,16 @@ rpn_calc(rpnp_t *rpnp, rpnstack_t *rpnstack, long data_idx,
                     * row in the rra (skip over non-relevant
                     * data sources)
                     */
-                   rpnstack -> s[++stptr] =  *(rpnp[rpi].data);
+                   if (rpnp[rpi].op == OP_VARIABLE) {
+                       rpnstack -> s[++stptr] =  *(rpnp[rpi].data);
+                   } else {
+                       if ((output_idx) <= 0) {
+                           rpnstack -> s[++stptr] = DNAN;
+                       } else {                            
+                           rpnstack -> s[++stptr] =  *(rpnp[rpi].data-rpnp[rpi].ds_cnt);
+                       }
+                      
+                   }              
                    if (data_idx % rpnp[rpi].step == 0){
                        rpnp[rpi].data += rpnp[rpi].ds_cnt;
                    }
@@ -440,15 +474,8 @@ rpn_calc(rpnp_t *rpnp, rpnstack_t *rpnstack, long data_idx,
                } else {
                    rpnstack -> s[++stptr] = output[output_idx-1];
                }
-               break;
-       case OP_PREV_OTHER:
-         if ((output_idx) <= 0) {
-               rpnstack -> s[++stptr] = DNAN;
-         } else {
-               rpnstack -> s[++stptr] = rpnp[rpnp[rpi].ptr].data[output_idx-1];
-         }
-         break;
-           case OP_UNKN:
+               break;
+        case OP_UNKN:
                rpnstack -> s[++stptr] = DNAN; 
                break;
            case OP_INF:
@@ -507,6 +534,21 @@ rpn_calc(rpnp_t *rpnp, rpnstack_t *rpnstack, long data_idx,
                stackunderflow(0);
                rpnstack -> s[stptr] = atan(rpnstack -> s[stptr]);
                break;
+           case OP_RAD2DEG:
+               stackunderflow(0);
+               rpnstack -> s[stptr] = 57.29577951 * rpnstack -> s[stptr];
+               break;
+           case OP_DEG2RAD:
+               stackunderflow(0);
+               rpnstack -> s[stptr] = 0.0174532952 * rpnstack -> s[stptr];
+               break;
+           case OP_ATAN2:
+               stackunderflow(1);
+               rpnstack -> s[stptr-1]= atan2(
+                               rpnstack -> s[stptr-1],
+                               rpnstack -> s[stptr]);
+               stptr--;
+               break;
            case OP_COS:
                stackunderflow(0);
                rpnstack -> s[stptr] = cos(rpnstack -> s[stptr]);
@@ -660,6 +702,83 @@ rpn_calc(rpnp_t *rpnp, rpnstack_t *rpnstack, long data_idx,
                stackunderflow(0);
                rpnstack->s[stptr] = isinf(rpnstack->s[stptr]) ? 1.0 : 0.0;
                break;
+           case OP_SQRT:
+               stackunderflow(0);
+               rpnstack -> s[stptr] = sqrt(rpnstack -> s[stptr]);
+               break;
+           case OP_SORT:
+               stackunderflow(0);
+               {
+                   int spn = (int)rpnstack -> s[stptr--];
+
+                   stackunderflow(spn-1);
+                   qsort(rpnstack -> s + stptr-spn+1, spn, sizeof(double),
+                         rpn_compare_double);
+               }
+               break;
+           case OP_REV:
+               stackunderflow(0);
+               {
+                   int spn = (int)rpnstack -> s[stptr--];
+                   double *p, *q;
+
+                   stackunderflow(spn-1);
+
+                   p = rpnstack -> s + stptr-spn+1;
+                   q = rpnstack -> s + stptr;
+                   while (p < q) {
+                           double      x = *q;
+                           
+                           *q-- = *p;
+                           *p++ = x;
+                   }
+               }
+               break;
+           case OP_TREND:
+               stackunderflow(1);
+               if ((rpi < 2) || (rpnp[rpi-2].op != OP_VARIABLE)) {
+                   rrd_set_error("malformed trend arguments");
+                   return -1;
+               } else {
+                   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)) {
+                       double accum = 0.0;
+                       int i = 0;
+               
+                       do {
+                           accum += rpnp[rpi-2].data[rpnp[rpi-2].ds_cnt * i--];
+                           dur -= step;
+                       } while (dur > 0);
+
+                       rpnstack -> s[--stptr] = (accum / -i);
+                   } else
+                       rpnstack -> s[--stptr] = DNAN;
+               }
+               break;
+           case OP_AVG:
+               stackunderflow(0);
+                {
+                   int i=(int)rpnstack -> s[stptr--];
+                   double sum=0;
+                   int count=0;
+                   stackunderflow(i-1);
+                   while(i>0) {
+                     double val=rpnstack -> s[stptr--];
+                     i--;
+                    if (isnan(val)) { continue; }
+                     count++;
+                     sum+=val;
+                   }
+                   // now push the result bavk on stack
+                   if (count>0) {
+                     rpnstack -> s[++stptr]=sum/count;
+                   } else {
+                     rpnstack -> s[++stptr]=DNAN;
+                   }
+                }
+                break;
            case OP_END:
                break;
        }