Added javadoc comments

diff --git a/src/main/java/de/ids_mannheim/korap/query/SpanExpansionQuery.java b/src/main/java/de/ids_mannheim/korap/query/SpanExpansionQuery.java
index 98dd204..2a0c80e 100644
--- a/src/main/java/de/ids_mannheim/korap/query/SpanExpansionQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/query/SpanExpansionQuery.java
@@ -18,7 +18,10 @@
  * SpanExpansionQuery makes a span longer by stretching out the start or the end
  * position of the span. The constraints of the expansion, such as how large the
  * expansion should be (min and max position) and the direction of the expansion
- * with respect to the original span, are specified in ExpansionConstraint.
+ * with respect to the original span, are specified in ExpansionConstraint. The
+ * direction is designated with the sign of a number, namely a negative number
+ * signifies the left direction, and a positive number (including 0) signifies
+ * the right direction.
  * 
  * <pre>
  * SpanTermQuery stq = new SpanTermQuery(new Term(&quot;tokens&quot;, &quot;s:lightning&quot;));
@@ -50,7 +53,26 @@
  * 
  * <pre>
  * 	[orth=the][orth!=lightning]         "the" must not be followed by "lightning" 
- * 	[pos!=ADJ]{1,2}[orth=lightning]	    one or two adjectives cannot precedes "lightning"
+ * 	[pos!=ADJ]{1,2}[orth=jacket]	    one or two adjectives cannot precedes "jacket"
+ * </pre>
+ * 
+ * The SpanExpansionQuery for the latter Poliqarp query with left direction from
+ * "jacket" example is:
+ * 
+ * <pre>
+ * SpanTermQuery notQuery = new SpanTermQuery(new Term(&quot;tokens&quot;, &quot;tt:p:/ADJ&quot;));
+ * SpanTermQuery stq = new SpanTermQuery(new Term(&quot;tokens&quot;, &quot;s:jacket&quot;));
+ * SpanExpansionQuery seq = new SpanExpansionQuery(stq, notQuery, 1, 2, -1, true);
+ * </pre>
+ * 
+ * Matches and non matches example:
+ * 
+ * <pre>
+ * [a jacket]                         match
+ * [such a jacket]                    non match, where such is an ADJ
+ * [leather jacket]                   non match
+ * [black leather jacket]             non match
+ * [large black leather jacket]       non match
  * </pre>
  * 
  * The positions of the expansion parts can be stored in payloads by using a
diff --git a/src/main/java/de/ids_mannheim/korap/query/SpanMultipleDistanceQuery.java b/src/main/java/de/ids_mannheim/korap/query/SpanMultipleDistanceQuery.java
index 5cc4d28..b8ab9fe 100644
--- a/src/main/java/de/ids_mannheim/korap/query/SpanMultipleDistanceQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/query/SpanMultipleDistanceQuery.java
@@ -14,108 +14,172 @@
 
 import de.ids_mannheim.korap.query.spans.MultipleDistanceSpans;
 
-/** Match two spans with respect to a list of distance constraints.
- * 	No repetition of constraints of the same type is allowed. For example,
- * 	there must only exactly one constraint for word/token-based distance.
- * 	
- * 	@author margaretha
+/**
+ * SpanMultipleDistanceQuery matches two spans with respect to a list of
+ * distance constraints. No repetition of constraints of the same unit type
+ * (e.g. word, sentence, paragraph) is allowed. For example, there must only
+ * exactly one constraint for word/token-based distance. A SpanDistanceQuery is
+ * created for each constraint.<br />
+ * <br />
+ * Examples:
+ * <ul>
+ * 
+ * <li>
+ * Search two terms x and y which are separated by minimum two and maximum three
+ * other words within the same sentence. The order of x and y does not matter.
+ * 
+ * <pre>
+ * List&lt;DistanceConstraint&gt; constraints = new ArrayList&lt;DistanceConstraint&gt;();
+ * constraints.add(new DistanceConstraint(2, 3, false, false));
+ * constraints.add(DistanceConstraint(new SpanElementQuery(&quot;tokens&quot;, &quot;s&quot;), 0, 0,
+ *         false, false));
+ * 
+ * SpanMultipleDistanceQuery mdq = SpanMultipleDistanceQuery(x, y, constraints,
+ *         false, true);
+ * </pre>
+ * 
+ * </li>
+ * 
+ * <li>
+ * Search term x which do <em>not</em> occur with term y in minimum two and
+ * maximum three other words and <em>not</em> in the same sentence. X must
+ * precede y.
+ * 
+ * <pre>
+ * List&lt;DistanceConstraint&gt; constraints = new ArrayList&lt;DistanceConstraint&gt;();
+ * constraints.add(new DistanceConstraint(2, 3, false, true));
+ * constraints.add(DistanceConstraint(new SpanElementQuery(&quot;tokens&quot;, &quot;s&quot;), 0, 0,
+ *         false, true));
+ * 
+ * SpanMultipleDistanceQuery mdq = SpanMultipleDistanceQuery(x, y, constraints,
+ *         true, true);
+ * </pre>
+ * 
+ * </li>
+ * </ul>
+ * 
+ * @author margaretha
  * */
