docs: function decisionStumpTrain(data, labels, ix, options) {
      
      options = options || {};
      var numtries = options.numTries || 10;
      
      // choose a dimension at random and pick a best split
      var ri = randi(0, data[0].length);
      var N = ix.length;
      
      // evaluate class entropy of incoming data
      var H = entropy(labels, ix);
      var bestGain = 0; 
      var bestThr = 0;
      for(var i=0;i<numtries;i++) {
      
          // pick a random splitting threshold
          var ix1 = ix[randi(0, N)];
          var ix2 = ix[randi(0, N)];
          while(ix2==ix1) ix2= ix[randi(0, N)]; // enforce distinctness of ix2
          
          var a= Math.random();
          var thr= data[ix1][ri]*a + data[ix2][ri]*(1-a);
          
          // measure information gain we'd get from split with thr
          var l1=1, r1=1, lm1=1, rm1=1; //counts for Left and label 1, right and label 1, left and minus 1, right and minus 1
          for(var j=0;j<ix.length;j++) {
              if(data[ix[j]][ri] < thr) {
                if(labels[ix[j]]==1) l1++;
                else lm1++;
              } else {
                if(labels[ix[j]]==1) r1++;
                else rm1++;
              }
          }
          var t = l1+lm1;  // normalize the counts to obtain probability estimates
          l1 = l1/t;
          lm1 = lm1/t;
          t = r1+rm1;
          r1 = r1/t;
          rm1= rm1/t;
          
          var LH = -l1*Math.log(l1) -lm1*Math.log(lm1); // left and right entropy
          var RH = -r1*Math.log(r1) -rm1*Math.log(rm1);
          
          var informationGain = H - LH - RH;
          //console.log("Considering split %f, entropy %f -> %f, %f. Gain %f", thr, H, LH, RH, informationGain);
          if(informationGain > bestGain || i === 0) {
              bestGain= informationGain;
              bestThr= thr;
          }
      }
      
      model= {};
      model.thr= bestThr;
      model.ri= ri;
      return model;
  }