package edu.stanford.nlp.util; /** * Represents a interval * * @author Angel Chang */ public class Interval<E extends Comparable<E>> extends Pair<E,E> implements HasInterval<E> { public static int INTERVAL_OPEN_BEGIN = 0x01; public static int INTERVAL_OPEN_END = 0x02; private int flags; public enum RelType { BEFORE, AFTER, EQUAL, BEGIN_MEET_END, END_MEET_BEGIN, CONTAIN, INSIDE, OVERLAP, UNKNOWN, NONE } protected final static int REL_FLAGS_SAME = 0x0001; protected final static int REL_FLAGS_BEFORE = 0x0002; protected final static int REL_FLAGS_AFTER = 0x0004; protected final static int REL_FLAGS_UNKNOWN = 0x0007; protected final static int REL_FLAGS_SS_SHIFT = 0; protected final static int REL_FLAGS_SE_SHIFT = 1*4; protected final static int REL_FLAGS_ES_SHIFT = 2*4; protected final static int REL_FLAGS_EE_SHIFT = 3*4; public final static int REL_FLAGS_SS_SAME = 0x0001; public final static int REL_FLAGS_SS_BEFORE = 0x0002; public final static int REL_FLAGS_SS_AFTER = 0x0004; public final static int REL_FLAGS_SS_UNKNOWN = 0x0007; public final static int REL_FLAGS_SE_SAME = 0x0010; public final static int REL_FLAGS_SE_BEFORE = 0x0020; public final static int REL_FLAGS_SE_AFTER = 0x0040; public final static int REL_FLAGS_SE_UNKNOWN = 0x0070; public final static int REL_FLAGS_ES_SAME = 0x0100; public final static int REL_FLAGS_ES_BEFORE = 0x0200; public final static int REL_FLAGS_ES_AFTER = 0x0400; public final static int REL_FLAGS_ES_UNKNOWN = 0x0700; public final static int REL_FLAGS_EE_SAME = 0x1000; public final static int REL_FLAGS_EE_BEFORE = 0x2000; public final static int REL_FLAGS_EE_AFTER = 0x4000; public final static int REL_FLAGS_EE_UNKNOWN = 0x7000; public final static int REL_FLAGS_INTERVAL_SAME = 0x00010000; // SS,EE SAME // Can be set with OVERLAP, INSIDE, CONTAIN public final static int REL_FLAGS_INTERVAL_BEFORE = 0x00020000; // ES BEFORE => SS, SE, EE BEFORE public final static int REL_FLAGS_INTERVAL_AFTER = 0x00040000; // SE AFTER => SS, ES, EE AFTER // flags can be set together along with SAME public final static int REL_FLAGS_INTERVAL_OVERLAP = 0x00100000; // SS SAME or AFTER, SE SAME or BEFORE // SS SAME or BEFORE, ES SAME or AFTER public final static int REL_FLAGS_INTERVAL_INSIDE = 0x00200000; // SS SAME or AFTER, EE SAME or BEFORE public final static int REL_FLAGS_INTERVAL_CONTAIN = 0x00400000; // SS SAME or BEFORE, EE SAME or AFTER public final static int REL_FLAGS_INTERVAL_UNKNOWN = 0x00770000; public final static int REL_FLAGS_INTERVAL_ALMOST_SAME = 0x01000000; public final static int REL_FLAGS_INTERVAL_ALMOST_BEFORE = 0x01000000; public final static int REL_FLAGS_INTERVAL_ALMOST_AFTER = 0x01000000; // public final static int REL_FLAGS_INTERVAL_ALMOST_OVERLAP = 0x10000000; // public final static int REL_FLAGS_INTERVAL_ALMOST_INSIDE = 0x20000000; // public final static int REL_FLAGS_INTERVAL_ALMOST_CONTAIN = 0x40000000; public final static int REL_FLAGS_INTERVAL_FUZZY = 0x80000000; protected Interval(E a, E b, int flags) { super(a,b); this.flags = flags; int comp = a.compareTo(b); if (comp > 0) { throw new IllegalArgumentException("Invalid interval: " + a + "," + b); } } public static <E extends Comparable<E>> Interval<E> toInterval(E a, E b) { return toInterval(a,b,0); } public static <E extends Comparable<E>> Interval<E> toInterval(E a, E b, int flags) { int comp = a.compareTo(b); if (comp <= 0) { return new Interval(a,b, flags); } else { return null; } } public static <E extends Comparable<E>> Interval<E> toValidInterval(E a, E b) { return toValidInterval(a,b,0); } public static <E extends Comparable<E>> Interval<E> toValidInterval(E a, E b, int flags) { int comp = a.compareTo(b); if (comp <= 0) { return new Interval(a,b,flags); } else { return new Interval(b,a,flags); } } public Interval<E> getInterval() { return this; } public E getBegin() { return first; } public E getEnd() { return second; } protected static <E extends Comparable<E>> E max(E a, E b) { int comp = a.compareTo(b); return (comp > 0)? a:b; } protected static <E extends Comparable<E>> E min(E a, E b) { int comp = a.compareTo(b); return (comp < 0)? a:b; } public boolean contains(E p) { return (first.compareTo(p) <= 0 && second.compareTo(p) >= 0); } // Returns (smallest) interval that contains both this and other public Interval expand(Interval<E> other) { if (other == null) return this; E a = min(this.first, other.first); E b = max(this.second, other.second); return toInterval(a,b); } // Returns interval that is the intersection of this and the other // Returns null if intersect is null public Interval intersect(Interval<E> other) { if (other == null) return null; E a = max(this.first, other.first); E b = min(this.second, other.second); return toInterval(a,b); } // Returns true if this interval overlaps the other // (i.e. the intersect would not be null public boolean overlaps(Interval<E> other) { if (other == null) return false; int comp12 = this.first.compareTo(other.second()); int comp21 = this.second.compareTo(other.first()); if (comp12 > 0 || comp21 < 0) { return false; } else { if (comp12 == 0) { if (!this.includesBegin() || !other.includesEnd()) { return false; } } if (comp21 == 0) { if (!this.includesEnd() || !other.includesBegin()) { return false; } } return true; } } public boolean includesBegin() { return ((flags & INTERVAL_OPEN_BEGIN) == 0); } public boolean includesEnd() { return ((flags & INTERVAL_OPEN_END) == 0); } /* // Returns true if end before (start of other) public boolean isEndBeforeBegin(Interval<E> other) { if (other == null) return false; int comp21 = this.second.compareTo(other.first()); return (comp21 < 0); } // Returns true if end before or eq (start of other) public boolean isEndBeforeEqBegin(Interval<E> other) { if (other == null) return false; int comp21 = this.second.compareTo(other.first()); return (comp21 <= 0); } // Returns true if end before or eq (start of other) public boolean isEndEqBegin(Interval<E> other) { if (other == null) return false; int comp21 = this.second.compareTo(other.first()); return (comp21 == 0); } // Returns true if start after (end of other) public boolean isBeginAfterEnd(Interval<E> other) { if (other == null) return false; int comp12 = this.first.compareTo(other.second()); return (comp12 > 0); } // Returns true if start eq(end of other) public boolean isBeginAfterEqEnd(Interval<E> other) { if (other == null) return false; int comp12 = this.first.compareTo(other.second()); return (comp12 >= 0); } // Returns true if start eq(end of other) public boolean isBeginEqEnd(Interval<E> other) { if (other == null) return false; int comp12 = this.first.compareTo(other.second()); return (comp12 >= 0); } // Returns true if start is the same public boolean isBeginSame(Interval<E> other) { if (other == null) return false; int comp11 = this.first.compareTo(other.first()); return (comp11 == 0); } // Returns true if end is the same public boolean isEndSame(Interval<E> other) { if (other == null) return false; int comp22 = this.second.compareTo(other.second()); return (comp22 == 0); } */ protected int toRelFlags(int comp, int shift) { int flags = 0; if (comp == 0) { flags = REL_FLAGS_SAME; } else if (comp > 0) { flags = REL_FLAGS_AFTER; } else { flags = REL_FLAGS_BEFORE; } flags = flags << shift; return flags; } public int getRelationFlags(Interval<E> other) { if (other == null) return 0; int flags = 0; int comp11 = this.first.compareTo(other.first()); // 3 choices flags |= toRelFlags(comp11, REL_FLAGS_SS_SHIFT); int comp22 = this.second.compareTo(other.second()); // 3 choices flags |= toRelFlags(comp22, REL_FLAGS_EE_SHIFT); int comp12 = this.first.compareTo(other.second()); // 3 choices flags |= toRelFlags(comp12, REL_FLAGS_SE_SHIFT); int comp21 = this.second.compareTo(other.first()); // 3 choices flags |= toRelFlags(comp21, REL_FLAGS_ES_SHIFT); flags = addIntervalRelationFlags(flags, false); return flags; } public int addIntervalRelationFlags(int flags, boolean checkFuzzy) { int f11 = extractRelationSubflags(flags, REL_FLAGS_SS_SHIFT); int f22 = extractRelationSubflags(flags, REL_FLAGS_EE_SHIFT); int f12 = extractRelationSubflags(flags, REL_FLAGS_SE_SHIFT); int f21 = extractRelationSubflags(flags, REL_FLAGS_ES_SHIFT); if (checkFuzzy) { boolean isFuzzy = checkMultipleBitSet(f11) || checkMultipleBitSet(f12) || checkMultipleBitSet(f21) || checkMultipleBitSet(f22); if (isFuzzy) { flags |= REL_FLAGS_INTERVAL_FUZZY; } } if (((f11 & REL_FLAGS_SAME) != 0) && ((f22 & REL_FLAGS_SAME) != 0)) { // SS,EE SAME flags |= REL_FLAGS_INTERVAL_SAME; // Possible } if (((f21 & REL_FLAGS_BEFORE) != 0)) { // ES BEFORE => SS, SE, EE BEFORE flags |= REL_FLAGS_INTERVAL_BEFORE; // Possible } if (((f12 & REL_FLAGS_AFTER) != 0)) { // SE AFTER => SS, ES, EE AFTER flags |= REL_FLAGS_INTERVAL_AFTER; // Possible } if (((f11 & (REL_FLAGS_SAME | REL_FLAGS_AFTER)) != 0) && ((f12 & (REL_FLAGS_SAME | REL_FLAGS_BEFORE)) != 0)) { // SS SAME or AFTER, SE SAME or BEFORE // |-----| // |------| flags |= REL_FLAGS_INTERVAL_OVERLAP; // Possible } if (((f11 & (REL_FLAGS_SAME | REL_FLAGS_BEFORE)) != 0) && ((f21 & (REL_FLAGS_SAME | REL_FLAGS_AFTER)) != 0)) { // SS SAME or BEFORE, ES SAME or AFTER // |------| // |-----| flags |= REL_FLAGS_INTERVAL_OVERLAP; // Possible } if (((f11 & (REL_FLAGS_SAME | REL_FLAGS_AFTER)) != 0) && ((f22 & (REL_FLAGS_SAME | REL_FLAGS_BEFORE)) != 0)) { // SS SAME or AFTER, EE SAME or BEFORE flags |= REL_FLAGS_INTERVAL_INSIDE; // Possible } if (((f11 & (REL_FLAGS_SAME | REL_FLAGS_BEFORE)) != 0) && ((f22 & (REL_FLAGS_SAME | REL_FLAGS_AFTER)) != 0)) { // SS SAME or BEFORE, EE SAME or AFTER flags |= REL_FLAGS_INTERVAL_CONTAIN; // Possible } return flags; } public static int extractRelationSubflags(int flags, int shift) { return (flags >> shift) & 0xf; } public static boolean checkMultipleBitSet(int flags) { boolean set = false; while (flags != 0) { if ((flags & 0x01) != 0) { if (set) { return false; } else { set = true; } } flags = flags >> 1; } return false; } public static boolean checkFlagSet(int flags, int flag) { return ((flags & flag) != 0); } public static boolean checkFlagExclusiveSet(int flags, int flag, int mask) { int f = flags & flag; if (f != 0) { return ((flags & mask & ~flag) != 0)? false:true; } else { return false; } } // Returns relation of this to other // EQUAL: this have same endpoints as other // OVERLAP: this and other overlaps // BEFORE: this ends before other starts // AFTER: this starts after other ends // BEGIN_MEET_END: this begin is the same as the others end // END_MEET_BEGIN: this end is the same as the others begin // CONTAIN: this contains the other // INSIDE: this is inside the other // TODO: Handle open/closed intervals? public RelType getRelation(Interval<E> other) { if (other == null) return RelType.NONE; int comp11 = this.first.compareTo(other.first()); // 3 choices int comp22 = this.second.compareTo(other.second()); // 3 choices if (comp11 == 0) { if (comp22 == 0) { // |---| this // |---| other return RelType.EQUAL; } if (comp22 < 0) { // SAME START - this finishes before other // |---| this // |------| other return RelType.INSIDE; } else { // SAME START - this finishes after other // |------| this // |---| other return RelType.CONTAIN; } } else if (comp22 == 0) { if (comp11 < 0) { // SAME FINISH - this start before other // |------| this // |---| other return RelType.CONTAIN; } else /*if (comp11 > 0) */ { // SAME FINISH - this starts after other // |---| this // |------| other return RelType.INSIDE; } } else if (comp11 > 0 && comp22 < 0) { // |---| this // |---------| other return RelType.INSIDE; } else if (comp11 < 0 && comp22 > 0) { // |---------| this // |---| other return RelType.CONTAIN; } else { int comp12 = this.first.compareTo(other.second()); int comp21 = this.second.compareTo(other.first()); if (comp12 > 0) { // |---| this // |---| other return RelType.AFTER; } else if (comp21 < 0) { // |---| this // |---| other return RelType.BEFORE; } else if (comp12 == 0) { // |---| this // |---| other return RelType.BEGIN_MEET_END; } else if (comp21 == 0) { // |---| this // |---| other return RelType.END_MEET_BEGIN; } else { return RelType.OVERLAP; } } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } if (!super.equals(o)) { return false; } Interval interval = (Interval) o; if (flags != interval.flags) { return false; } return true; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + flags; return result; } }