-public class SpanMultipleDistanceQuery extends SimpleSpanQuery{
-	
-	private List<DistanceConstraint> constraints;  
-	private boolean isOrdered;
-	private String spanName;
-			
-	public SpanMultipleDistanceQuery(SpanQuery firstClause, SpanQuery secondClause,
-			List<DistanceConstraint> constraints, boolean isOrdered, 
-			boolean collectPayloads) {
-		super(firstClause, secondClause, collectPayloads);
-		this.constraints = constraints;
-		this.isOrdered = isOrdered;
-		spanName = "spanMultipleDistance";
-	}
+public class SpanMultipleDistanceQuery extends SimpleSpanQuery {
 
-	@Override
-	public SpanMultipleDistanceQuery clone() {
-		SpanMultipleDistanceQuery query = new SpanMultipleDistanceQuery(
-		    (SpanQuery) firstClause.clone(),
-		    (SpanQuery) secondClause.clone(),
-		    this.constraints,
-		    this.isOrdered,
-		    collectPayloads
-        );		
-		
-		query.setBoost(getBoost());
-		return query;
-	}
-	
-	@Override
-	public String toString(String field) {
-		StringBuilder sb = new StringBuilder();
-		sb.append(this.spanName);
-		sb.append("(");
-		sb.append(firstClause.toString(field));
-	    sb.append(", ");
-		sb.append(secondClause.toString(field));
-		sb.append(", ");
-		sb.append("[");
-		
-		DistanceConstraint c;
-		int size = constraints.size();
-		for (int i=0; i < size; i++){
-			c = constraints.get(i);
-			sb.append("(");
-			sb.append(c.getUnit());
-			sb.append("[");
-			sb.append(c.getMinDistance());
-			sb.append(":");
-			sb.append(c.getMaxDistance());
-			sb.append("], ");		
-			sb.append(c.isOrdered() ? "ordered, " : "notOrdered, ");
-			sb.append(c.isExclusion() ? "excluded)]" : "notExcluded)");			
-			if (i < size-1) sb.append(", ");
-		}		
-		sb.append("])");
-		sb.append(ToStringUtils.boost(getBoost()));
-    	return sb.toString();
+    private List<DistanceConstraint> constraints;
+    private boolean isOrdered;
+    private String spanName;
+
+    /**
+     * Constructs a SpanMultipleDistanceQuery for the two given SpanQueries.
+     * 
+     * @param firstClause the first SpanQuery
+     * @param secondClause the second SpanQuery
+     * @param constraints the list of distance constraints
+     * @param isOrdered a boolean representing the value <code>true</code>, if
+     *        the firstspans must occur before the secondspans, otherwise
+     *        <code>false</code>.
+     * @param collectPayloads a boolean flag representing the value
+     *        <code>true</code> if payloads are to be collected, otherwise
+     *        <code>false</code>.
+     */
+    public SpanMultipleDistanceQuery(SpanQuery firstClause,
+            SpanQuery secondClause, List<DistanceConstraint> constraints,
+            boolean isOrdered, boolean collectPayloads) {
+        super(firstClause, secondClause, collectPayloads);
+        this.constraints = constraints;
+        this.isOrdered = isOrdered;
+        spanName = "spanMultipleDistance";
     }
-	
-	/** Filter the span matches of each constraint, returning only the matches 
-	 * 	meeting all the constraints.
-	 * 	@return only the span matches meeting all the constraints.
-	 * */
-	@Override
-	public Spans getSpans(AtomicReaderContext context, Bits acceptDocs,
-			Map<Term, TermContext> termContexts) throws IOException {			
-		
-		SpanDistanceQuery sdq,sdq2;		
-		Spans ds,ds2;
-		MultipleDistanceSpans mds = null;
-		boolean exclusion;
-				
-		sdq = new SpanDistanceQuery(firstClause, secondClause, constraints.get(0), collectPayloads);
-		ds = sdq.getSpans(context, acceptDocs, termContexts);
-				
-		for (int i=1; i< constraints.size(); i++){
-			sdq2 = new SpanDistanceQuery(firstClause, secondClause, constraints.get(i), 
-					collectPayloads);
-			ds2 = sdq2.getSpans(context, acceptDocs, termContexts);			
-			
-			exclusion = sdq.isExclusion() && sdq2.isExclusion();
-			mds = new MultipleDistanceSpans(this, context, acceptDocs, 
-					termContexts, ds, ds2, isOrdered, exclusion); 
-			ds = mds;
-		}
-		
-		return mds;
-	}	
 
-	public List<DistanceConstraint> getConstraints() {
-		return constraints;
-	}
+    @Override
+    public SpanMultipleDistanceQuery clone() {
+        SpanMultipleDistanceQuery query = new SpanMultipleDistanceQuery(
+                (SpanQuery) firstClause.clone(),
+                (SpanQuery) secondClause.clone(), this.constraints,
+                this.isOrdered, collectPayloads);
 
-	public void setConstraints(List<DistanceConstraint> constraints) {
-		this.constraints = constraints;
-	}
-	
+        query.setBoost(getBoost());
+        return query;
+    }
+
+    @Override
+    public String toString(String field) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(this.spanName);
+        sb.append("(");
+        sb.append(firstClause.toString(field));
+        sb.append(", ");
+        sb.append(secondClause.toString(field));
+        sb.append(", ");
+        sb.append("[");
+
+        DistanceConstraint c;
+        int size = constraints.size();
+        for (int i = 0; i < size; i++) {
+            c = constraints.get(i);
+            sb.append("(");
+            sb.append(c.getUnit());
+            sb.append("[");
+            sb.append(c.getMinDistance());
+            sb.append(":");
+            sb.append(c.getMaxDistance());
+            sb.append("], ");
+            sb.append(c.isOrdered() ? "ordered, " : "notOrdered, ");
+            sb.append(c.isExclusion() ? "excluded)]" : "notExcluded)");
+            if (i < size - 1)
+                sb.append(", ");
+        }
+        sb.append("])");
+        sb.append(ToStringUtils.boost(getBoost()));
+        return sb.toString();
+    }
+
+    /**
+     * Filters the span matches of each constraint, returning only the matches
+     * meeting all the constraints.
+     * 
+     * @return only the span matches meeting all the constraints.
+     * */
+    @Override
+    public Spans getSpans(AtomicReaderContext context, Bits acceptDocs,
+            Map<Term, TermContext> termContexts) throws IOException {
+
+        SpanDistanceQuery sdq, sdq2;
+        Spans ds, ds2;
+        MultipleDistanceSpans mds = null;
+        boolean exclusion;
+
+        sdq = new SpanDistanceQuery(firstClause, secondClause,
+                constraints.get(0), collectPayloads);
+        ds = sdq.getSpans(context, acceptDocs, termContexts);
+
+        for (int i = 1; i < constraints.size(); i++) {
+            sdq2 = new SpanDistanceQuery(firstClause, secondClause,
+                    constraints.get(i), collectPayloads);
+            ds2 = sdq2.getSpans(context, acceptDocs, termContexts);
+
+            exclusion = sdq.isExclusion() && sdq2.isExclusion();
+            mds = new MultipleDistanceSpans(this, context, acceptDocs,
+                    termContexts, ds, ds2, isOrdered, exclusion);
+            ds = mds;
+        }
+
+        return mds;
+    }
+
+    /**
+     * Returns the list of distance constraints.
+     * 
+     * @return the list of distance constraints
+     */
+    public List<DistanceConstraint> getConstraints() {
+        return constraints;
+    }
+
+    /**
+     * Sets the list of distance constraints.
+     * 
+     * @param constraints the list of distance constraints
+     */
+    public void setConstraints(List<DistanceConstraint> constraints) {
+        this.constraints = constraints;
+    }
+
 }
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/ExpandedExclusionSpans.java b/src/main/java/de/ids_mannheim/korap/query/spans/ExpandedExclusionSpans.java
index 79c6db4..2f3857d 100644
--- a/src/main/java/de/ids_mannheim/korap/query/spans/ExpandedExclusionSpans.java
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/ExpandedExclusionSpans.java
@@ -14,236 +14,304 @@
 
 import de.ids_mannheim.korap.query.SpanExpansionQuery;
 
-/** Spans expanded with min m tokens and max n tokens, and throughout all 
- * 	the expansions do not contain the notClause. 
+/**
+ * Enumeration of Spans expanded with minimum <code>m</code> and maximum
+ * <code>n</code> tokens, and throughout all the expansions do <em>not</em>
+ * contain a specific Spans (notClause). See examples in
+ * {@link SpanExpansionQuery}.
  * 
- * 	@author margaretha
+ * The expansion direction is designated with the sign of a number, namely a
+ * negative number signifies the expansion to the <em>left</em> of the original
+ * span, and a positive number (including 0) signifies the expansion to the
+ * <em>right</em> of the original span.
+ * 
+ * @author margaretha
  * */
-public class ExpandedExclusionSpans extends SimpleSpans{
-	
-	private int min, max;
-	private int direction;
-	private byte classNumber;
-	private List<CandidateSpan> candidateSpans;
-	private boolean hasMoreNotClause;
-	private Spans notClause;
-	
-	private long matchCost;
-	
-	public ExpandedExclusionSpans(SpanExpansionQuery spanExpansionQuery,
-			AtomicReaderContext context, Bits acceptDocs,
-			Map<Term, TermContext> termContexts) throws IOException {
-		super(spanExpansionQuery, context, acceptDocs, termContexts);
-		
-		if (spanExpansionQuery.getSecondClause() == null){
-			throw new IllegalArgumentException("The SpanExpansionQuery " +
-				"is not valid. The spanquery to exclude (notClause) cannot " +
-				"be null.");
-		}
-		
-		/*if (spanExpansionQuery.getMin() < 1){
-			throw new IllegalArgumentException("Min occurrence for notClause " +
-				"must be at least 1.");
-		}*/
-		
-		this.min = spanExpansionQuery.getMin();
-		this.max = spanExpansionQuery.getMax();
-		this.direction = spanExpansionQuery.getDirection();
-		this.classNumber = spanExpansionQuery.getClassNumber();
-		
-		this.notClause = secondSpans;
-		this.hasMoreNotClause = notClause.next();		
-		
-		candidateSpans = new ArrayList<CandidateSpan>();		
-		hasMoreSpans = firstSpans.next();
-	}
+public class ExpandedExclusionSpans extends SimpleSpans {
 
-	@Override
-	public boolean next() throws IOException {
-		matchPayload.clear();
-		isStartEnumeration = false;
-		return advance();
-	}
+    private int min, max;
+    private int direction;
+    private byte classNumber;
+    private List<CandidateSpan> candidateSpans;
+    private boolean hasMoreNotClause;
+    private Spans notClause;
 
-	private boolean advance() throws IOException {
-		while (hasMoreSpans || candidateSpans.size() > 0){
-			if (candidateSpans.size() > 0){
-				// set a candidate span as a match
-				CandidateSpan cs = candidateSpans.get(0);
-				matchDocNumber = cs.getDoc();
-				matchStartPosition = cs.getStart();
-				matchEndPosition = cs.getEnd();
-				matchPayload = cs.getPayloads();
-				matchCost = cs.getCost() + notClause.cost();
-				candidateSpans.remove(0);
-				return true;
-			}
-			else if (!hasMoreNotClause || notClause.doc() > firstSpans.doc()){
-				generateCandidates(min, max, direction);
-				hasMoreSpans = firstSpans.next();				
-			}
-			else findMatches();
-		}
-		return false;
-	}
-	
-	private void findMatches() throws IOException {
-		while (hasMoreNotClause &&	notClause.doc() <= firstSpans.doc()){
-			if (notClause.doc() == firstSpans.doc()){ 
-				if (direction < 0 ){ // left
-					expandLeft();
-				} // right
-				else { 	expandRight(); }
-				break;
-			}
-			else if (!notClause.next()) hasMoreNotClause = false;
-		}
-	}
-	
-	private void expandLeft() throws IOException{
-		//int counter = max;
-		int maxPos = max;
-		CandidateSpan lastNotClause = null;
-		while (hasMoreNotClause && 
-				notClause.start() < firstSpans.start()){
-			
-			// between max and firstspan.start()
-			if (notClause.start() >= firstSpans.start() - maxPos){
-				maxPos = firstSpans.start() - notClause.start() -1;
-				lastNotClause = new CandidateSpan(notClause);
-				//counter--;
-			} 
-			if (!notClause.next()) hasMoreNotClause = false;
-		}									
-		
-		// if a notClause is between max and firstspan.start, 
-		// then maxPos = last NotClause pos -1
-		generateCandidates(min, maxPos, direction);
-		
-		if (lastNotClause != null)
-			while ((hasMoreSpans = firstSpans.next())
-					// the next notClause is not in between max and firstspan.start()
-					&& notClause.start() > firstSpans.start()
-					// the last notClause is in between max and firstspan.start()
-					&& lastNotClause.getStart() < firstSpans.start()								
-					&& lastNotClause.getStart() >= firstSpans.start() - max
-				){
-											
-				maxPos = firstSpans.start() - lastNotClause.getStart() -1;
-				generateCandidates(min, maxPos, direction);
-			}
-		else hasMoreSpans = firstSpans.next();
-	}
-	
-	private void expandRight() throws IOException{
-		int expansionEnd = firstSpans.end() + max;
-		int maxPos = max;
-		boolean isFound = false;
-		
-		CandidateSpan firstNotClause = null;
-		//System.out.println("main start:"+firstSpans.start());
-		while (hasMoreNotClause && notClause.start() < expansionEnd){
-			// between firstspan.end() and expansionEnd
-			if (!isFound && notClause.start() >= firstSpans.end()){							
-				maxPos = notClause.start() - firstSpans.end() -1;
-				firstNotClause = new CandidateSpan(notClause);
-				isFound = true;
-			}						
-			if (!notClause.next()) hasMoreNotClause = false;
-		}
-		// if a notClause is between firstSpan.end and max
-		// then maxPos = the first notClause pos -1 
-		generateCandidates(min, maxPos, direction);
-		
-		if (firstNotClause !=null){
-			while ((hasMoreSpans = firstSpans.next()) 
-					// in between
-					&& firstNotClause.getStart() < firstSpans.end() + max
-					&& firstNotClause.getStart() >= firstSpans.end())								
-				{
-				//System.out.println("first start:"+firstNotClause.getStart()+", main start:"+firstSpans.start());
-				maxPos = firstNotClause.getStart() - firstSpans.end() -1;
-				generateCandidates(min, maxPos, direction);
-			} 
-		}
-		else hasMoreSpans = firstSpans.next();
-	}
-	
-	private void generateCandidates(int minPos, int maxPos, int direction) 
-			throws IOException {
-		int counter;
-		int start, end;
-		CandidateSpan cs;
-		if (direction < 0 ) { // left
-			counter = maxPos;
-			while (counter >= min){
-				start = Math.max(0,firstSpans.start() - counter);
-				if (start > -1 ){					
-					end = firstSpans.end();
-					//System.out.println(start+","+end);
-					cs = new CandidateSpan(start, end, firstSpans.doc(),
-							firstSpans.cost(), 
-							calculatePayload(start,firstSpans.start())
-					);
-					candidateSpans.add(cs);
-				}
-				counter --;
-			}
-		}
-		else { // right
-			counter = minPos;
-			while(counter <= maxPos){
-				start = firstSpans.start();
-				end = firstSpans.end() + counter;
-				//System.out.println(start+","+end);
-				
-				cs = new CandidateSpan(start, end, firstSpans.doc(), 
-						firstSpans.cost(), 
-						calculatePayload(firstSpans.end(), end)
-				);				
-				candidateSpans.add(cs);
-				counter++;
-			}			
-		}
-	}
-	
-	private ArrayList<byte[]> calculatePayload(int start, int end)
-			throws IOException{
-		
-		ArrayList<byte[]> payload = new ArrayList<byte[]>();
-									
-		if (firstSpans.isPayloadAvailable()){				
-			payload.addAll(firstSpans.getPayload());
-		}
-		if (classNumber > 0 ){	
-			//System.out.println("Extension offsets "+start+","+end);
-			payload.add(calculateExtensionOffsets(start, end));
-		}
-		return payload;
-	}
-	
-	private byte[] calculateExtensionOffsets(int start, int end) {
-		ByteBuffer buffer = ByteBuffer.allocate(9);
-		buffer.putInt(start);					
-		buffer.putInt(end);	
-		buffer.put(classNumber);
-		return buffer.array();
-	}
-	
-	@Override
-	public boolean skipTo(int target) throws IOException {
-		if (hasMoreSpans && (firstSpans.doc() < target)){
-  			if (!firstSpans.skipTo(target)){
-  				hasMoreSpans = false;
-  				return false;
-  			}
-  		} 		
-  		matchPayload.clear();
-  		return advance();
-	}
+    private long matchCost;
 
-	@Override
-	public long cost() {
-		return matchCost;
-	}
+    /**
+     * Constructs ExpandedExclusionSpans from the given
+     * {@link SpanExpansionQuery}.
+     * 
+     * @param spanExpansionQuery a SpanExpansionQuery
+     * @param context
+     * @param acceptDocs
+     * @param termContexts
+     * @throws IOException
+     */
+    public ExpandedExclusionSpans(SpanExpansionQuery spanExpansionQuery,
+            AtomicReaderContext context, Bits acceptDocs,
+            Map<Term, TermContext> termContexts) throws IOException {
+        super(spanExpansionQuery, context, acceptDocs, termContexts);
+
+        if (spanExpansionQuery.getSecondClause() == null) {
+            throw new IllegalArgumentException(
+                    "The SpanExpansionQuery "
+                            + "is not valid. The spanquery to exclude (notClause) cannot "
+                            + "be null.");
+        }
+
+        /*
+         * if (spanExpansionQuery.getMin() < 1){ throw new
+         * IllegalArgumentException("Min occurrence for notClause " +
+         * "must be at least 1."); }
+         */
+
+        this.min = spanExpansionQuery.getMin();
+        this.max = spanExpansionQuery.getMax();
+        this.direction = spanExpansionQuery.getDirection();
+        this.classNumber = spanExpansionQuery.getClassNumber();
+
+        this.notClause = secondSpans;
+        this.hasMoreNotClause = notClause.next();
+
+        candidateSpans = new ArrayList<CandidateSpan>();
+        hasMoreSpans = firstSpans.next();
+    }
+
+    @Override
+    public boolean next() throws IOException {
+        matchPayload.clear();
+        isStartEnumeration = false;
+        return advance();
+    }
+
+    /**
+     * Advances the ExpandedExclusionSpans to the next match.
+     * 
+     * @return <code>true</code> if a match is found, <code>false</code>
+     *         otherwise.
+     * @throws IOException
+     */
+    private boolean advance() throws IOException {
+        while (hasMoreSpans || candidateSpans.size() > 0) {
+            if (candidateSpans.size() > 0) {
+                // set a candidate span as a match
+                CandidateSpan cs = candidateSpans.get(0);
+                matchDocNumber = cs.getDoc();
+                matchStartPosition = cs.getStart();
+                matchEndPosition = cs.getEnd();
+                matchPayload = cs.getPayloads();
+                matchCost = cs.getCost() + notClause.cost();
+                candidateSpans.remove(0);
+                return true;
+            } else if (!hasMoreNotClause || notClause.doc() > firstSpans.doc()) {
+                generateCandidates(min, max, direction);
+                hasMoreSpans = firstSpans.next();
+            } else
+                findMatches();
+        }
+        return false;
+    }
+
+    /**
+     * Finds matches by expanding the firstspans either to the left or to the
+     * right.
+     * 
+     * @throws IOException
+     */
+    private void findMatches() throws IOException {
+        while (hasMoreNotClause && notClause.doc() <= firstSpans.doc()) {
+            if (notClause.doc() == firstSpans.doc()) {
+                if (direction < 0) { // left
+                    expandLeft();
+                } // right
+                else {
+                    expandRight();
+                }
+                break;
+            } else if (!notClause.next())
+                hasMoreNotClause = false;
+        }
+    }
+
+    /**
+     * Expands the firstspans to the left.
+     * 
+     * @throws IOException
+     */
+    private void expandLeft() throws IOException {
+        //int counter = max;
+        int maxPos = max;
+        CandidateSpan lastNotClause = null;
+        while (hasMoreNotClause && notClause.start() < firstSpans.start()) {
+
+            // between max and firstspan.start()
+            if (notClause.start() >= firstSpans.start() - maxPos) {
+                maxPos = firstSpans.start() - notClause.start() - 1;
+                lastNotClause = new CandidateSpan(notClause);
+                //counter--;
+            }
+            if (!notClause.next())
+                hasMoreNotClause = false;
+        }
+
+        // if a notClause is between max and firstspan.start, 
+        // then maxPos = last NotClause pos -1
+        generateCandidates(min, maxPos, direction);
+
+        if (lastNotClause != null)
+            while ((hasMoreSpans = firstSpans.next())
+                    // the next notClause is not in between max and firstspan.start()
+                    && notClause.start() > firstSpans.start()
+                    // the last notClause is in between max and firstspan.start()
+                    && lastNotClause.getStart() < firstSpans.start()
+                    && lastNotClause.getStart() >= firstSpans.start() - max) {
+
+                maxPos = firstSpans.start() - lastNotClause.getStart() - 1;
+                generateCandidates(min, maxPos, direction);
+            }
+        else
+            hasMoreSpans = firstSpans.next();
+    }
+
+    /**
+     * Expands the firstspans to the right.
+     * 
+     * @throws IOException
+     */
+    private void expandRight() throws IOException {
+        int expansionEnd = firstSpans.end() + max;
+        int maxPos = max;
+        boolean isFound = false;
+
+        CandidateSpan firstNotClause = null;
+        //System.out.println("main start:"+firstSpans.start());
+        while (hasMoreNotClause && notClause.start() < expansionEnd) {
+            // between firstspan.end() and expansionEnd
+            if (!isFound && notClause.start() >= firstSpans.end()) {
+                maxPos = notClause.start() - firstSpans.end() - 1;
+                firstNotClause = new CandidateSpan(notClause);
+                isFound = true;
+            }
+            if (!notClause.next())
+                hasMoreNotClause = false;
+        }
+        // if a notClause is between firstSpan.end and max
+        // then maxPos = the first notClause pos -1 
+        generateCandidates(min, maxPos, direction);
+
+        if (firstNotClause != null) {
+            while ((hasMoreSpans = firstSpans.next())
+                    // in between
+                    && firstNotClause.getStart() < firstSpans.end() + max
+                    && firstNotClause.getStart() >= firstSpans.end()) {
+                //System.out.println("first start:"+firstNotClause.getStart()+", main start:"+firstSpans.start());
+                maxPos = firstNotClause.getStart() - firstSpans.end() - 1;
+                generateCandidates(min, maxPos, direction);
+            }
+        } else
+            hasMoreSpans = firstSpans.next();
+    }
+
+    /**
+     * Creates new candidate matches for the given direction, minimum and
+     * maximum positions.
+     * 
+     * @param minPos minimum position
+     * @param maxPos maximum position
+     * @param direction the expansion direction
+     * @throws IOException
+     */
+    private void generateCandidates(int minPos, int maxPos, int direction)
+            throws IOException {
+        int counter;
+        int start, end;
+        CandidateSpan cs;
+        if (direction < 0) { // left
+            counter = maxPos;
+            while (counter >= min) {
+                start = Math.max(0, firstSpans.start() - counter);
+                if (start > -1) {
+                    end = firstSpans.end();
+                    //System.out.println(start+","+end);
+                    cs = new CandidateSpan(start, end, firstSpans.doc(),
+                            firstSpans.cost(), createPayloads(start,
+                                    firstSpans.start()));
+                    candidateSpans.add(cs);
+                }
+                counter--;
+            }
+        } else { // right
+            counter = minPos;
+            while (counter <= maxPos) {
+                start = firstSpans.start();
+                end = firstSpans.end() + counter;
+                //System.out.println(start+","+end);
+
+                cs = new CandidateSpan(start, end, firstSpans.doc(),
+                        firstSpans.cost(), createPayloads(firstSpans.end(),
+                                end));
+                candidateSpans.add(cs);
+                counter++;
+            }
+        }
+    }
+
+    /**
+     * Creates payloads for a candiate match by copying the payloads of the
+     * firstspans, and adds expansion offsets with the given start and end
+     * positions to the payloads, if the class number is set.
+     * 
+     * @param start the start offset
+     * @param end the end offset
+     * @return payloads
+     * @throws IOException
+     */
+    private ArrayList<byte[]> createPayloads(int start, int end)
+            throws IOException {
+
+        ArrayList<byte[]> payload = new ArrayList<byte[]>();
+
+        if (firstSpans.isPayloadAvailable()) {
+            payload.addAll(firstSpans.getPayload());
+        }
+        if (classNumber > 0) {
+            //System.out.println("Extension offsets "+start+","+end);
+            payload.add(createExtensionPayloads(start, end));
+        }
+        return payload;
+    }
+
+    /**
+     * Generates a byte array of extension offsets and class number to be added
+     * into the payloads.
+     * 
+     * @param start the start offset
+     * @param end the end offset
+     * @return a byte array of extension offsets and class number
+     */
+    private byte[] createExtensionPayloads(int start, int end) {
+        ByteBuffer buffer = ByteBuffer.allocate(9);
+        buffer.putInt(start);
+        buffer.putInt(end);
+        buffer.put(classNumber);
+        return buffer.array();
+    }
+
+    @Override
+    public boolean skipTo(int target) throws IOException {
+        if (hasMoreSpans && (firstSpans.doc() < target)) {
+            if (!firstSpans.skipTo(target)) {
+                hasMoreSpans = false;
+                return false;
+            }
+        }
+        matchPayload.clear();
+        return advance();
+    }
+
+    @Override
+    public long cost() {
+        return matchCost;
+    }
 }
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/ExpandedSpans.java b/src/main/java/de/ids_mannheim/korap/query/spans/ExpandedSpans.java
index 81032a6..4e4f551 100644
--- a/src/main/java/de/ids_mannheim/korap/query/spans/ExpandedSpans.java
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/ExpandedSpans.java
@@ -13,134 +13,178 @@
 
 import de.ids_mannheim.korap.query.SpanExpansionQuery;
 
-/** Spans expanded with min m tokens and max n tokens.
+/**
+ * Enumeration of spans expanded with minimum <code>m</code> and maximum
+ * <code>n</code> token positions to either left or right direction from the
+ * original spans. See examples in {@link SpanExpansionQuery}.
+ * 
  * @author margaretha
  * */
-public class ExpandedSpans extends SimpleSpans{
-	
-	private int min, max;
-	private byte classNumber;
-	private int direction;	
-	private List<CandidateSpan> candidateSpans;
-	private long matchCost;
-	
-	public ExpandedSpans(SpanExpansionQuery spanExpansionQuery,
-			AtomicReaderContext context, Bits acceptDocs,
-			Map<Term, TermContext> termContexts) throws IOException {
-		super(spanExpansionQuery, context, acceptDocs, termContexts);
-		this.min = spanExpansionQuery.getMin();
-		this.max = spanExpansionQuery.getMax();
-		this.direction = spanExpansionQuery.getDirection();
-		this.classNumber = spanExpansionQuery.getClassNumber();
-		
-		candidateSpans = new ArrayList<CandidateSpan>();		
-		hasMoreSpans = true;		
-	}
+public class ExpandedSpans extends SimpleSpans {
 
-	@Override
-	public boolean next() throws IOException {
-		matchPayload.clear();
-		isStartEnumeration = false;
-		if (candidateSpans.size() == 0 && hasMoreSpans)
-			hasMoreSpans = firstSpans.next();
-		return advance();
-	}
+    private int min, max;
+    private byte classNumber;
+    private int direction;
+    private List<CandidateSpan> candidateSpans;
+    private long matchCost;
 
-	private boolean advance() throws IOException {		
-		while (candidateSpans.size() > 0 || hasMoreSpans) {
-			if (candidateSpans.size() > 0 ){
-				setMatch(candidateSpans.get(0));
-				candidateSpans.remove(0);								
-				return true;
-			}
-			else { setCandidateList(); }
-		}		
-		return false;
-	}
-	
-	private void setCandidateList() throws IOException {
-		CandidateSpan cs;
-		int counter, start, end;
-		
-		if (direction < 0 ){
-			counter = max;
-			while (counter >= min ){
-				start =  Math.max(0,firstSpans.start() - counter);			
-				cs = new CandidateSpan(
-						start, 
-						firstSpans.end(), 
-						firstSpans.doc(), 
-						firstSpans.cost(), 
-						calculatePayload(start, firstSpans.start())
-				);
-								
-				candidateSpans.add(cs);
-				counter--;
-			}
-		}
-		else{
-			counter = min;
-			while (counter <= max){
-				// TODO: How do I know if the end is already too far (over the end of the doc)? 
-				end = firstSpans.end() + counter;
-				cs = new CandidateSpan(
-						firstSpans.start(), 
-						end, 
-						firstSpans.doc(), 
-						firstSpans.cost(), 
-						calculatePayload(firstSpans.end(), end)
-				);
-				candidateSpans.add(cs);
-				counter++;
-			}
-		}	
-	}
-	
-	private ArrayList<byte[]> calculatePayload(int start, int end)
-			throws IOException{
-		
-		ArrayList<byte[]> payload = new ArrayList<byte[]>();
-		if (firstSpans.isPayloadAvailable()){				
-			payload.addAll(firstSpans.getPayload());
-		}
-		if (classNumber > 0 ){	
-			//System.out.println("Extension offsets "+start+","+end);
-			payload.add(calculateExtensionOffsets(start, end));
-		}
-		return payload;
-	}
-	
-	private byte[] calculateExtensionOffsets(int start, int end) {
-		ByteBuffer buffer = ByteBuffer.allocate(9);		
-		buffer.putInt(start);
-		buffer.putInt(end);
-		buffer.put(classNumber);
-		return buffer.array();
-	}
+    /**
+     * Constructs ExpandedSpans from the given {@link SpanExpansionQuery}.
+     * 
+     * @param spanExpansionQuery a SpanExpansionQuery
+     * @param context
+     * @param acceptDocs
+     * @param termContexts
+     * @throws IOException
+     */
+    public ExpandedSpans(SpanExpansionQuery spanExpansionQuery,
+            AtomicReaderContext context, Bits acceptDocs,
+            Map<Term, TermContext> termContexts) throws IOException {
+        super(spanExpansionQuery, context, acceptDocs, termContexts);
+        this.min = spanExpansionQuery.getMin();
+        this.max = spanExpansionQuery.getMax();
+        this.direction = spanExpansionQuery.getDirection();
+        this.classNumber = spanExpansionQuery.getClassNumber();
 
-	private void setMatch(CandidateSpan candidateSpan) {
-		matchDocNumber = candidateSpan.getDoc();
-		matchStartPosition = candidateSpan.getStart();
-		matchEndPosition = candidateSpan.getEnd();			
-		matchPayload = candidateSpan.getPayloads();
-		matchCost = candidateSpan.getCost();
-	}
+        candidateSpans = new ArrayList<CandidateSpan>();
+        hasMoreSpans = true;
+    }
 
-	@Override
-	public boolean skipTo(int target) throws IOException {
-		if (hasMoreSpans && (firstSpans.doc() < target)){
-  			if (!firstSpans.skipTo(target)){
-  				hasMoreSpans = false;
-  				return false;
-  			}
-  		} 		
-  		matchPayload.clear();
-  		return advance();
-	}
+    @Override
+    public boolean next() throws IOException {
+        matchPayload.clear();
+        isStartEnumeration = false;
+        if (candidateSpans.size() == 0 && hasMoreSpans)
+            hasMoreSpans = firstSpans.next();
+        return advance();
+    }
 
-	@Override
-	public long cost() {
-		return matchCost;
-	}
+    /**
+     * Advances the ExpandedSpans to the next match by setting the first element
+     * in the candidateList as the match. Set the candidateList, if it is empty
+     * 
+     * @return <code>true</code> if a match is found, <code>false</code>
+     *         otherwise.
+     * @throws IOException
+     */
+    private boolean advance() throws IOException {
+        while (candidateSpans.size() > 0 || hasMoreSpans) {
+            if (candidateSpans.size() > 0) {
+                setMatch(candidateSpans.get(0));
+                candidateSpans.remove(0);
+                return true;
+            } else {
+                setCandidateList();
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Sets the candidateList by adding new candidate match spans for all
+     * possible expansion with respect to the expansion length (min,max)
+     * variables.
+     * 
+     * @throws IOException
+     */
+    private void setCandidateList() throws IOException {
+        CandidateSpan cs;
+        int counter, start, end;
+
+        if (direction < 0) {
+            counter = max;
+            while (counter >= min) {
+                start = Math.max(0, firstSpans.start() - counter);
+                cs = new CandidateSpan(start, firstSpans.end(),
+                        firstSpans.doc(), firstSpans.cost(), createPayloads(
+                                start, firstSpans.start()));
+
+                candidateSpans.add(cs);
+                counter--;
+            }
+        } else {
+            counter = min;
+            while (counter <= max) {
+                // TODO: How do I know if the end is already too far (over the end of the doc)? 
+                end = firstSpans.end() + counter;
+                cs = new CandidateSpan(firstSpans.start(), end,
+                        firstSpans.doc(), firstSpans.cost(), createPayloads(
+                                firstSpans.end(), end));
+                candidateSpans.add(cs);
+                counter++;
+            }
+        }
+    }
+
+    /**
+     * Prepares the payloads for a candidate match (ExpandedSpans). If the class
+     * number is set, the extension offsets with the given start and end
+     * positions are to be stored in the payloads.
+     * 
+     * @param start
+     * @param end
+     * @return the payloads for a candidaete match
+     * @throws IOException
+     */
+    private ArrayList<byte[]> createPayloads(int start, int end)
+            throws IOException {
+
+        ArrayList<byte[]> payload = new ArrayList<byte[]>();
+        if (firstSpans.isPayloadAvailable()) {
+            payload.addAll(firstSpans.getPayload());
+        }
+        if (classNumber > 0) {
+            //System.out.println("Extension offsets "+start+","+end);
+            payload.add(createExtensionPayloads(start, end));
+        }
+        return payload;
+    }
+
+    /**
+     * Prepares a byte array of extension offsets with the given start and end
+     * positions and the class number, to be stored in payloads.
+     * 
+     * @param start
+     * @param end
+     * @return a byte array of extension offsets and the class number
+     */
+    private byte[] createExtensionPayloads(int start, int end) {
+        ByteBuffer buffer = ByteBuffer.allocate(9);
+        buffer.putInt(start);
+        buffer.putInt(end);
+        buffer.put(classNumber);
+        return buffer.array();
+    }
+
+    /**
+     * Sets the properties of the given candidate match span as the current
+     * match (state of ExpandedSpans).
+     * 
+     * @param candidateSpan
+     */
+    private void setMatch(CandidateSpan candidateSpan) {
+        matchDocNumber = candidateSpan.getDoc();
+        matchStartPosition = candidateSpan.getStart();
+        matchEndPosition = candidateSpan.getEnd();
+        matchPayload = candidateSpan.getPayloads();
+        matchCost = candidateSpan.getCost();
+    }
+
+    @Override
+    public boolean skipTo(int target) throws IOException {
+        if (hasMoreSpans && (firstSpans.doc() < target)) {
+            if (!firstSpans.skipTo(target)) {
+                hasMoreSpans = false;
+                return false;
+            }
+        }
+        matchPayload.clear();
+        return advance();
+    }
+
+    @Override
+    public long cost() {
+        return matchCost;
+    }
 
 }
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/MultipleDistanceSpans.java b/src/main/java/de/ids_mannheim/korap/query/spans/MultipleDistanceSpans.java
index 0dbd06e..f2edd23 100644
--- a/src/main/java/de/ids_mannheim/korap/query/spans/MultipleDistanceSpans.java
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/MultipleDistanceSpans.java
@@ -8,151 +8,177 @@
 import org.apache.lucene.index.TermContext;
 import org.apache.lucene.search.spans.Spans;
 import org.apache.lucene.util.Bits;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import de.ids_mannheim.korap.query.SpanMultipleDistanceQuery;
 
-/**	Span enumeration of matches whose two sub-spans have exactly the same 
- * 	first and second sub-sub-spans. This class basically filters the span 
- * 	matches of its child spans.
+/**
+ * Span enumeration of matches whose two sub-spans have exactly the same first
+ * and second sub-sub-spans. To obtain these matches, the span matches of the
+ * child spans are filtered.
  * 
- * 	This accommodates distance constraint with exclusion.
- * 	Case 1: return the match from another non-exclusion constraint.
- * 	Case 2: return only the first-span when all constraints are exclusions.
- * 	Case 3:	spans are not in the same doc 
- * 	 
- *	@author margaretha
+ * MultipleDistanceSpans accommodates distance constraint with exclusion. <br />
+ * <br />
+ * 
+ * This class deals with the following cases:
+ * <ol>
+ * <li>return the match from another non-exclusion constraint.</li>
+ * <li>return only the first-span when all constraints are exclusions.</li>
+ * <li>spans are not in the same doc</li>
+ * </ol>
+ * 
+ * @author margaretha
  * */
-public class MultipleDistanceSpans extends DistanceSpans{
+public class MultipleDistanceSpans extends DistanceSpans {
 
-    private final static Logger log = LoggerFactory.getLogger(MultipleDistanceSpans.class);
-    // This advices the java compiler to ignore all loggings
-    public static final boolean DEBUG = false;
+    private DistanceSpans x, y;
+    private boolean isOrdered;
 
-	private DistanceSpans x,y;
-	private boolean isOrdered;
-	
-	public MultipleDistanceSpans(SpanMultipleDistanceQuery query,
-			AtomicReaderContext context, Bits acceptDocs,
-			Map<Term, TermContext> termContexts, Spans firstSpans, 
-			Spans secondSpans, boolean isOrdered, boolean exclusion) 
-			throws IOException {
-		super(query, context, acceptDocs, termContexts);
-		this.isOrdered = isOrdered;
-		this.exclusion = exclusion;		
-		x = (DistanceSpans) firstSpans;
-		y = (DistanceSpans) secondSpans;		
-		hasMoreSpans = x.next() && y.next();		
-	}
+    /**
+     * Constructs MultipleDistanceSpans for the two given Spans with the given
+     * {@link SpanMultipleDistanceQuery}.
+     * 
+     * @param query a SpanMultipleDistanceQuery
+     * @param context
+     * @param acceptDocs
+     * @param termContexts
+     * @param firstSpans the firstspans
+     * @param secondSpans the secondspans
+     * @param isOrdered <code>true</code> if the spans must occur in order,
+     *        <code>false</code> otherwise.
+     * @param exclusion <code>true</code> if the secondspans must <em>not</em>
+     *        occur together with the firstspans.
+     * @throws IOException
+     */
+    public MultipleDistanceSpans(SpanMultipleDistanceQuery query,
+            AtomicReaderContext context, Bits acceptDocs,
+            Map<Term, TermContext> termContexts, Spans firstSpans,
+            Spans secondSpans, boolean isOrdered, boolean exclusion)
+            throws IOException {
+        super(query, context, acceptDocs, termContexts);
+        this.isOrdered = isOrdered;
+        this.exclusion = exclusion;
+        x = (DistanceSpans) firstSpans;
+        y = (DistanceSpans) secondSpans;
+        hasMoreSpans = x.next() && y.next();
+    }
 
-	@Override
-	public boolean next() throws IOException {
-		isStartEnumeration=false;
-  		matchPayload.clear();
-		return advance();		
-	}
-	
-	/** Find the next match.
-	 * */
-	protected boolean advance() throws IOException {		
-		while (hasMoreSpans && ensureSameDoc(x, y)){ 
-			if (findMatch()){
-				moveForward();
-				return true;
-			}
-			moveForward();
-		}		
-		return false;
-	}
-	
-	/** Find the next match of one of the sub/child-span.
-	 * */
-	private void moveForward() throws IOException{
-		if (isOrdered){
-			if (x.end() < y.end() || 
-					(x.end() == y.end() && x.start() < y.start()) )
-				hasMoreSpans = x.next();			 
-			else hasMoreSpans = y.next();
-		}
-		// The matches of unordered distance spans are ordered by the 
-		// start position
-		else {
-			if (x.start() < y.start() || 
-					(x.start() == y.start() && x.end() < y.end()) )
-				hasMoreSpans = x.next();			 
-			else hasMoreSpans = y.next();
-		}
-	}
-	
-	/** Check if the sub-spans of x and y having exactly the same position.  
-	 * 	This is basically an AND operation.
-	 * 	@return true iff the sub-spans are identical.
-	 * */	
-	protected boolean findMatch() throws IOException {
-		 		
-		CandidateSpan xf = x.getMatchFirstSpan();
-		CandidateSpan xs = x.getMatchSecondSpan();
-		
-		CandidateSpan yf = y.getMatchFirstSpan();
-		CandidateSpan ys = y.getMatchSecondSpan();
-		
-		if (x.isExclusion() || y.isExclusion()){			
-			if (xf.getStart() == yf.getStart() && xf.getEnd() == yf.getEnd()){
-				// case 2
-				if (x.isExclusion() && y.isExclusion()){
-					// set x or y doesnt matter
-					setMatchProperties(x,true);
-				}
-				// case 1
-				else if (x.isExclusion()){  
-					// set y, the usual match
-					setMatchProperties(y,true);
-				}
-				// case 1
-				else { setMatchProperties(x,true); }
-				return true;
-			}
-		}
-		else if (xf.getStart() == yf.getStart() &&
-				xf.getEnd() == yf.getEnd() &&
-				xs.getStart() == ys.getStart() &&
-				xs.getEnd() == ys.getEnd()){
-			setMatchProperties(x,false);			
-			return true;
-		}		
-		return false;
-	}
-	
+    @Override
+    public boolean next() throws IOException {
+        isStartEnumeration = false;
+        matchPayload.clear();
+        return advance();
+    }
 
-	private void setMatchProperties(DistanceSpans span, boolean exclusion) {
-		matchStartPosition = span.start();
-		matchEndPosition = span.end();
-		matchDocNumber = span.doc();
-		matchPayload = span.matchPayload;	
-		
-		setMatchFirstSpan(span.getMatchFirstSpan());
-		if (!exclusion) setMatchSecondSpan(span.getMatchSecondSpan());
-		if (DEBUG)
-		    log.trace("doc# {}, start {}, end {}",matchDocNumber,
-			      matchStartPosition,matchEndPosition);	
-	}
+    /**
+     * Finds the next match.
+     * */
+    protected boolean advance() throws IOException {
+        while (hasMoreSpans && ensureSameDoc(x, y)) {
+            if (findMatch()) {
+                moveForward();
+                return true;
+            }
+            moveForward();
+        }
+        return false;
+    }
 
-	@Override
-	public boolean skipTo(int target) throws IOException {
-		if (hasMoreSpans && (y.doc() < target)){
-  			if (!y.skipTo(target)){  				
-  				return false;
-  			}
-  		}
-		matchPayload.clear();
-		isStartEnumeration=false;
-		return advance();
-	}
+    /**
+     * Finds the next match of one of the sub/child-span.
+     * 
+     * @throws IOException
+     */
+    private void moveForward() throws IOException {
+        if (isOrdered) {
+            if (x.end() < y.end()
+                    || (x.end() == y.end() && x.start() < y.start()))
+                hasMoreSpans = x.next();
+            else
+                hasMoreSpans = y.next();
+        }
+        // The matches of unordered distance spans are ordered by the 
+        // start position
+        else {
+            if (x.start() < y.start()
+                    || (x.start() == y.start() && x.end() < y.end()))
+                hasMoreSpans = x.next();
+            else
+                hasMoreSpans = y.next();
+        }
+    }
 
-	@Override
-	public long cost() {		
-		return x.cost() + y.cost();
-	}
+    /**
+     * Checks if the sub-spans of x and y having exactly the same position. This
+     * is basically an AND operation.
+     * 
+     * @return true iff the sub-spans are identical.
+     * @throws IOException
+     */
+    protected boolean findMatch() throws IOException {
+
+        CandidateSpan xf = x.getMatchFirstSpan();
+        CandidateSpan xs = x.getMatchSecondSpan();
+
+        CandidateSpan yf = y.getMatchFirstSpan();
+        CandidateSpan ys = y.getMatchSecondSpan();
+
+        if (x.isExclusion() || y.isExclusion()) {
+            if (xf.getStart() == yf.getStart() && xf.getEnd() == yf.getEnd()) {
+                // case 2
+                if (x.isExclusion() && y.isExclusion()) {
+                    // set x or y doesnt matter
+                    setMatchProperties(x, true);
+                }
+                // case 1
+                else if (x.isExclusion()) {
+                    // set y, the usual match
+                    setMatchProperties(y, true);
+                }
+                // case 1
+                else {
+                    setMatchProperties(x, true);
+                }
+                return true;
+            }
+        } else if (xf.getStart() == yf.getStart() && xf.getEnd() == yf.getEnd()
+                && xs.getStart() == ys.getStart() && xs.getEnd() == ys.getEnd()) {
+            setMatchProperties(x, false);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * @param span a DistanceSpan
+     * @param exclusion <code>true</code> if the spans must <em>not</em> occur
+     *        together, <code>false</code> otherwise.
+     */
+    private void setMatchProperties(DistanceSpans span, boolean exclusion) {
+        matchStartPosition = span.start();
+        matchEndPosition = span.end();
+        matchDocNumber = span.doc();
+        matchPayload = span.matchPayload;
+
+        setMatchFirstSpan(span.getMatchFirstSpan());
+        if (!exclusion)
+            setMatchSecondSpan(span.getMatchSecondSpan());
+    }
+
+    @Override
+    public boolean skipTo(int target) throws IOException {
+        if (hasMoreSpans && (y.doc() < target)) {
+            if (!y.skipTo(target)) {
+                return false;
+            }
+        }
+        matchPayload.clear();
+        isStartEnumeration = false;
+        return advance();
+    }
+
+    @Override
+    public long cost() {
+        return x.cost() + y.cost();
+    }
 
 }