Add a delta operator

Computes the difference between successive samples.
1. If the value increases, emit the difference.
2. If the value decreases, emit the value.
3. If the value does not change, outputs undefined

This is most useful for a heatgrid of a continuously
increasing counter, to show where the counter changes.
diff --git a/render.js b/render.js
index 5a1f6b8..1d8a407 100644
--- a/render.js
+++ b/render.js
@@ -974,6 +974,61 @@
   }
 
 
+  function deltaBy(ingrid, keys) {
+    var outgrid = {headers: ingrid.headers, data: [], types: ingrid.types};
+    for (var rowi = 0; rowi < ingrid.data.length; rowi++) {
+      var row = ingrid.data[rowi];
+      outgrid.data.push(row);
+    }
+
+    var keycols = [];
+    for (var keyi in keys) {
+      var key = keys[keyi];
+      keycols.push(keyToColNum(ingrid, key));
+    }
+
+    if (outgrid.data.length < 2) {
+      return outgrid;
+    }
+    for (var keyi in keycols) {
+      var keycol = keycols[keyi];
+
+      var prev_val = undefined;
+      for (var rowi = 1; rowi < outgrid.data.length; rowi++) {
+        var row = outgrid.data[rowi];
+        var val = row[keycol];
+        console.debug('prev_val: ', prev_val, ' val: ', val);
+        if (val == undefined) {
+          continue;
+        } else if (outgrid.types[keycol] === T_NUM) {
+          if (prev_val != undefined) {
+            if (val > prev_val) {
+              var new_val = val - prev_val;
+              console.debug('out: ', new_val);
+              outgrid.data[rowi][keycol] = new_val;
+            } else if (val == prev_val) {
+              console.debug('out: ', undefined);
+              outgrid.data[rowi][keycol] = undefined;
+            }
+          }
+          prev_val = val;
+        }
+      }
+    }
+
+    return outgrid;
+  }
+
+
+  function doDeltaBy(grid, argval) {
+    console.debug('deltaBy:', argval);
+    console.debug('grid:', grid);
+    grid = deltaBy(grid, argval.split(','));
+    console.debug('grid:', grid);
+    return grid;
+  }
+
+
   function orderBy(grid, keys) {
     var keycols = [];
     for (var keyi in keys) {
@@ -1229,6 +1284,8 @@
         transform(doQueryBy, argval);
       } else if (argkey == 'limit') {
         transform(doLimit, argval);
+      } else if (argkey == 'delta') {
+        transform(doDeltaBy, argval);
       } else if (argkey == 'order') {
         transform(doOrderBy, argval);
       } else if (argkey == 'extract_regexp') {
@@ -1627,6 +1684,7 @@
       stringifiedCols: stringifiedCols,
       filterBy: filterBy,
       queryBy: queryBy,
+      deltaBy: deltaBy,
       orderBy: orderBy,
       extractRegexp: extractRegexp,
       fillNullsWithZero: fillNullsWithZero,
diff --git a/t/trender.js b/t/trender.js
index cfac92e..ba09458 100644
--- a/t/trender.js
+++ b/t/trender.js
@@ -109,6 +109,35 @@
 });
 
 
+wvtest('delta', function() {
+  var grid = {
+    headers: ['a', 'b'],
+    types: ['number', 'number'],
+    data: [
+      [0, 1],
+      [5, 7],
+      [30, 1],
+      [2, 1],
+      [2, 1],
+    ]
+  };
+  var dt = afterquery.internal.deltaBy(grid, 'a');
+  dump(dt);
+  WVPASSEQ(dt.data.length, 5);
+  WVPASSEQ(dt.data[0][0], 0);
+  WVPASSEQ(dt.data[0][1], 1);
+  WVPASSEQ(dt.data[1][0], 5);
+  WVPASSEQ(dt.data[1][1], 7);
+  WVPASSEQ(dt.data[2][0], 25);
+  WVPASSEQ(dt.data[2][1], 1);
+  WVPASSEQ(dt.data[3][0], 2);
+  WVPASSEQ(dt.data[3][1], 1);
+  WVPASSEQ(dt.data[4][0], undefined);
+  WVPASSEQ(dt.data[4][1], 1);
+});
+
+
+
 wvtest('guessTypes', function() {
   var data1 = [['1999-01-01', '1999-02-02', 1, 2.5, false, 'foo']];
   var data2 = [['1999-01-01', '1999-02-02 12:34', 2, 'x', true, null]];