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]"