Added SpanClassFilter for filtering query results by using a class
operation (disjoint, differ, equal, include, intersect).
Change-Id: Iffa37806e4fbee6305013bdea63f6ba18f9806cb
diff --git a/src/main/java/de/ids_mannheim/korap/query/SpanClassFilterQuery.java b/src/main/java/de/ids_mannheim/korap/query/SpanClassFilterQuery.java
new file mode 100644
index 0000000..48790bc
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/SpanClassFilterQuery.java
@@ -0,0 +1,86 @@
+package de.ids_mannheim.korap.query;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.lucene.index.AtomicReaderContext;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermContext;
+import org.apache.lucene.search.spans.SpanQuery;
+import org.apache.lucene.search.spans.Spans;
+import org.apache.lucene.util.Bits;
+
+import de.ids_mannheim.korap.query.spans.ClassFilteredSpans;
+
+public class SpanClassFilterQuery extends SimpleSpanQuery {
+
+ public enum ClassOperation {
+ DISJOINT, INTERSECT, INCLUDE, EQUAL, DIFFER
+ }
+
+ private ClassOperation operation;
+ private byte classNum1, classNum2;
+
+ public SpanClassFilterQuery (SpanQuery sq, ClassOperation type,
+ int classNum1, int classNum2,
+ boolean collectPayloads) {
+ super(sq, collectPayloads);
+ this.operation = type;
+ this.classNum1 = (byte) classNum1;
+ this.classNum2 = (byte) classNum2;
+ }
+
+ @Override
+ public SimpleSpanQuery clone() {
+ return new SpanClassFilterQuery((SpanQuery) firstClause.clone(),
+ operation,
+ classNum1, classNum2, collectPayloads);
+ }
+
+ @Override
+ public Spans getSpans(AtomicReaderContext context, Bits acceptDocs,
+ Map<Term, TermContext> termContexts) throws IOException {
+ return new ClassFilteredSpans(this, context, acceptDocs,
+ termContexts);
+ }
+
+ @Override
+ public String toString(String field) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("spanClassFilter(");
+ sb.append(firstClause.toString());
+ sb.append(",");
+ sb.append(operation);
+ sb.append(",");
+ sb.append(classNum1);
+ sb.append(",");
+ sb.append(classNum2);
+ sb.append(")");
+ return sb.toString();
+ }
+
+ public ClassOperation getOperation() {
+ return operation;
+ }
+
+ public void setOperation(ClassOperation operation) {
+ this.operation = operation;
+ }
+
+ public byte getClassNum1() {
+ return classNum1;
+ }
+
+ public void setClassNum1(byte classNum1) {
+ this.classNum1 = classNum1;
+ }
+
+ public byte getClassNum2() {
+ return classNum2;
+ }
+
+ public void setClassNum2(byte classNum2) {
+ this.classNum2 = classNum2;
+ }
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/ClassFilteredSpans.java b/src/main/java/de/ids_mannheim/korap/query/spans/ClassFilteredSpans.java
new file mode 100644
index 0000000..bfd5f11
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/ClassFilteredSpans.java
@@ -0,0 +1,132 @@
+package de.ids_mannheim.korap.query.spans;
+
+import static de.ids_mannheim.korap.util.KrillByte.byte2int;
+
+import java.io.IOException;
+import java.util.BitSet;
+import java.util.Map;
+
+import org.apache.lucene.index.AtomicReaderContext;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermContext;
+import org.apache.lucene.util.Bits;
+
+import de.ids_mannheim.korap.query.SpanClassFilterQuery;
+import de.ids_mannheim.korap.query.SpanClassFilterQuery.ClassOperation;
+
+public class ClassFilteredSpans extends SimpleSpans {
+
+ private BitSet bitset1, bitset2;
+ private ClassOperation operation;
+ private byte classNum1, classNum2;
+
+ public ClassFilteredSpans (SpanClassFilterQuery query,
+ AtomicReaderContext context, Bits acceptDocs,
+ Map<Term, TermContext> termContexts) throws IOException {
+ super(query, context, acceptDocs, termContexts);
+ this.operation = query.getOperation();
+ this.classNum1 = query.getClassNum1();
+ this.classNum2 = query.getClassNum2();
+ hasMoreSpans = firstSpans.next();
+ }
+
+ @Override
+ public boolean next() throws IOException {
+ while (hasMoreSpans) {
+ matchPayload.clear();
+ bitset1 = null;
+ bitset2 = null;
+ if (isClassOperationValid()) {
+ this.matchStartPosition = firstSpans.start();
+ this.matchEndPosition = firstSpans.end();
+ this.matchDocNumber = firstSpans.doc();
+ this.matchPayload.addAll(firstSpans.getPayload());
+ hasMoreSpans = firstSpans.next();
+ return true;
+ }
+ hasMoreSpans = firstSpans.next();
+ }
+ return false;
+ }
+
+ private boolean isClassOperationValid() throws IOException {
+ setBitsets();
+ int cardinality = Math.max(bitset1.cardinality(), bitset2.cardinality());
+ bitset1.and(bitset2);
+ // System.out.println("cardinality:" + cardinality);
+ switch (operation) {
+ case DISJOINT:
+ if (bitset1.cardinality() == 0) return true;
+ break;
+ case EQUAL:
+ if (cardinality == bitset1.cardinality()) return true;
+ break;
+ case DIFFER:
+ if (cardinality == 0 || cardinality != bitset1.cardinality())
+ return true;
+ break;
+ case INCLUDE:
+ if (bitset1.cardinality() == bitset2.cardinality()) {
+ return true;
+ }
+ break;
+ case INTERSECT:
+ if (bitset1.cardinality() > 0) return true;
+ break;
+ }
+
+ return false;
+ }
+
+ private void setBitsets() throws IOException {
+ BitSet bs = new BitSet();
+ int start, end;
+ // System.out.println("------------------------");
+ for (byte[] payload : firstSpans.getPayload()) {
+ if (payload.length == 9){
+ start = byte2int(payload, 0) + 1;
+ end = byte2int(payload, 4) + 1;
+ if (payload[8] == classNum1) {
+ // System.out.println("bitset1 " + start + " " +
+ // end);
+ if (bitset1 == null) {
+ bitset1 = new BitSet();
+ bitset1.set(start, end);
+ }
+ else {
+ bs.set(start, end);
+ bitset1.or(bs);
+ }
+ // System.out.println(bitset1);
+ }
+ else if (payload[8] == classNum2) {
+ // System.out.println("#bitset2 " + start + " " +
+ // end);
+ if (bitset2 == null) {
+ bitset2 = new BitSet();
+ bitset2.set(start, end);
+ }
+ else {
+ bs.set(start, end);
+ bitset2.or(bs);
+ // System.out.println("OR #2");
+ }
+ // System.out.println(bitset2);
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public boolean skipTo(int target) throws IOException {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public long cost() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+}
diff --git a/src/test/java/de/ids_mannheim/korap/index/TestClassFilterIndex.java b/src/test/java/de/ids_mannheim/korap/index/TestClassFilterIndex.java
new file mode 100644
index 0000000..77ef45d
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/index/TestClassFilterIndex.java
@@ -0,0 +1,297 @@
+package de.ids_mannheim.korap.index;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import de.ids_mannheim.korap.KrillIndex;
+import de.ids_mannheim.korap.query.DistanceConstraint;
+import de.ids_mannheim.korap.query.SpanClassFilterQuery;
+import de.ids_mannheim.korap.query.SpanClassFilterQuery.ClassOperation;
+import de.ids_mannheim.korap.query.SpanClassQuery;
+import de.ids_mannheim.korap.query.SpanDistanceQuery;
+import de.ids_mannheim.korap.query.SpanElementQuery;
+import de.ids_mannheim.korap.response.Result;
+
+public class TestClassFilterIndex {
+
+ private KrillIndex ki;
+ private Result kr;
+
+ @Test
+ public void testInclude() throws IOException {
+ ki = new KrillIndex();
+ ki.addDoc(TestReferenceIndex.createFieldDoc0());
+ ki.commit();
+
+ SpanElementQuery seq1 = new SpanElementQuery("tokens", "np");
+ SpanElementQuery seq2 = new SpanElementQuery("tokens", "vp");
+ SpanClassQuery scq1 = new SpanClassQuery(seq1, (byte) 1);
+ SpanClassQuery scq2 = new SpanClassQuery(seq2, (byte) 2);
+ SpanDistanceQuery sdq = new SpanDistanceQuery(scq1, scq2,
+ new DistanceConstraint(0, 1, false, false), true);
+
+ SpanClassFilterQuery sq = new SpanClassFilterQuery(sdq,
+ ClassOperation.INCLUDE, 2, 1, true);
+
+ assertEquals(
+ sq.toString(),
+ "spanClassFilter(spanDistance({1: <tokens:np />}, {2: <tokens:vp />}, "
+ + "[(w[0:1], notOrdered, notExcluded)]),INCLUDE,2,1)");
+
+ kr = ki.search(sq, (short) 10);
+ // for (Match km : kr.getMatches()) {
+ // System.out.println(km.getStartPos() + "," + km.getEndPos()
+ // + " "
+ // + km.getSnippetBrackets());
+ // }
+ assertEquals(7, kr.getTotalResults());
+ assertEquals(1, kr.getMatch(0).getStartPos());
+ assertEquals(5, kr.getMatch(0).getEndPos());
+ assertEquals(
+ "Frankenstein, [{2:treat {1:my daughter} well}]. She is the one that saved ...",
+ kr.getMatch(0).getSnippetBrackets());
+ assertEquals(6, kr.getMatch(1).getStartPos());
+ assertEquals(18, kr.getMatch(1).getEndPos());
+ assertEquals(
+ "Frankenstein, treat my daughter well. She [{2:is {1:the one} that saved "
+ + "your master who you hold so dear}].", kr.getMatch(1)
+ .getSnippetBrackets());
+ assertEquals(
+ "Frankenstein, treat my daughter well. She [{2:is {1:the one that "
+ + "saved your master who you hold so dear}}].", kr
+ .getMatch(2).getSnippetBrackets());
+ assertEquals("Frankenstein, treat my daughter well. She [{2:is the one that "
+ + "saved {1:your master} who you hold so dear}].", kr
+ .getMatch(3).getSnippetBrackets());
+ assertEquals(
+ "Frankenstein, treat my daughter well. She [{2:is the one that saved your master who {1:you} hold so dear}].",
+ kr.getMatch(4).getSnippetBrackets());
+
+ }
+
+ @Test
+ public void testDisjoint() throws IOException {
+ ki = new KrillIndex();
+ ki.addDoc(TestReferenceIndex.createFieldDoc0());
+ ki.commit();
+
+ SpanElementQuery seq1 = new SpanElementQuery("tokens", "np");
+ SpanElementQuery seq2 = new SpanElementQuery("tokens", "vp");
+ SpanClassQuery scq1 = new SpanClassQuery(seq1, (byte) 1);
+ SpanClassQuery scq2 = new SpanClassQuery(seq2, (byte) 2);
+ SpanDistanceQuery sdq = new SpanDistanceQuery(scq1, scq2,
+ new DistanceConstraint(0, 1, false, false), true);
+
+ // kr = ki.search(sdq, (short) 10);
+ // for (Match km : kr.getMatches()) {
+ // System.out.println(km.getStartPos() + "," + km.getEndPos()
+ // + " "
+ // + km.getSnippetBrackets());
+ // }
+
+ SpanClassFilterQuery sq = new SpanClassFilterQuery(sdq,
+ ClassOperation.DISJOINT, 2, 1, true);
+
+ kr = ki.search(sq, (short) 10);
+ // for (Match km : kr.getMatches()) {
+ // System.out.println(km.getStartPos() + "," + km.getEndPos()
+ // + " "
+ // + km.getSnippetBrackets());
+ // }
+ assertEquals(0, kr.getMatch(0).getStartPos());
+ assertEquals(5, kr.getMatch(0).getEndPos());
+ assertEquals(
+ "[{1:Frankenstein}, {2:treat my daughter well}]. She is the one that saved ...",
+ kr.getMatch(0).getSnippetBrackets());
+ assertEquals(1, kr.getMatch(1).getStartPos());
+ assertEquals(6, kr.getMatch(1).getEndPos());
+ assertEquals(
+ "Frankenstein, [{2:treat my daughter well}. {1:She}] is the one that saved your ...",
+ kr.getMatch(1).getSnippetBrackets());
+
+ assertEquals(5, kr.getMatch(2).getStartPos());
+ assertEquals(18, kr.getMatch(2).getEndPos());
+ assertEquals(
+ "Frankenstein, treat my daughter well. [{1:She} {2:is the one that saved your master who you hold so dear}].",
+ kr.getMatch(2).getSnippetBrackets());
+ }
+
+ // Problem with SpanDistanceQuery - unordered distance spans,
+ // -> unsorted
+ @Test
+ public void testEqual() throws IOException {
+ ki = new KrillIndex();
+ ki.addDoc(TestReferenceIndex.createFieldDoc0());
+ ki.commit();
+
+ SpanElementQuery seq1 = new SpanElementQuery("tokens", "np");
+ SpanElementQuery seq2 = new SpanElementQuery("tokens", "prp");
+ SpanClassQuery scq1 = new SpanClassQuery(seq1, (byte) 1);
+ SpanClassQuery scq2 = new SpanClassQuery(seq2, (byte) 2);
+ SpanDistanceQuery sdq = new SpanDistanceQuery(scq1, scq2,
+ new DistanceConstraint(0, 1, false, false), true);
+
+ kr = ki.search(sdq, (short) 10);
+ assertEquals(6, kr.getTotalResults());
+
+ kr = ki.search(scq2, (short) 10);
+ // for (Match km : kr.getMatches()) {
+ // System.out.println(km.getStartPos() + "," + km.getEndPos()
+ // + " "
+ // + km.getSnippetBrackets());
+ // }
+
+ SpanClassFilterQuery sq = new SpanClassFilterQuery(sdq,
+ ClassOperation.EQUAL, 2, 1, true);
+
+ kr = ki.search(sq, (short) 10);
+ assertEquals(5, kr.getMatch(0).getStartPos());
+ assertEquals(6, kr.getMatch(0).getEndPos());
+ assertEquals(14, kr.getMatch(1).getStartPos());
+ assertEquals(15, kr.getMatch(1).getEndPos());
+
+ }
+
+ @Test
+ public void testDiffer() throws IOException {
+ ki = new KrillIndex();
+ ki.addDoc(TestReferenceIndex.createFieldDoc0());
+ ki.commit();
+
+ SpanElementQuery seq1 = new SpanElementQuery("tokens", "np");
+ SpanElementQuery seq2 = new SpanElementQuery("tokens", "prp");
+ SpanClassQuery scq1 = new SpanClassQuery(seq1, (byte) 1);
+ SpanClassQuery scq2 = new SpanClassQuery(seq2, (byte) 2);
+ SpanDistanceQuery sdq = new SpanDistanceQuery(scq1, scq2,
+ new DistanceConstraint(0, 2, false, false), true);
+
+ SpanClassFilterQuery sq = new SpanClassFilterQuery(sdq,
+ ClassOperation.DIFFER, 1, 2, true);
+ kr = ki.search(sq, (short) 20);
+ // for (Match km : kr.getMatches()) {
+ // System.out.println(km.getStartPos() + "," + km.getEndPos()
+ // + " "
+ // + km.getSnippetBrackets());
+ // }
+
+ assertEquals(9, kr.getTotalResults());
+ assertEquals(0, kr.getMatch(0).getStartPos());
+ assertEquals(3, kr.getMatch(0).getEndPos());
+ assertEquals(
+ "[{1:Frankenstein}, treat {2:my}] daughter well. She is the one ...",
+ kr.getMatch(0).getSnippetBrackets());
+
+ assertEquals(5, kr.getMatch(3).getStartPos());
+ assertEquals(9, kr.getMatch(3).getEndPos());
+ assertEquals(
+ "Frankenstein, treat my daughter well. [{2:She} is {1:the one}] that saved your master who you ...",
+ kr.getMatch(3).getSnippetBrackets());
+ // she is both prp and np
+ }
+
+
+ @Test
+ public void testIntersect() throws IOException {
+ ki = new KrillIndex();
+ ki.addDoc(TestReferenceIndex.createFieldDoc0());
+ ki.commit();
+
+ SpanElementQuery seq1 = new SpanElementQuery("tokens", "np");
+ SpanElementQuery seq2 = new SpanElementQuery("tokens", "vb");
+ SpanClassQuery scq = new SpanClassQuery(seq2, (byte) 3);
+ SpanDistanceQuery sdq = new SpanDistanceQuery(seq1, scq,
+ new DistanceConstraint(0, 1, false, false), true);
+ SpanClassQuery scq1 = new SpanClassQuery(sdq, (byte) 1);
+
+ SpanElementQuery seq3 = new SpanElementQuery("tokens", "prp");
+ SpanDistanceQuery sdq2 = new SpanDistanceQuery(seq3, seq2,
+ new DistanceConstraint(0, 1, false, false), true);
+ SpanClassQuery scq2 = new SpanClassQuery(sdq2, (byte) 2);
+
+ SpanDistanceQuery sdq3 = new SpanDistanceQuery(scq1, scq2,
+ new DistanceConstraint(0, 1, false, false), true);
+
+ SpanClassFilterQuery sq = new SpanClassFilterQuery(sdq3,
+ ClassOperation.INTERSECT, 1, 2, true);
+
+ assertEquals("spanClassFilter(spanDistance({1: spanDistance(<tokens:np />, " +
+ "{3: <tokens:vb />}, [(w[0:1], notOrdered, notExcluded)])}, " +
+ "{2: spanDistance(<tokens:prp />, <tokens:vb />, [(w[0:1], " +
+ "notOrdered, notExcluded)])}, [(w[0:1], notOrdered, notExcluded)]),INTERSECT,1,2)",
+ sq.toString());
+
+
+ kr = ki.search(sq, (short) 20);
+
+ // for (Match km : kr.getMatches()) {
+ // System.out.println(km.getStartPos() + "," + km.getEndPos()
+ // + " "
+ // + km.getSnippetBrackets());
+ // }
+
+ assertEquals(13, kr.getTotalResults());
+ assertEquals(0, kr.getMatch(0).getStartPos());
+ assertEquals(3, kr.getMatch(0).getEndPos());
+ assertEquals(
+ "[{1:Frankenstein, {2:{3:treat}}}{2: my}] daughter well. She is the one ...",
+ kr.getMatch(0).getSnippetBrackets());
+ assertEquals(1, kr.getMatch(1).getStartPos());
+ assertEquals(4, kr.getMatch(1).getEndPos());
+ assertEquals(
+ "Frankenstein, [{1:{2:{3:treat} my} daughter}] well. She is the one that ...",
+ kr.getMatch(1).getSnippetBrackets());
+ }
+
+
+ @Test
+ public void testMultipleSameClasses() throws IOException {
+
+ ki = new KrillIndex();
+ ki.addDoc(TestReferenceIndex.createFieldDoc0());
+ ki.commit();
+
+ SpanElementQuery seq1 = new SpanElementQuery("tokens", "nn");
+ SpanElementQuery seq = new SpanElementQuery("tokens", "prp");
+ SpanClassQuery scq1 = new SpanClassQuery(seq1, (byte) 1);
+ SpanClassQuery scq = new SpanClassQuery(seq, (byte) 1);
+
+ SpanDistanceQuery sdq = new SpanDistanceQuery(scq, scq1,
+ new DistanceConstraint(3, 5, false, false), true);
+
+ SpanElementQuery seq2 = new SpanElementQuery("tokens", "vp");
+ SpanClassQuery scq2 = new SpanClassQuery(seq2, (byte) 2);
+
+ SpanDistanceQuery sdq2 = new SpanDistanceQuery(sdq, scq2,
+ new DistanceConstraint(0, 1, false, false), true);
+
+ SpanClassFilterQuery sq = new SpanClassFilterQuery(sdq2,
+ ClassOperation.INCLUDE, 2, 1, true);
+
+ kr = ki.search(sdq2, (short) 20);
+ assertEquals(6, kr.getTotalResults());
+
+ // for (Match km : kr.getMatches()) {
+ // System.out.println(km.getStartPos() + "," + km.getEndPos()
+ // + " "
+ // + km.getSnippetBrackets());
+ // }
+
+ kr = ki.search(sq, (short) 20);
+
+ // for (Match km : kr.getMatches()) {
+ // System.out.println(km.getStartPos() + "," + km.getEndPos()
+ // + " "
+ // + km.getSnippetBrackets());
+ // }
+
+ assertEquals(6, kr.getMatch(0).getStartPos());
+ assertEquals(18, kr.getMatch(0).getEndPos());
+ assertEquals(
+ "Frankenstein, treat my daughter well. She [{2:is the {1:one} that saved {1:your} master who you hold so dear}].",
+ kr.getMatch(0).getSnippetBrackets());
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/de/ids_mannheim/korap/index/TestReferenceIndex.java b/src/test/java/de/ids_mannheim/korap/index/TestReferenceIndex.java
index 2a167a0..4a56a04 100644
--- a/src/test/java/de/ids_mannheim/korap/index/TestReferenceIndex.java
+++ b/src/test/java/de/ids_mannheim/korap/index/TestReferenceIndex.java
@@ -263,27 +263,27 @@
"tokens",
"Frankenstein, treat my daughter well. She is the one that saved your master who you hold so dear.",
- "[(0-12)s:Frankenstein|_0#0-12|<>:nn#0-12$<i>1<s>18|<>:s#0-37$<i>4<s>1|"
+ "[(0-12)s:Frankenstein|_0#0-12|<>:nn#0-12$<i>1<s>18|<>:s#0-37$<i>5<s>1|"
+ "<>:np#0-13$<i>1<s>2|"
+ "<:stanford/d:tag$<i>1<s>1<s>18<s>19]"
- + "[(14-19)s:treat|_1#14-19|<>:vb#14-19$<i>2<s>19|<>:vp#14-36$<i>4<s>3|"
+ + "[(14-19)s:treat|_1#14-19|<>:vb#14-19$<i>2<s>19|<>:vp#14-36$<i>5<s>3|"
+ ">:stanford/d:tag$<i>0<s>2<s>19<s>18|"
+ ">:stanford/d:tag$<i>3<s>3<s>19<s>21|"
+ ">:stanford/d:tag$<i>4<s>4<s>19<s>22]"
- + "[(20-22)s:my|_2#20-22|<>:prp#20-22$<i>3<s>20|<>:np#20-31$<i>3<s>4]"
+ + "[(20-22)s:my|_2#20-22|<>:prp#20-22$<i>3<s>20|<>:np#20-31$<i>4<s>4]"
+ "[(23-31)s:daughter|_3#23-31|<>:nn#23-31$<i>4<s>21|"
+ ">:stanford/d:tag$<i>2<s>5<s>21<s>20]"
- + "[(32-36)s:well|_4#32-36|<>:rb#32-36$<i>5<s>22|<>:advp#32-36$<i>4<s>5]"
+ + "[(32-36)s:well|_4#32-36|<>:rb#32-36$<i>5<s>22|<>:advp#32-36$<i>5<s>5]"
- + "[(38-41)s:She|_5#38-41|<>:prp#38-41$<i>6<s>23|<>:s#38-97$<i>17<s>6]"
+ + "[(38-41)s:She|_5#38-41|<>:prp#38-41$<i>6<s>23|<>:np#38-41$<i>6<s>36|<>:s#38-97$<i>18<s>6]"
- + "[(42-44)s:is|_6#42-44|<>:vb#42-44$<i>7<s>24|<>:vp#42-96$<i>17<s>7]"
+ + "[(42-44)s:is|_6#42-44|<>:vb#42-44$<i>7<s>24|<>:vp#42-96$<i>18<s>7]"
- + "[(45-48)s:the|_7#45-48|<>:dt#45-48$<i>8<s>25|<>:np#45-52$<i>8<s>8|<>:np#45-96$<i>17<s>9]"
+ + "[(45-48)s:the|_7#45-48|<>:dt#45-48$<i>8<s>25|<>:np#45-52$<i>9<s>8|<>:np#45-96$<i>18<s>9]"
+ "[(49-52)s:one|_8#49-52|<>:nn#49-52$<i>9<s>26|"
+ ">:stanford/d:tag$<i>5<s>6<s>26<s>23|"
@@ -291,29 +291,29 @@
+ ">:stanford/d:tag$<i>7<s>8<s>26<s>25|"
+ ">:stanford/d:tag$<i>10<s>9<s>26<s>28]"
- + "[(53-57)s:that|_9#53-57|<>:rp#53-57$<i>10<s>27|<>:sb#53-96$<i>17<s>10]"
+ + "[(53-57)s:that|_9#53-57|<>:rp#53-57$<i>10<s>27|<>:sb#53-96$<i>18<s>10]"
- + "[(58-63)s:saved|_10#58-63|<>:vb#58-63$<i>11<s>28|<>:s#58-96$<i>17<s>11|"
- + "<>:vp#58-96$<i>17<s>12|"
+ + "[(58-63)s:saved|_10#58-63|<>:vb#58-63$<i>11<s>28|<>:s#58-96$<i>18<s>11|"
+ + "<>:vp#58-96$<i>18<s>12|"
+ ">:stanford/d:tag$<i>9<s>10<s>28<s>27|"
+ ">:stanford/d:tag$<i>12<s>11<s>28<s>30|"
+ ">:stanford/d:tag$<i>15<s>12<s>28<s>33]"
- + "[(64-68)s:your|_11#64-68|<>:prp#64-68$<i>12<s>29|<>:np#64-75$<i>12<s>13]"
+ + "[(64-68)s:your|_11#64-68|<>:prp#64-68$<i>12<s>29|<>:np#64-75$<i>13<s>13]"
+ "[(69-75)s:master|_12#69-75|<>:nn#69-75$<i>13<s>30|"
+ ">:stanford/d:tag$<i>11<s>13<s>30<s>29]"
- + "[(76-79)s:who|_13#76-79|<>:rp#76-79$<i>14<s>31|<>:sb#76-96$<i>17<s>14]"
+ + "[(76-79)s:who|_13#76-79|<>:rp#76-79$<i>14<s>31|<>:sb#76-96$<i>18<s>14]"
- + "[(80-83)s:you|_14#80-83|<>:prp#80-83$<i>15<s>32|<>:s#80-96$<i>17<s>15]"
+ + "[(80-83)s:you|_14#80-83|<>:prp#80-83$<i>15<s>32|<>:np#80-83$<i>15<s>37|<>:s#80-96$<i>18<s>15]"
- + "[(84-88)s:hold|_15#84-88|<>:vb#84-88$<i>16<s>33|<>:vp#84-96$<i>17<s>16|"
+ + "[(84-88)s:hold|_15#84-88|<>:vb#84-88$<i>16<s>33|<>:vp#84-96$<i>18<s>16|"
+ ">:stanford/d:tag$<i>13<s>14<s>33<s>31|"
+ ">:stanford/d:tag$<i>14<s>15<s>33<s>32|"
+ ">:stanford/d:tag$<i>17<s>16<s>33<s>35]"
- + "[(89-91)s:so|_16#89-91|<>:rb#89-91$<i>17<s>341|<>:adjp#89-96$<i>17<s>17]"
+ + "[(89-91)s:so|_16#89-91|<>:rb#89-91$<i>17<s>341|<>:adjp#89-96$<i>18<s>17]"
+ "[(92-96)s:dear|_17#92-96|<>:jj#92-96$<i>18<s>35|"
+ ">:stanford/d:tag$<i>16<s>17<s>35<s>34]"