docs: public double calcError(final Frame ftest, final Vec vactual,
                            final Frame fpreds, final Frame hitratio_fpreds,
                            final String label, final boolean printMe,
                            final int max_conf_mat_size, final water.api.ConfusionMatrix cm,
                            final AUC auc,
                            final HitRatio hr)
    {
      StringBuilder sb = new StringBuilder();
      double error = Double.POSITIVE_INFINITY;
      // populate AUC
      if (auc != null) {
        assert(isClassifier());
        assert(nclasses() == 2);
        auc.actual = ftest;
        auc.vactual = vactual;
        auc.predict = fpreds;
        auc.vpredict = fpreds.vecs()[2]; //binary classifier (label, prob0, prob1 (THIS ONE), adaptedlabel)
        auc.invoke();
        auc.toASCII(sb);
        error = auc.data().err(); //using optimal threshold for F1
      }
      // populate CM
      if (cm != null) {
        cm.actual = ftest;
        cm.vactual = vactual;
        cm.predict = fpreds;
        cm.vpredict = fpreds.vecs()[0]; // prediction (either label or regression target)
        cm.invoke();
        if (isClassifier()) {
          if (auc != null) {
            AUCData aucd = auc.data();
            //override the CM with the one computed by AUC (using optimal threshold)
            //Note: must still call invoke above to set the domains etc.
            cm.cm = new long[3][3]; // 1 extra layer for NaNs (not populated here, since AUC skips them)
            cm.cm[0][0] = aucd.cm()[0][0];
            cm.cm[1][0] = aucd.cm()[1][0];
            cm.cm[0][1] = aucd.cm()[0][1];
            cm.cm[1][1] = aucd.cm()[1][1];
            double cm_err = new hex.ConfusionMatrix(cm.cm).err();
            double auc_err = aucd.err();
            if (! (Double.isNaN(cm_err) && Double.isNaN(auc_err))) // NOTE: NaN != NaN
              assert(cm_err == auc_err); //check consistency with AUC-computed error
          } else {
            error = new hex.ConfusionMatrix(cm.cm).err(); //only set error if AUC didn't already set the error
          }
          if (cm.cm.length <= max_conf_mat_size+1) cm.toASCII(sb);
        } else {
          assert(auc == null);
          error = cm.mse;
          cm.toASCII(sb);
        }
      }
      // populate HitRatio
      if (hr != null) {
        assert(isClassifier());
        hr.actual = ftest;
        hr.vactual = vactual;
        hr.predict = hitratio_fpreds;
        hr.invoke();
        hr.toASCII(sb);
      }
      if (printMe && sb.length() > 0) {
        Log.info("Scoring on " + label + " data:");
        for (String s : sb.toString().split("\n")) Log.info(s);
      }
      return error;
    }