package com.bbn.bue.common.evaluation;

import com.bbn.bue.common.Inspector;
import com.bbn.bue.common.symbols.Symbol;

import com.google.common.annotations.Beta;
import com.google.common.io.CharSink;

import java.io.IOException;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Observes an {@link Alignment} between system items on the left and gold standard items on the
 * right. A system/gold item is assumed to be found if aligned.  Writes summary F-measure scores
 * based on its observations.
 *
 * At the moment this only writes confusion matrices but it will be extended to write more soon.
 *
 * Although this only implements {@code Inspector<Alignment<Object,Object>>}, it is safe to cast it
 * to inspect any alignment.
 */
@Beta
public final class AggregateBinaryFScoresInspector<KeyT, TestT>
    implements Inspector<Alignment<? extends KeyT, ? extends TestT>> {

  private final SummaryConfusionMatrices.Builder summaryConfusionMatrixB =
      SummaryConfusionMatrices.builder();
  private final CharSink outSink;
  private static final Symbol PRESENT = Symbol.from("Present");
  private static final Symbol ABSENT = Symbol.from("Absent");

  private AggregateBinaryFScoresInspector(final CharSink outSink) {
    this.outSink = checkNotNull(outSink);
  }

  public static <KeyT, TestT> AggregateBinaryFScoresInspector<KeyT, TestT> createOutputtingTo(
      CharSink outputSink) {
    return new AggregateBinaryFScoresInspector<KeyT, TestT>(outputSink);
  }

  @Override
  public void finish() throws IOException {
    final SummaryConfusionMatrix summaryConfusionMatrix = summaryConfusionMatrixB.build();
    outSink.write(SummaryConfusionMatrices.prettyPrint(summaryConfusionMatrix));
  }

  @Override
  public void inspect(final Alignment<? extends KeyT, ? extends TestT> alignment) {
    summaryConfusionMatrixB
        .accumulatePredictedGold(PRESENT, PRESENT, alignment.rightAligned().size());
    summaryConfusionMatrixB
        .accumulatePredictedGold(PRESENT, ABSENT, alignment.leftUnaligned().size());
    summaryConfusionMatrixB
        .accumulatePredictedGold(ABSENT, PRESENT, alignment.rightUnaligned().size());
  }
}


@@ -156,16 +156,64 @@
 s.Beta;%0A
+import com.google.common.collect.ImmutableList;%0A
 import c
@@ -1061,16 +1061,85 @@
 lder();%0A
+  private final ImmutableList.Builder%3CFMeasureCounts%3E countsBuilder;%0A
   privat
@@ -1397,24 +1397,69 @@
 l(outSink);%0A
+    countsBuilder = ImmutableList.builder();%0A
   %7D%0A%0A  publi
@@ -1800,16 +1800,119 @@
 uild();%0A
+    final FMeasureCounts summaryCounts = FMeasureCounts.combineToMicroFMeasure(countsBuilder.build());%0A
     outS
@@ -1981,16 +1981,62 @@
 nMatrix)
+%0A        + summaryCounts.compactPrettyString()
 );%0A  %7D%0A%0A
@@ -2127,24 +2127,218 @@
 lignment) %7B%0A
+    final int truePositives = alignment.rightAligned().size();%0A    final int falseNegatives = alignment.leftUnaligned().size();%0A    final int falsePositives = alignment.rightUnaligned().size();%0A
     summaryC
@@ -2408,39 +2408,21 @@
 NT, 
-alignment.rightAligned().size()
+truePositives
 );%0A 
@@ -2502,40 +2502,22 @@
 NT, 
+f
 al
-ignment.leftUnaligned().size()
+seNegatives
 );%0A 
@@ -2597,40 +2597,112 @@
 NT, 
+f
 al
-ignment.rightUnaligned().size(
+sePositives);%0A    countsBuilder.add(FMeasureCounts.from(truePositives, falsePositives, falseNegatives
 ));%0A

Make AggregateF actually produce an F-score.