the -r option should not expect an argument
[rrdtool.git] / src / rrd_graph.c
index 2a13cb5..d80b74c 100644 (file)
@@ -42,22 +42,25 @@ text_prop_t text_prop[] = {
 };
 
 xlab_t xlab[] = {
-    {0,        TMT_SECOND,30, TMT_MINUTE,5,  TMT_MINUTE,5,         0,"%H:%M"},
-    {2,        TMT_MINUTE,1,  TMT_MINUTE,5,  TMT_MINUTE,5,         0,"%H:%M"},
-    {5,        TMT_MINUTE,2,  TMT_MINUTE,10, TMT_MINUTE,10,        0,"%H:%M"},
-    {10,       TMT_MINUTE,5,  TMT_MINUTE,20, TMT_MINUTE,20,        0,"%H:%M"},
-    {30,       TMT_MINUTE,10, TMT_HOUR,1,    TMT_HOUR,1,           0,"%H:%M"},
-    {60,       TMT_MINUTE,30, TMT_HOUR,2,    TMT_HOUR,2,           0,"%H:%M"},
-    {180,      TMT_HOUR,1,    TMT_HOUR,6,    TMT_HOUR,6,           0,"%H:%M"},
-    /*{300,      TMT_HOUR,3,    TMT_HOUR,12,   TMT_HOUR,12,    12*3600,"%a %p"},  this looks silly*/
-    {600,      TMT_HOUR,6,    TMT_DAY,1,     TMT_DAY,1,      24*3600,"%a"},
-    {1800,     TMT_HOUR,12,   TMT_DAY,1,     TMT_DAY,2,      24*3600,"%a"},
-    {3600,     TMT_DAY,1,     TMT_WEEK,1,     TMT_WEEK,1,    7*24*3600,"Week %V"},
-    {3*3600,   TMT_WEEK,1,      TMT_MONTH,1,     TMT_WEEK,2,    7*24*3600,"Week %V"},
-    {6*3600,   TMT_MONTH,1,   TMT_MONTH,1,   TMT_MONTH,1, 30*24*3600,"%b"},
-    {48*3600,  TMT_MONTH,1,   TMT_MONTH,3,   TMT_MONTH,3, 30*24*3600,"%b"},
-    {10*24*3600, TMT_YEAR,1,  TMT_YEAR,1,    TMT_YEAR,1, 365*24*3600,"%y"},
-    {-1,TMT_MONTH,0,TMT_MONTH,0,TMT_MONTH,0,0,""}
+    {0,                 0,   TMT_SECOND,30, TMT_MINUTE,5,  TMT_MINUTE,5,         0,"%H:%M"},
+    {2,                 0,   TMT_MINUTE,1,  TMT_MINUTE,5,  TMT_MINUTE,5,         0,"%H:%M"},
+    {5,                 0,   TMT_MINUTE,2,  TMT_MINUTE,10, TMT_MINUTE,10,        0,"%H:%M"},
+    {10,                0,   TMT_MINUTE,5,  TMT_MINUTE,20, TMT_MINUTE,20,        0,"%H:%M"},
+    {30,                0,   TMT_MINUTE,10, TMT_HOUR,1,    TMT_HOUR,1,           0,"%H:%M"},
+    {60,                0,   TMT_MINUTE,30, TMT_HOUR,2,    TMT_HOUR,2,           0,"%H:%M"},
+    {180,               0,   TMT_HOUR,1,    TMT_HOUR,6,    TMT_HOUR,6,           0,"%H:%M"},
+    {180,       1*24*3600,   TMT_HOUR,1,    TMT_HOUR,6,    TMT_HOUR,6,           0,"%a %H:%M"},
+    /*{300,             0,   TMT_HOUR,3,    TMT_HOUR,12,   TMT_HOUR,12,    12*3600,"%a %p"},  this looks silly*/
+    {600,               0,   TMT_HOUR,6,    TMT_DAY,1,     TMT_DAY,1,      24*3600,"%a"},
+    {600,       1*24*3600,   TMT_HOUR,6,    TMT_DAY,1,     TMT_DAY,1,      24*3600,"%a %d"},
+    {1800,              0,   TMT_HOUR,12,   TMT_DAY,1,     TMT_DAY,2,      24*3600,"%a"},
+    {1800,      1*24*3600,   TMT_HOUR,12,   TMT_DAY,1,     TMT_DAY,2,      24*3600,"%a %d"},
+    {3600,              0,   TMT_DAY,1,     TMT_WEEK,1,    TMT_WEEK,1,    7*24*3600,"Week %V"},
+    {3*3600,            0,   TMT_WEEK,1,    TMT_MONTH,1,   TMT_WEEK,2,    7*24*3600,"Week %V"},
+    {6*3600,            0,   TMT_MONTH,1,   TMT_MONTH,1,   TMT_MONTH,1, 30*24*3600,"%b"},
+    {48*3600,           0,   TMT_MONTH,1,   TMT_MONTH,3,   TMT_MONTH,3, 30*24*3600,"%b"},
+    {10*24*3600,        0,   TMT_YEAR,1,  TMT_YEAR,1,    TMT_YEAR,1, 365*24*3600,"%y"},
+    {-1,0,TMT_MONTH,0,TMT_MONTH,0,TMT_MONTH,0,0,""}
 };
 
 /* sensible logarithmic y label intervals ...
@@ -1768,7 +1771,9 @@ vertical_grid(
        factor=(im->end - im->start)/im->xsize;
        xlab_sel=0;
        while ( xlab[xlab_sel+1].minsec != -1 
-               && xlab[xlab_sel+1].minsec <= factor){ xlab_sel++; }
+               && xlab[xlab_sel+1].minsec <= factor) { xlab_sel++; }   /* pick the last one */
+       while ( xlab[xlab_sel-1].minsec == xlab[xlab_sel].minsec
+               && xlab[xlab_sel].length > (im->end - im->start)) { xlab_sel--; }       /* go back to the smallest size */
        im->xlab_user.gridtm = xlab[xlab_sel].gridtm;
        im->xlab_user.gridst = xlab[xlab_sel].gridst;
        im->xlab_user.mgridtm = xlab[xlab_sel].mgridtm;
