// use default settings for maxSize, nu, r from state
        public static void MakeStep(IGbmState state)
        {
            var gbmState = state as GbmState;

            if (gbmState == null)
            {
                throw new ArgumentException("state");
            }

            MakeStep(gbmState, gbmState.maxSize, gbmState.nu, gbmState.r, gbmState.m);
        }
        // allow dynamic adaptation of maxSize, nu and r (even though this is not used)
        public static void MakeStep(IGbmState state, int maxSize, double nu, double r, double m)
        {
            var gbmState = state as GbmState;

            if (gbmState == null)
            {
                throw new ArgumentException("state");
            }

            var problemData  = gbmState.problemData;
            var lossFunction = gbmState.lossFunction;
            var yPred        = gbmState.pred;
            var yPredTest    = gbmState.predTest;
            var treeBuilder  = gbmState.treeBuilder;
            var y            = gbmState.y;
            var activeIdx    = gbmState.activeIdx;
            var pseudoRes    = gbmState.pseudoRes;
            var trainingRows = gbmState.trainingRows;
            var testRows     = gbmState.testRows;

            // copy output of gradient function to pre-allocated rim array (pseudo-residual per row and model)
            int rimIdx = 0;

            foreach (var g in lossFunction.GetLossGradient(y, yPred))
            {
                pseudoRes[rimIdx++] = g;
            }

            var tree = treeBuilder.CreateRegressionTreeForGradientBoosting(pseudoRes, yPred, maxSize, activeIdx, lossFunction, r, m);

            int i = 0;

            foreach (var pred in tree.GetEstimatedValues(problemData.Dataset, trainingRows))
            {
                yPred[i] = yPred[i] + nu * pred;
                i++;
            }
            // update predictions for validation set
            i = 0;
            foreach (var pred in tree.GetEstimatedValues(problemData.Dataset, testRows))
            {
                yPredTest[i] = yPredTest[i] + nu * pred;
                i++;
            }

            gbmState.AddModel(tree, nu);
        }
    // allow dynamic adaptation of maxSize, nu and r (even though this is not used)
    public static void MakeStep(IGbmState state, int maxSize, double nu, double r, double m) {
      var gbmState = state as GbmState;
      if (gbmState == null) throw new ArgumentException("state");

      var problemData = gbmState.problemData;
      var lossFunction = gbmState.lossFunction;
      var yPred = gbmState.pred;
      var yPredTest = gbmState.predTest;
      var treeBuilder = gbmState.treeBuilder;
      var y = gbmState.y;
      var activeIdx = gbmState.activeIdx;
      var pseudoRes = gbmState.pseudoRes;
      var trainingRows = gbmState.trainingRows;
      var testRows = gbmState.testRows;

      // copy output of gradient function to pre-allocated rim array (pseudo-residual per row and model)
      int rimIdx = 0;
      foreach (var g in lossFunction.GetLossGradient(y, yPred)) {
        pseudoRes[rimIdx++] = g;
      }

      var tree = treeBuilder.CreateRegressionTreeForGradientBoosting(pseudoRes, yPred, maxSize, activeIdx, lossFunction, r, m);

      int i = 0;
      foreach (var pred in tree.GetEstimatedValues(problemData.Dataset, trainingRows)) {
        yPred[i] = yPred[i] + nu * pred;
        i++;
      }
      // update predictions for validation set
      i = 0;
      foreach (var pred in tree.GetEstimatedValues(problemData.Dataset, testRows)) {
        yPredTest[i] = yPredTest[i] + nu * pred;
        i++;
      }

      gbmState.AddModel(tree, nu);
    }
    // use default settings for maxSize, nu, r from state
    public static void MakeStep(IGbmState state) {
      var gbmState = state as GbmState;
      if (gbmState == null) throw new ArgumentException("state");

      MakeStep(gbmState, gbmState.maxSize, gbmState.nu, gbmState.r, gbmState.m);
    }