class ClassificationEvaluationUtil implements Serializable {

    public static void updateAccurateBinaryMetricsSummary(Tuple3<Double, Boolean, Double> cur,
                                                          AccurateBinaryMetricsSummary binaryMetricsSummary,
                                                          long[] countValues,
                                                          double[] recordValues,
                                                          boolean first) {
        if(binaryMetricsSummary.total == 0) {
            recordValues[TPR] = countValues[TOTAL_TRUE] == 0 ? 1.0 : 1.0 * countValues[CUR_TRUE] / countValues[TOTAL_TRUE];
            recordValues[FPR] = countValues[TOTAL_FALSE] == 0 ? 1.0 : 1.0 * countValues[CUR_FALSE] / countValues[TOTAL_FALSE];
            recordValues[PRECISION] = countValues[CUR_TRUE] + countValues[CUR_FALSE] == 0 ? 1.0
                : 1.0 * countValues[CUR_TRUE] / (countValues[CUR_TRUE] + countValues[CUR_FALSE]);
            recordValues[POSITIVE_RATE] = 1.0 * (countValues[CUR_TRUE] + countValues[CUR_FALSE]) / (countValues[TOTAL_TRUE] + countValues[TOTAL_FALSE]);
        }

        if(!isMiddlePoint(cur)) {
            binaryMetricsSummary.total++;
            binaryMetricsSummary.logLoss += cur.f2;
            if (cur.f1) {
                countValues[CUR_TRUE]++;
            } else {
                countValues[CUR_FALSE]++;
            }
        }

        double threshold = cur.f0;
        double tpr = countValues[TOTAL_TRUE] == 0 ? 1.0 : 1.0 * countValues[CUR_TRUE] / countValues[TOTAL_TRUE];
        double fpr = countValues[TOTAL_FALSE] == 0 ? 1.0 : 1.0 * countValues[CUR_FALSE] / countValues[TOTAL_FALSE];
        double precision = countValues[CUR_TRUE] + countValues[CUR_FALSE] == 0 ? 1.0
            : 1.0 * countValues[CUR_TRUE] / (countValues[CUR_TRUE] + countValues[CUR_FALSE]);
        double positiveRate = 1.0 * (countValues[CUR_TRUE] + countValues[CUR_FALSE]) / (countValues[TOTAL_TRUE] + countValues[TOTAL_FALSE]);

        if(binaryMetricsSummary.total == 1 && first){
            recordValues[PRECISION] = precision;
            ConfusionMatrix confusionMatrix = new ConfusionMatrix(
                new long[][] {{0, 0}, {countValues[TOTAL_TRUE], countValues[TOTAL_FALSE]}});
            binaryMetricsSummary.metricsInfoList.add(Tuple2.of(1.0, confusionMatrix));
        }

        //binaryMetricsSummary.auc += ((fpr - recordValues[FPR]) * (tpr + recordValues[TPR]) / 2);
        binaryMetricsSummary.gini += ((positiveRate - recordValues[POSITIVE_RATE]) * (tpr + recordValues[TPR]) / 2);
        binaryMetricsSummary.prc += ((tpr - recordValues[TPR]) * (precision + recordValues[PRECISION]) / 2);
        binaryMetricsSummary.ks = Math.max(Math.abs(fpr - tpr), binaryMetricsSummary.ks);

        recordValues[TPR] = tpr;
        recordValues[FPR] = fpr;
        recordValues[PRECISION] = precision;
        recordValues[POSITIVE_RATE] = positiveRate;

        ConfusionMatrix confusionMatrix = new ConfusionMatrix(new long[][] {{countValues[CUR_TRUE], countValues[CUR_FALSE]},
            {countValues[TOTAL_TRUE] - countValues[CUR_TRUE], countValues[TOTAL_FALSE] - countValues[CUR_FALSE]}});

        //keep the middlePoint(p = 0.5), keep the first point(p = 1.0), then compare the threshold
        if(binaryMetricsSummary.metricsInfoList.isEmpty() || (binaryMetricsSummary.total == 1 && first) || isMiddlePoint(cur) ||
            Math.abs(threshold - binaryMetricsSummary.metricsInfoList.get(binaryMetricsSummary.metricsInfoList.size() - 1).f0)
                >= PROBABILITY_ERROR) {
            binaryMetricsSummary.metricsInfoList.add(Tuple2.of(threshold, confusionMatrix));
        }else{
            Tuple2<Double, ConfusionMatrix> metricsInfo = binaryMetricsSummary.metricsInfoList
                .get(binaryMetricsSummary.metricsInfoList.size() - 1);
            metricsInfo.f1 = confusionMatrix;
        }
    }

}