@@ -2292,6 +2297,39 @@ graph_size_location(image_desc_t *im, int elements
     return 0;
 }
 
+/* from http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm */
+/* yes we are loosing precision by doing tos with floats instead of doubles
+   but it seems more stable this way. */
+   
+static int AlmostEqual2sComplement (float A, float B, int maxUlps)
+{
+
+    int aInt = *(int*)&A;
+    int bInt = *(int*)&B;
+    int intDiff;
+    /* Make sure maxUlps is non-negative and small enough that the
+       default NAN won't compare as equal to anything.  */
+
+    /* assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); */
+
+    /* Make aInt lexicographically ordered as a twos-complement int */
+
+    if (aInt < 0)
+        aInt = 0x80000000l - aInt;
+
+    /* Make bInt lexicographically ordered as a twos-complement int */
+
+    if (bInt < 0)
+        bInt = 0x80000000l - bInt;
+
+    intDiff = abs(aInt - bInt);
+
+    if (intDiff <= maxUlps)
+        return 1;
+
+    return 0;
+}
+
 /* draw that picture thing ... */
 int
 graph_paint(image_desc_t *im, char ***calcpr)
@@ -2472,7 +2510,7 @@ graph_paint(image_desc_t *im, char ***calcpr)
       if (im->gdes[i].col != 0x0){   
         /* GF_LINE and friend */
         if(stack_gf == GF_LINE ){
-          double last_y=0;
+          double last_y=0.0;
           node = NULL;
           for(ii=1;ii<im->xsize;ii++){
            if (isnan(im->gdes[i].p_data[ii]) || (im->slopemode==1 && isnan(im->gdes[i].p_data[ii-1]))){
@@ -2496,10 +2534,10 @@ graph_paint(image_desc_t *im, char ***calcpr)
                }
              } else {
                double new_y = ytr(im,im->gdes[i].p_data[ii]);
-              if ( im->slopemode==0 && new_y != last_y){
+              if ( im->slopemode==0 && ! AlmostEqual2sComplement(new_y,last_y,4)){
                    gfx_add_point(node,ii-1+im->xorigin,new_y);
-                   last_y = new_y;
               };
+               last_y = new_y;
                gfx_add_point(node,ii+im->xorigin,new_y);
              };
 
@@ -2516,7 +2554,7 @@ graph_paint(image_desc_t *im, char ***calcpr)
            if ( idxI > 0 && ( drawem != 0 || ii==im->xsize)){
                int cntI=1;
                int lastI=0;
-               while (cntI < idxI && foreY[lastI] == foreY[cntI] && foreY[lastI] == foreY[cntI+1]){cntI++;}
+               while (cntI < idxI && AlmostEqual2sComplement(foreY[lastI],foreY[cntI],4) && AlmostEqual2sComplement(foreY[lastI],foreY[cntI+1],4)){cntI++;}
                node = gfx_new_area(im->canvas,
                                 backX[0],backY[0],
                                 foreX[0],foreY[0],
@@ -2524,14 +2562,14 @@ graph_paint(image_desc_t *im, char ***calcpr)
                while (cntI < idxI) {
                  lastI = cntI;
                  cntI++;
-                 while ( cntI < idxI && foreY[lastI] == foreY[cntI] && foreY[lastI] == foreY[cntI+1]){cntI++;} 
+                 while ( cntI < idxI && AlmostEqual2sComplement(foreY[lastI],foreY[cntI],4) && AlmostEqual2sComplement(foreY[lastI],foreY[cntI+1],4)){cntI++;} 
                  gfx_add_point(node,foreX[cntI],foreY[cntI]);
                }
                gfx_add_point(node,backX[idxI],backY[idxI]);
                while (idxI > 1){
                  lastI = idxI;
                  idxI--;
-                 while ( idxI > 1 && backY[lastI] == backY[idxI] && backY[lastI] == backY[idxI-1]){idxI--;} 
+                 while ( idxI > 1 && AlmostEqual2sComplement(backY[lastI], backY[idxI],4) && AlmostEqual2sComplement(backY[lastI],backY[idxI-1],4)){idxI--;} 
                  gfx_add_point(node,backX[idxI],backY[idxI]);
                }
                idxI=-1;
@@ -3406,6 +3444,9 @@ char *str;
     else if    (!strcmp("TOTAL",  func)) gdes->vf.op = VDEF_TOTAL;
     else if    (!strcmp("FIRST",  func)) gdes->vf.op = VDEF_FIRST;
     else if    (!strcmp("LAST",   func)) gdes->vf.op = VDEF_LAST;
+    else if     (!strcmp("LSLSLOPE", func)) gdes->vf.op = VDEF_LSLSLOPE;
+    else if     (!strcmp("LSLINT",   func)) gdes->vf.op = VDEF_LSLINT;
+    else if     (!strcmp("LSLCORREL",func)) gdes->vf.op = VDEF_LSLCORREL;
     else {
        rrd_set_error("Unknown function '%s' in VDEF '%s'\n"
            ,func
@@ -3441,6 +3482,9 @@ char *str;
        case VDEF_TOTAL:
        case VDEF_FIRST:
        case VDEF_LAST:
+       case VDEF_LSLSLOPE:
+       case VDEF_LSLINT:
+       case VDEF_LSLCORREL:
            if (isnan(param)) {
                gdes->vf.param = DNAN;
                gdes->vf.val   = DNAN;
@@ -3599,6 +3643,48 @@ printf("DEBUG: %3li:%10.2f %c\n",step,array[step],step==field?'*':' ');
                dst->vf.when = src->start + (step+1)*src->step;
            }
            break;
+       case VDEF_LSLSLOPE:
+       case VDEF_LSLINT:
+       case VDEF_LSLCORREL:{
+           /* Bestfit line by linear least squares method */ 
+
+           int cnt=0;
+           double SUMx, SUMy, SUMxy, SUMxx, SUMyy, slope, y_intercept, correl ;
+           SUMx = 0; SUMy = 0; SUMxy = 0; SUMxx = 0; SUMyy = 0;
+
+           for (step=0;step<steps;step++) {
+               if (finite(data[step*src->ds_cnt])) {
+                   cnt++;
+                   SUMx  += step;
+                   SUMxx += step * step;
+                   SUMxy += step * data[step*src->ds_cnt];
+                   SUMy  += data[step*src->ds_cnt];
+                   SUMyy  += data[step*src->ds_cnt]*data[step*src->ds_cnt];
+               };
+           }
+
+           slope = ( SUMx*SUMy - cnt*SUMxy ) / ( SUMx*SUMx - cnt*SUMxx );
+           y_intercept = ( SUMy - slope*SUMx ) / cnt;
+           correl = (SUMxy - (SUMx*SUMy)/cnt) / sqrt((SUMxx - (SUMx*SUMx)/cnt)*(SUMyy - (SUMy*SUMy)/cnt));
+
+           if (cnt) {
+                   if (dst->vf.op == VDEF_LSLSLOPE) {
+                       dst->vf.val  = slope;
+                       dst->vf.when = cnt*src->step;
+                   } else if (dst->vf.op == VDEF_LSLINT)  {
+                       dst->vf.val = y_intercept;
+                       dst->vf.when = cnt*src->step;
+                   } else if (dst->vf.op == VDEF_LSLCORREL)  {
+                       dst->vf.val = correl;
+                       dst->vf.when = cnt*src->step;
+                   };
+               
+           } else {
+               dst->vf.val  = DNAN;
+               dst->vf.when = 0;
+           }
+           }
+           break;
     }
     return 0;
 }