sclass Scorer { double score, total; L successes, errors; // set to non-null if you want them filled bool verboseFailures, verboseAll; void addZeroToOne aka add(double score) { ++total; this.score += clamp(score, 0, 1); } void addZeroToOneError(double error) { addZeroToOne(1-error); } void addError { add(false); } void addError(A info) { add(false, info); } void error(A info) { addError(info); } void addOK { add(true); } void addOK(A info) { add(true, info); } void ok() { addOK(); } void ok(A info) { addOK(info); } bool add(bool correct) { ++total; if (correct) ++score; ret correct; } bool add(bool correct, A info) { main add(correct ? successes : errors, info); if (verboseAll || verboseFailures && !correct) _print((correct ? "[GOOD] " : "[BAD] ") + info); ret add(correct); } // works if you use Scorer or Scorer void eq(O a, O b) { if (_eq(a, b)) add(true); else add(false, (A) (a + " != " + b)); } void print() { main print(toString()); } toString { ret formatDouble(ratioToPercent(score, total), 1) + "% correct (n=" + formatDouble(total, 1) + ")"; } double get() { ret ratioToPercent(score, total); } double percentScore() { ret get(); } double score() { ret get(); } bool allCorrect() { ret score == total; } void add(Scorer scorer) { if (scorer == null) ret; total += scorer.total; score += scorer.score; addAll(successes, scorer.successes); addAll(errors, scorer.errors); } void collectErrors() { errors = new L; } void collectSuccesses() { successes = new L; } }