package com.bbn.bue.common.evaluation;

import com.bbn.bue.common.HasDocID;
import com.bbn.bue.common.strings.offsets.Offset;
import com.bbn.bue.common.strings.offsets.OffsetRange;
import com.bbn.bue.common.symbols.Symbol;
import com.google.common.annotations.Beta;
import com.google.common.base.Equivalence;
import com.google.common.base.Objects;

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

/**
 * A struct to wrap an {@link OffsetRange} alongside the corresponding {@link Symbol} type and
 * document id. The type is meant to represent the class label in a multi-class prediction tag,
 * such as a named entity type or a part of speech.
 */
@Beta
public final class ScoringTypedOffsetRange<T extends Offset & Comparable<T>>
    implements HasDocID, HasScoringType, HasOffsetRange {

  private final Symbol docId;
  private final Symbol type;
  private final OffsetRange<T> offsetRange;

  private ScoringTypedOffsetRange(final Symbol docId, final Symbol type, final OffsetRange<T> offsetRange) {
    this.docId = checkNotNull(docId);
    this.type = checkNotNull(type);
    this.offsetRange = checkNotNull(offsetRange);
  }

  public static <T extends Offset & Comparable<T>> ScoringTypedOffsetRange<T> create(final Symbol docId,
      final Symbol type, final OffsetRange<T> offsetRange) {
    return new ScoringTypedOffsetRange<T>(docId, type, offsetRange);
  }

  @Override
  public Symbol docID() {
    return docId;
  }

  @Override
  public Symbol scoringType() {
    return type;
  }

  @Override
  public OffsetRange<T> offsetRange() {
    return offsetRange;
  }

  @Override
  public int hashCode() {
    return Objects.hashCode(docId, type, offsetRange);
  }

  @Override
  public boolean equals(final Object obj) {
    if (this == obj) {
      return true;
    }
    if (obj == null || getClass() != obj.getClass()) {
      return false;
    }
    final ScoringTypedOffsetRange other = (ScoringTypedOffsetRange) obj;
    return Objects.equal(this.docId, other.docId)
        && Objects.equal(this.type, other.type)
        && Objects.equal(this.offsetRange, other.offsetRange);
  }

  @Override
  public String toString() {
    return docId + "-" + type + "-" + offsetRange;
  }

  /**
   * Returns an enum-wrapped {@link Equivalence} that uses only the document id and offset range
   * to compare {@link ScoringTypedOffsetRange}s.
   */
  public static DocIdOffsetEquivalence docIdOffsetEquivalence() {
    return new DocIdOffsetEquivalence();
  }

  /**
   * Equivalence that ignores the scoring type.
   */
  private static final class DocIdOffsetEquivalence extends Equivalence<ScoringTypedOffsetRange> {

    @Override
    protected boolean doEquivalent(ScoringTypedOffsetRange a, ScoringTypedOffsetRange b) {
      return Objects.equal(a.docID(), b.docID()) && Objects.equal(a.offsetRange(), b.offsetRange());
    }

    @Override
    protected int doHash(ScoringTypedOffsetRange scoringTypedOffsetRange) {
      return Objects.hashCode(scoringTypedOffsetRange.docID(), scoringTypedOffsetRange.offsetRange());
    }
  }
}

@@ -216,16 +216,17 @@
 Symbol;%0A
+%0A
 import c
@@ -333,21 +333,146 @@
 on.base.
-Objec
+Function;%0Aimport com.google.common.base.Objects;%0Aimport com.google.common.collect.Ordering;%0Aimport com.google.common.primitives.In
 ts;%0A%0Aimp
@@ -2603,24 +2603,1397 @@
 nce();%0A  %7D%0A%0A
+  public static Function%3CScoringTypedOffsetRange%3C?%3E, Symbol%3E typeFunction() %7B%0A    return TypeFunction.INSTANCE;%0A  %7D%0A%0A  /**%0A   * Provides an ordering of %7B@code ScoringTypedOffsetRange%7D by their degree of overlap with a%0A   * reference %7B@code ScoringTypedOffsetRange%7D, from least overlap to most.%0A   */%0A  public static %3COffsetType extends Offset & Comparable%3COffsetType%3E%3E Ordering%3CScoringTypedOffsetRange%3COffsetType%3E%3E%0A    orderByOverlapWith(final ScoringTypedOffsetRange%3COffsetType%3E reference) %7B%0A    return new Ordering%3CScoringTypedOffsetRange%3COffsetType%3E%3E() %7B%0A      @Override%0A      public int compare(final ScoringTypedOffsetRange%3COffsetType%3E left, final%0A      ScoringTypedOffsetRange%3COffsetType%3E right) %7B%0A        final int leftIntersectionSize =%0A            left.offsetRange().intersection(reference.offsetRange())%0A                .transform(OffsetRange.lengthFunction()).or(0);%0A        final int rightIntersectionSize = right.offsetRange().intersection(reference.offsetRange())%0A            .transform(OffsetRange.lengthFunction()).or(0);%0A%0A        return Ints.compare(leftIntersectionSize, rightIntersectionSize);%0A      %7D%0A    %7D;%0A  %7D%0A%0A  private enum TypeFunction implements Function%3CScoringTypedOffsetRange%3C?%3E, Symbol%3E %7B%0A    INSTANCE %7B%0A      @Override%0A      public Symbol apply(final ScoringTypedOffsetRange%3C?%3E input) %7B%0A        return input.scoringType();%0A      %7D%0A    %7D%0A  %7D%0A%0A
   /**%0A   * E

Order ScoringTypedOffsetRanges by overlap with another