Added javadoc comments.
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/CandidateSpan.java b/src/main/java/de/ids_mannheim/korap/query/spans/CandidateSpan.java
index 968fa39..7beedc0 100644
--- a/src/main/java/de/ids_mannheim/korap/query/spans/CandidateSpan.java
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/CandidateSpan.java
@@ -6,133 +6,251 @@
 
 import org.apache.lucene.search.spans.Spans;
 
-/** A span kept as a candidate for matching with another Span
- * 	@author margaretha
+/**
+ * A CandidateSpan works as an object storing the current state of the
+ * corresponding Lucene {@link Spans}, which is an enumeration. CandidateSpan is
+ * used for various purposes, such as for collecting spans which will be used in
+ * a latter process or next matching.
+ * 
+ * @author margaretha
  * */
-public class CandidateSpan implements Comparable<CandidateSpan>, Cloneable{	
-	private int doc,start,end;
-	private long cost;
-	private Collection<byte[]> payloads = new ArrayList<>();
-	private int position;
-	private CandidateSpan childSpan; // used for example for multiple distance with unordered constraint 
-	protected short spanId;
-	
-	
-	public CandidateSpan(Spans span) throws IOException {
-		this.doc = span.doc();
-		this.start = span.start();
-		this.end = span.end();
-		this.cost = span.cost();
-		if (span.isPayloadAvailable())
-			setPayloads(span.getPayload());
-	}	
-	
-	@Override
-	protected CandidateSpan clone() throws CloneNotSupportedException {
-		return new CandidateSpan(
-				this.start,
-				this.end,
-				this.doc,
-				this.cost,
-				this.payloads
-		);		
-	}
-	
-	public CandidateSpan(Spans span, int position) throws IOException {
-		this(span);
-		this.position = position;		
-	}
-	
-	public CandidateSpan(int start, int end, int doc, long cost,
-			Collection<byte[]> payloads) {
-		this.start = start;
-		this.end = end;
-		this.doc = doc;
-		this.cost = cost;
-		if (payloads != null) setPayloads(payloads);
-	}
+public class CandidateSpan implements Comparable<CandidateSpan>, Cloneable {
+    private int doc, start, end;
+    private long cost;
+    private Collection<byte[]> payloads = new ArrayList<>();
+    private int position;
+    private CandidateSpan childSpan; // used for example for multiple distance
+                                     // with unordered constraint
+    protected short spanId;
 
-	public int getDoc() {
-		return doc;
-	}
-	public void setDoc(int doc) {
-		this.doc = doc;
-	}
-	public int getStart() {
-		return start;
-	}
-	public void setStart(int start) {
-		this.start = start;
-	}
-	public int getEnd() {
-		return end;
-	}
-	public void setEnd(int end) {
-		this.end = end;
-	}
+    /**
+     * Constructs a CandidateSpan for the given Span.
+     * 
+     * @param span a Span
+     * @throws IOException
+     */
+    public CandidateSpan(Spans span) throws IOException {
+        this.doc = span.doc();
+        this.start = span.start();
+        this.end = span.end();
+        this.cost = span.cost();
+        if (span.isPayloadAvailable())
+            setPayloads(span.getPayload());
+    }
 
-	public Collection<byte[]> getPayloads() {
-		return payloads;
-	}
+    /**
+     * Constructs a CandidateSpan for the given Span and element position (where
+     * the span is included in a document). The element position is important
+     * for the matching process in {@link ElementDistanceSpans}.
+     * 
+     * @param span a Span
+     * @param position an element position
+     * @throws IOException
+     */
+    public CandidateSpan(Spans span, int position) throws IOException {
+        this(span);
+        this.position = position;
+    }
 
-	public void setPayloads(Collection<byte[]> payloads) {		
-		
-		for (byte[] b : payloads){			
-			if (b == null)				
-				this.payloads.add(null);			
-			else 
-				this.payloads.add(b.clone());			
-		}
-	}
+    /**
+     * Constructs a CandidateSpan from all the given variables which are
+     * properties of a Span.
+     * 
+     * @param start the start position of a span
+     * @param end the end position of a span
+     * @param doc the document including the span
+     * @param cost the cost of finding a span
+     * @param payloads the payloads of a span
+     */
+    public CandidateSpan(int start, int end, int doc, long cost,
+            Collection<byte[]> payloads) {
+        this.start = start;
+        this.end = end;
+        this.doc = doc;
+        this.cost = cost;
+        if (payloads != null)
+            setPayloads(payloads);
+    }
 
-	public long getCost() {
-		return cost;
-	}
+    @Override
+    protected CandidateSpan clone() throws CloneNotSupportedException {
+        return new CandidateSpan(this.start, this.end, this.doc, this.cost,
+                this.payloads);
+    }
 
-	public void setCost(long cost) {
-		this.cost = cost;
-	}
+    /**
+     * Returns the document number containing the CandidateSpan.
+     * 
+     * @return the document number
+     */
+    public int getDoc() {
+        return doc;
+    }
 
-	public int getPosition() {
-		return position;
-	}
+    /**
+     * Sets the document number containing the CandidateSpan.
+     * 
+     * @param doc the document number
+     */
+    public void setDoc(int doc) {
+        this.doc = doc;
+    }
 
-	public void setPosition(int position) {
-		this.position = position;
-	}
+    /**
+     * Returns the start position of the CandidateSpan.
+     * 
+     * @return the start position
+     */
+    public int getStart() {
+        return start;
+    }
 
-	public CandidateSpan getChildSpan() {
-		return childSpan;
-	}
+    /**
+     * Sets the start position of the CandidateSpan.
+     * 
+     * @param start the start position
+     */
+    public void setStart(int start) {
+        this.start = start;
+    }
 
-	public void setChildSpan(CandidateSpan childSpan) {
-		this.childSpan = childSpan;
-	}
+    /**
+     * Returns the end position of the CandidateSpan.
+     * 
+     * @return the end position
+     */
+    public int getEnd() {
+        return end;
+    }
 
-	public short getSpanId() {
-		return spanId;
-	}
+    /**
+     * Sets the end position of the CandidateSpan.
+     * 
+     * @param end the end position
+     */
+    public void setEnd(int end) {
+        this.end = end;
+    }
 
-	public void setSpanId(short elementRef) {
-		this.spanId = elementRef;
-	}
+    /**
+     * Returns the payloads of the CandidateSpan.
+     * 
+     * @return the payloads
+     */
+    public Collection<byte[]> getPayloads() {
+        return payloads;
+    }
 
-	@Override
-	public int compareTo(CandidateSpan o) {
-		if (this.doc == o.doc){
-			if (this.getStart() == o.getStart()){
-				if (this.getEnd() == o.getEnd())
-					return 0;	
-				if (this.getEnd() > o.getEnd() )
-					return 1;
-				else return -1;
-			}
-			else if (this.getStart() < o.getStart())
-				return -1;
-			else return 1;	
-		}
-		else if (this.doc < o.doc)
-			return -1;
-		else return 1;
-	}
+    /**
+     * Sets the payloads of the CandidateSpan.
+     * 
+     * @param payloads the payloads
+     */
+    public void setPayloads(Collection<byte[]> payloads) {
+
+        for (byte[] b : payloads) {
+            if (b == null)
+                this.payloads.add(null);
+            else
+                this.payloads.add(b.clone());
+        }
+    }
+
+    /**
+     * Returns the cost of finding the CandidateSpan.
+     * 
+     * @return the cost
+     */
+    public long getCost() {
+        return cost;
+    }
+
+    /**
+     * Sets the cost of finding the CandidateSpan.
+     * 
+     * @param cost the cost
+     */
+    public void setCost(long cost) {
+        this.cost = cost;
+    }
+
+    /**
+     * Returns the element position number containing the CandidateSpan.
+     * 
+     * @return the element position number
+     */
+    public int getPosition() {
+        return position;
+    }
+
+    /**
+     * Sets the element position number containing the CandidateSpan.
+     * 
+     * @param position the element position number
+     */
+    public void setPosition(int position) {
+        this.position = position;
+    }
+
+    /**
+     * Returns a child/sub Span of the CandidateSpan.
+     * 
+     * @return a child/sub span of the CandidateSpan
+     */
+    public CandidateSpan getChildSpan() {
+        return childSpan;
+    }
+
+    /**
+     * Sets the child/sub span of the CandidateSpan.
+     * 
+     * @param childSpan a child/sub span of the CandidateSpan
+     */
+    public void setChildSpan(CandidateSpan childSpan) {
+        this.childSpan = childSpan;
+    }
+
+    /**
+     * Returns the span id of another Span related to the CandidateSpan. Only
+     * CandidateSpan of particular Spans such as {@link AttributeSpans} having
+     * this property. For instance, an AttributeSpan has a spanId of the element
+     * it belongs to.
+     * 
+     * @return the span id of another Span related to the CandidateSpan
+     */
+    public short getSpanId() {
+        return spanId;
+    }
+
+    /**
+     * Sets the span id of another Span related to the CandidateSpan. Only
+     * CandidateSpan of particular Spans such as {@link AttributeSpans} having
+     * this property. For instance, an AttributeSpan has a spanId of the element
+     * it belongs to.
+     * 
+     * @param spanId the span id of another Span related to the CandidateSpan
+     */
+    public void setSpanId(short spanId) {
+        this.spanId = spanId;
+    }
+
+    @Override
+    public int compareTo(CandidateSpan o) {
+        if (this.doc == o.doc) {
+            if (this.getStart() == o.getStart()) {
+                if (this.getEnd() == o.getEnd())
+                    return 0;
+                if (this.getEnd() > o.getEnd())
+                    return 1;
+                else
+                    return -1;
+            } else if (this.getStart() < o.getStart())
+                return -1;
+            else
+                return 1;
+        } else if (this.doc < o.doc)
+            return -1;
+        else
+            return 1;
+    }
 }
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/ElementDistanceSpans.java b/src/main/java/de/ids_mannheim/korap/query/spans/ElementDistanceSpans.java
index 229004e..cad169a 100644
--- a/src/main/java/de/ids_mannheim/korap/query/spans/ElementDistanceSpans.java
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/ElementDistanceSpans.java
@@ -12,145 +12,158 @@
 
 import de.ids_mannheim.korap.query.SpanDistanceQuery;
 
-/** Span enumeration of element-based distance span matches. 	
- * 	Each match consists of two child spans. The element-distance between 
- * 	the child spans is the difference between the element position numbers 
- * 	where the child spans are. The element-distance unit can be a sentence 
- * 	or a paragraph. All other child spans occurrence which are not in 
- * 	a sentence or a paragraph (with respect to the element distance type 
- * 	current used), are ignored.
- * 	
- * 	Note: elements cannot overlap to each other.
+/**
+ * Span enumeration of element-based distance span matches. Each match consists
+ * of two child spans. The element-distance between the child spans is the
+ * difference between the element position numbers where the child spans are.
+ * The element-distance unit can be a sentence or a paragraph. All other child
+ * spans' occurrences which are not in a sentence or a paragraph (with respect
+ * to the element distance type currently used), are ignored.
+ * 
+ * Note: elements cannot overlap to each other.
  * 
  * @author margaretha
  * */
 public class ElementDistanceSpans extends OrderedDistanceSpans {
 
-	private Spans elements;	
-	private boolean hasMoreElements;
-	private int elementPosition;	
-	private int secondSpanPostion;	
-		
-	public ElementDistanceSpans(SpanDistanceQuery query,
-			AtomicReaderContext context, Bits acceptDocs,
-			Map<Term, TermContext> termContexts)
-			throws IOException {
-		super(query, context, acceptDocs, termContexts);
-  		
-  		elements = query.getElementQuery().
-  			getSpans(context, acceptDocs, termContexts);
-  		
-  		hasMoreElements = elements.next();
-  		hasMoreSpans = hasMoreFirstSpans && hasMoreElements;
-  		elementPosition=0;
-	}
+    private Spans elements;
+    private boolean hasMoreElements;
+    private int elementPosition;
+    private int secondSpanPostion;
 
-	@Override
-	protected boolean findMatch() throws IOException {
-		CandidateSpan candidateSpan = candidateList.get(candidateListIndex);
-		int actualDistance = secondSpanPostion - candidateSpan.getPosition();
-		
-		// In the same element
-		if (minDistance == 0 && actualDistance == 0){			
-			setMatchProperties(candidateSpan, true);
-			return true;			
-		}		
-		
-		if (minDistance <= actualDistance && actualDistance <= maxDistance){
-			setMatchProperties(candidateSpan, false);
-			return true;
-		}
-		
-		return false;
-	}
-	
-	@Override
-	protected void setCandidateList() throws IOException{
- 		if (candidateListDocNum == elements.doc() && 
- 				candidateListDocNum == secondSpans.doc()){
- 			candidateListIndex = -1;
- 			addNewCandidates();
- 		}
- 		else {
- 			candidateList.clear(); 			
- 			if (hasMoreFirstSpans && findSameDoc(firstSpans, secondSpans, elements)){
- 				candidateListDocNum = firstSpans.doc();
- 				elementPosition=0;
- 				candidateListIndex = -1;
- 				addNewCandidates();				
-			}		
-		}		
-	}
-	
-	/** Add new possible candidates. Candidates must be in an element 
-	 * 	and not too far from the secondspan.
-	 * */
-	private void addNewCandidates() throws IOException{		
-		while ( hasMoreFirstSpans && 
-				firstSpans.doc() == candidateListDocNum &&
-				firstSpans.start() < secondSpans.end()){
-			
-			if (advanceElementTo(firstSpans)){
-				candidateList.add(new CandidateSpan(firstSpans,elementPosition));				
-				filterCandidateList(elementPosition);
-			}
-			hasMoreFirstSpans = firstSpans.next();
-		}
-	}
-	
-	
-	/** Advance elements until encountering a span within the given document.
-	 * @return true iff an element containing the span, is found.
-	 */
-	private boolean advanceElementTo(Spans span) throws IOException{
-		while (hasMoreElements && 
-				elements.doc() == candidateListDocNum &&
-				elements.start() < span.end()){
-			
-			if (span.start() >= elements.start() &&
-					span.end() <= elements.end()){
-				return true;
-			}			
-			
-			hasMoreElements = elements.next();
-			elementPosition++;
-		}
-		return false;
-	}
-	
+    /**
+     * Constructs ElementDistanceSpans based on the given SpanDistanceQuery.
+     * 
+     * @param query a SpanDistanceQuery
+     * @param context
+     * @param acceptDocs
+     * @param termContexts
+     * @throws IOException
+     */
+    public ElementDistanceSpans(SpanDistanceQuery query,
+            AtomicReaderContext context, Bits acceptDocs,
+            Map<Term, TermContext> termContexts) throws IOException {
+        super(query, context, acceptDocs, termContexts);
 
-	/** Reduce the number of candidates by removing all candidates that are 
-	 * 	not within the max distance from the given element position.
-	 * */
-	private void filterCandidateList(int position){
-		
-		Iterator<CandidateSpan> i = candidateList.iterator();
-		CandidateSpan cs;
-		while(i.hasNext()){
-			cs = i.next();
-			if (cs.getPosition() == position || 
-					cs.getPosition()+maxDistance >= position){
-				break;
-			}
-			i.remove();
-		}		
-		//System.out.println("pos "+position+" " +candidateList.size());
-	}
-	
-	@Override
-	protected boolean isSecondSpanValid() throws IOException{
-		if (advanceElementTo(secondSpans)){
-			secondSpanPostion = elementPosition;
-			filterCandidateList(secondSpanPostion);
-			return true;
-		}
-		// second span is not in an element
-		return false;
-	}
-	
-	@Override
-	public long cost() {
-		CandidateSpan candidateSpan = candidateList.get(candidateListIndex);
-		return elements.cost() + candidateSpan.getCost() + secondSpans.cost();
-	}
+        elements = query.getElementQuery().getSpans(context, acceptDocs,
+                termContexts);
+
+        hasMoreElements = elements.next();
+        hasMoreSpans = hasMoreFirstSpans && hasMoreElements;
+        elementPosition = 0;
+    }
+
+    @Override
+    protected boolean findMatch() throws IOException {
+        CandidateSpan candidateSpan = candidateList.get(candidateListIndex);
+        int actualDistance = secondSpanPostion - candidateSpan.getPosition();
+
+        // In the same element
+        if (minDistance == 0 && actualDistance == 0) {
+            setMatchProperties(candidateSpan, true);
+            return true;
+        }
+
+        if (minDistance <= actualDistance && actualDistance <= maxDistance) {
+            setMatchProperties(candidateSpan, false);
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    protected void setCandidateList() throws IOException {
+        if (candidateListDocNum == elements.doc()
+                && candidateListDocNum == secondSpans.doc()) {
+            candidateListIndex = -1;
+            addNewCandidates();
+        } else {
+            candidateList.clear();
+            if (hasMoreFirstSpans
+                    && findSameDoc(firstSpans, secondSpans, elements)) {
+                candidateListDocNum = firstSpans.doc();
+                elementPosition = 0;
+                candidateListIndex = -1;
+                addNewCandidates();
+            }
+        }
+    }
+
+    /**
+     * Add new possible (candidate) firstspans. Candidate firstspans must be in
+     * an element and not too far from the secondspan.
+     * 
+     * @throws IOException
+     */
+    private void addNewCandidates() throws IOException {
+        while (hasMoreFirstSpans && firstSpans.doc() == candidateListDocNum
+                && firstSpans.start() < secondSpans.end()) {
+
+            if (advanceElementTo(firstSpans)) {
+                candidateList
+                        .add(new CandidateSpan(firstSpans, elementPosition));
+                filterCandidateList(elementPosition);
+            }
+            hasMoreFirstSpans = firstSpans.next();
+        }
+    }
+
+    /**
+     * Advance elements until encountering a span within the given document.
+     * 
+     * @return true iff an element containing the span, is found.
+     */
+    private boolean advanceElementTo(Spans span) throws IOException {
+        while (hasMoreElements && elements.doc() == candidateListDocNum
+                && elements.start() < span.end()) {
+
+            if (span.start() >= elements.start()
+                    && span.end() <= elements.end()) {
+                return true;
+            }
+
+            hasMoreElements = elements.next();
+            elementPosition++;
+        }
+        return false;
+    }
+
+    /**
+     * Reduce the number of candidates by removing all candidates that are not
+     * within the max distance from the given element position.
+     * 
+     * @param position an element position
+     */
+    private void filterCandidateList(int position) {
+
+        Iterator<CandidateSpan> i = candidateList.iterator();
+        CandidateSpan cs;
+        while (i.hasNext()) {
+            cs = i.next();
+            if (cs.getPosition() == position
+                    || cs.getPosition() + maxDistance >= position) {
+                break;
+            }
+            i.remove();
+        }
+        // System.out.println("pos "+position+" " +candidateList.size());
+    }
+
+    @Override
+    protected boolean isSecondSpanValid() throws IOException {
+        if (advanceElementTo(secondSpans)) {
+            secondSpanPostion = elementPosition;
+            filterCandidateList(secondSpanPostion);
+            return true;
+        }
+        // second span is not in an element
+        return false;
+    }
+
+    @Override
+    public long cost() {
+        CandidateSpan candidateSpan = candidateList.get(candidateListIndex);
+        return elements.cost() + candidateSpan.getCost() + secondSpans.cost();
+    }
 }
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/OrderedDistanceSpans.java b/src/main/java/de/ids_mannheim/korap/query/spans/OrderedDistanceSpans.java
index 1dd1a04..52185e8 100644
--- a/src/main/java/de/ids_mannheim/korap/query/spans/OrderedDistanceSpans.java
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/OrderedDistanceSpans.java
@@ -12,120 +12,145 @@
 
 import de.ids_mannheim.korap.query.SpanDistanceQuery;
 
-/** Base class for calculating a distance between two ordered spans.
- * 	@author margaretha
+/**
+ * Base class for calculating a distance between two ordered spans.
+ * 
+ * @author margaretha
  * */
 public abstract class OrderedDistanceSpans extends DistanceSpans {
 
-        public static final boolean DEBUG = false;
+    protected boolean hasMoreFirstSpans;
+    protected int minDistance, maxDistance;
 
-	protected boolean hasMoreFirstSpans;	
-	protected int minDistance,maxDistance;
-	
-	protected List<CandidateSpan> candidateList;
-	protected int candidateListIndex;
-	protected int candidateListDocNum;
-	
-    
-	public OrderedDistanceSpans(SpanDistanceQuery query,
-			AtomicReaderContext context, Bits acceptDocs,
-			Map<Term, TermContext> termContexts)
-			throws IOException {		
-		super(query, context, acceptDocs, termContexts);
-		
-		minDistance = query.getMinDistance();
-		maxDistance = query.getMaxDistance();		
-  		 		  		
-  		hasMoreFirstSpans = firstSpans.next();
-  		
-		candidateList = new ArrayList<>();
-		candidateListIndex = -1;
-		candidateListDocNum = firstSpans.doc();
-	}
-	
-	/** Find a span match in the candidate list.
-	 * */
-	@Override
-	protected boolean advance() throws IOException {
-		while( hasMoreSpans && candidateListIndex < candidateList.size() ){					
-			// Check candidates
-			for (candidateListIndex++;candidateListIndex < candidateList.size();
-					candidateListIndex++){
-				if (findMatch())					
-					return true;					
-			}			
-			
-			do { // Forward secondspan 
-				hasMoreSpans = secondSpans.next();
-				setCandidateList();
-			}
-			while (hasMoreSpans && !isSecondSpanValid());
-		}
-		return false;
-	}
-	
-	/** Determine if the current second span is valid. It is always valid in 
-	 * 	TokenDistanceSpan, but it can be invalid in the ElementDistanceSpan,
-	 * 	namely when it is not within a particular element (a sentence or a 
-	 * 	paragraph depends on the element distance unit).
-	 *  
-	 * */
-	protected abstract boolean isSecondSpanValid() throws IOException;
-	
-	/** Collect all possible firstspan instances as candidate spans for
-	 * 	the current secondspan. The candidate spans are within the max 
-	 * 	distance from the current secondspan. 
-	 * */
-	protected abstract void setCandidateList() throws IOException;
-	
-	/** Define the conditions for a match. 
-	 * */
-	protected abstract boolean findMatch() throws IOException;	
-	
-	/** Define the properties of a span match.
-	 * */
-	protected void setMatchProperties(CandidateSpan candidateSpan, 
-			boolean isDistanceZero) throws IOException{
-		
-		setMatchFirstSpan(candidateSpan);
-		setMatchSecondSpan(new CandidateSpan(secondSpans));
-		
-		if (isDistanceZero){
-			matchStartPosition = Math.min(candidateSpan.getStart(), secondSpans.start());
-			matchEndPosition = Math.max(candidateSpan.getEnd(), secondSpans.end());
-		}
-		else {
-			matchStartPosition = candidateSpan.getStart();
-			matchEndPosition = secondSpans.end();
-		}
-		
-		this.matchDocNumber = secondSpans.doc();		
-		if (collectPayloads){			
-  		    if (candidateSpan.getPayloads() != null) {
-  		    	matchPayload.addAll(candidateSpan.getPayloads()); 
-  		    }
-  		    if (secondSpans.isPayloadAvailable()) {
-  		    	matchPayload.addAll(secondSpans.getPayload());  		    	
-  		    }
-		}
+    protected List<CandidateSpan> candidateList;
+    protected int candidateListIndex;
+    protected int candidateListDocNum;
 
-		if (DEBUG)
-		    log.trace("doc# {}, start {}, end {}",matchDocNumber,matchStartPosition,
-			      matchEndPosition);	
-	}
+    /**
+     * Constructs an OrderedDistanceSpans based on the given SpanDistanceQuery.
+     * 
+     * @param query a SpanDistanceQuery
+     * @param context
+     * @param acceptDocs
+     * @param termContexts
+     * @throws IOException
+     */
+    public OrderedDistanceSpans(SpanDistanceQuery query,
+            AtomicReaderContext context, Bits acceptDocs,
+            Map<Term, TermContext> termContexts) throws IOException {
+        super(query, context, acceptDocs, termContexts);
 
-	@Override
-	public boolean skipTo(int target) throws IOException {
-		if (hasMoreSpans && (secondSpans.doc() < target)){
-  			if (!secondSpans.skipTo(target)){
-  				candidateList.clear();
-  				return false;
-  			}
-  		} 	
-		
-		setCandidateList();
-		matchPayload.clear();
-		isStartEnumeration=false;
-		return advance();
-	}
+        minDistance = query.getMinDistance();
+        maxDistance = query.getMaxDistance();
+
+        hasMoreFirstSpans = firstSpans.next();
+
+        candidateList = new ArrayList<>();
+        candidateListIndex = -1;
+        candidateListDocNum = firstSpans.doc();
+    }
+
+    /**
+     * Finds a span match in the candidate list.
+     * */
+    @Override
+    protected boolean advance() throws IOException {
+        while (hasMoreSpans && candidateListIndex < candidateList.size()) {
+            // Check candidates
+            for (candidateListIndex++; candidateListIndex < candidateList
+                    .size(); candidateListIndex++) {
+                if (findMatch())
+                    return true;
+            }
+
+            do { // Forward secondspan
+                hasMoreSpans = secondSpans.next();
+                setCandidateList();
+            } while (hasMoreSpans && !isSecondSpanValid());
+        }
+        return false;
+    }
+
+    /**
+     * Determines if the current second span is valid (i.e. within an element).
+     * It is always valid in TokenDistanceSpan, but it can be invalid in the
+     * ElementDistanceSpan, namely when it is not within a particular element (a
+     * sentence or a paragraph depends on the element distance unit).
+     * 
+     * @return <code>true</code> of the current second span is valid,
+     *         <code>false</code> otherwise.
+     * @throws IOException
+     */
+    protected abstract boolean isSecondSpanValid() throws IOException;
+
+    /**
+     * Stores/collects the states of all possible firstspans as candidate spans
+     * for the current secondspan. The candidate spans must be within the
+     * maximum distance from the current secondspan.
+     * 
+     * @throws IOException
+     */
+    protected abstract void setCandidateList() throws IOException;
+
+    /**
+     * Defines the conditions for a match and tells if a match is found.
+     * 
+     * @return <code>true</code> if a match is found, <code>false</code>
+     *         otherwise.
+     * @throws IOException
+     */
+    protected abstract boolean findMatch() throws IOException;
+
+    /**
+     * Defines the properties of a span match. The distance between the first
+     * and the second spans is zero, when there is an intersection between them
+     * in {@link TokenDistanceSpans}, or they occur in the same element in
+     * {@link ElementDistanceSpans}.
+     * 
+     * @param candidateSpan a match span
+     * @param isDistanceZero <code>true</code> if the distance between the first
+     *        and the second spans is zero, <code>false</code> otherwise.
+     * @throws IOException
+     */
+    protected void setMatchProperties(CandidateSpan candidateSpan,
+            boolean isDistanceZero) throws IOException {
+
+        setMatchFirstSpan(candidateSpan);
+        setMatchSecondSpan(new CandidateSpan(secondSpans));
+
+        if (isDistanceZero) {
+            matchStartPosition = Math.min(candidateSpan.getStart(),
+                    secondSpans.start());
+            matchEndPosition = Math.max(candidateSpan.getEnd(),
+                    secondSpans.end());
+        } else {
+            matchStartPosition = candidateSpan.getStart();
+            matchEndPosition = secondSpans.end();
+        }
+
+        this.matchDocNumber = secondSpans.doc();
+        if (collectPayloads) {
+            if (candidateSpan.getPayloads() != null) {
+                matchPayload.addAll(candidateSpan.getPayloads());
+            }
+            if (secondSpans.isPayloadAvailable()) {
+                matchPayload.addAll(secondSpans.getPayload());
+            }
+        }
+    }
+
+    @Override
+    public boolean skipTo(int target) throws IOException {
+        if (hasMoreSpans && (secondSpans.doc() < target)) {
+            if (!secondSpans.skipTo(target)) {
+                candidateList.clear();
+                return false;
+            }
+        }
+
+        setCandidateList();
+        matchPayload.clear();
+        isStartEnumeration = false;
+        return advance();
+    }
 }
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/TokenDistanceSpans.java b/src/main/java/de/ids_mannheim/korap/query/spans/TokenDistanceSpans.java
index f3c4907..150c61e 100644
--- a/src/main/java/de/ids_mannheim/korap/query/spans/TokenDistanceSpans.java
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/TokenDistanceSpans.java
@@ -12,95 +12,108 @@
 
 import de.ids_mannheim.korap.query.SpanDistanceQuery;
 
-/** Enumeration of token-based distance span matches. 
- * 	Each match consists of two specified spans having an actual distance
- *  in the range of the min and max distance parameters given in the query.
+/**
+ * Enumeration of token-based distance span matches consisting of two child
+ * spans having an actual distance in the range of the minimum and maximum
+ * distance parameters specified in the corresponding query.
  * 
- *	@author margaretha 
+ * @author margaretha
  * */
-public class TokenDistanceSpans extends OrderedDistanceSpans{
+public class TokenDistanceSpans extends OrderedDistanceSpans {
 
-	public TokenDistanceSpans(SpanDistanceQuery query,
-			AtomicReaderContext context, Bits acceptDocs,
-			Map<Term, TermContext> termContexts) throws IOException {
-		super(query, context, acceptDocs, termContexts);		
-		hasMoreSpans = hasMoreFirstSpans;
-	}
-	
-	@Override
-	protected void setCandidateList() throws IOException{
- 		if (candidateListDocNum == secondSpans.doc()){						
-			copyPossibleCandidates();
-			addNewCandidates();
-			candidateListIndex = -1;
- 		}
- 		else {
- 			candidateList.clear(); 			
- 			if (hasMoreFirstSpans && ensureSameDoc(firstSpans,secondSpans)){
- 				candidateListDocNum = firstSpans.doc();
-				addNewCandidates();
-				candidateListIndex = -1;
-			}		
-		} 		
-	}
-	
-	/** Copy candidate spans which are still possible to create a match,
-	 * 	from the candidate list prepared for the previous second spans. 
-	 * */
-	private void copyPossibleCandidates(){
-		List<CandidateSpan> temp = new ArrayList<>();
-		for (CandidateSpan cs : candidateList){
-			if (cs.getEnd()+maxDistance > secondSpans.start())
-				temp.add(cs);
-		}
-		candidateList = temp;
-	}
-	
-	/** Add new possible candidates for the current secondspan.
-	 * */
-	private void addNewCandidates() throws IOException{
-		while ( hasMoreFirstSpans && 
-				firstSpans.doc() == candidateListDocNum &&
-				firstSpans.start() < secondSpans.end()){
-			
-			if (firstSpans.end()+maxDistance > secondSpans.start())
-				candidateList.add(new CandidateSpan(firstSpans));
-			
-			hasMoreFirstSpans = firstSpans.next();
-		}
-	}
-	
-	@Override
-	protected boolean findMatch() throws IOException {
-		CandidateSpan candidateSpan = candidateList.get(candidateListIndex);		
-		if (minDistance == 0 &&
-				// intersection
-				candidateSpan.getStart() < secondSpans.end() && 
-				secondSpans.start() < candidateSpan.getEnd()){		
-			
-			setMatchProperties(candidateSpan, true);
-			return true;			
-		}
-		
-		int actualDistance = secondSpans.start() - candidateSpan.getEnd() +1;
-		if (candidateSpan.getStart() < secondSpans.start() &&
-				minDistance <= actualDistance && 
-				actualDistance <= maxDistance){					
-			
-			setMatchProperties(candidateSpan, false);
-			return true;
-		}		
-		return false;
-	}
-	
-	@Override
-	public long cost() {
-		CandidateSpan candidateSpan = candidateList.get(candidateListIndex);
-		return candidateSpan.getCost() + secondSpans.cost();
-	}
+    /**
+     * Constructs a TokenDistanceSpans from the given query.
+     * 
+     * @param query a SpanDistanceQuery
+     * @param context
+     * @param acceptDocs
+     * @param termContexts
+     * @throws IOException
+     */
+    public TokenDistanceSpans(SpanDistanceQuery query,
+            AtomicReaderContext context, Bits acceptDocs,
+            Map<Term, TermContext> termContexts) throws IOException {
+        super(query, context, acceptDocs, termContexts);
+        hasMoreSpans = hasMoreFirstSpans;
+    }
 
-	@Override
-	protected boolean isSecondSpanValid() throws IOException {
-		return true;
-	}
+    @Override
+    protected void setCandidateList() throws IOException {
+        if (candidateListDocNum == secondSpans.doc()) {
+            copyPossibleCandidates();
+            addNewCandidates();
+            candidateListIndex = -1;
+        } else {
+            candidateList.clear();
+            if (hasMoreFirstSpans && ensureSameDoc(firstSpans, secondSpans)) {
+                candidateListDocNum = firstSpans.doc();
+                addNewCandidates();
+                candidateListIndex = -1;
+            }
+        }
+    }
+
+    /**
+     * Restructures the candidateList to contain only candidate (first) spans
+     * which are still possible to create a match, from the candidate list
+     * prepared for the previous second spans.
+     * 
+     * */
+    private void copyPossibleCandidates() {
+        List<CandidateSpan> temp = new ArrayList<>();
+        for (CandidateSpan cs : candidateList) {
+            if (cs.getEnd() + maxDistance > secondSpans.start())
+                temp.add(cs);
+        }
+        candidateList = temp;
+    }
+
+    /**
+     * Add new possible firstspan candidates for the current secondspan.
+     * */
+    private void addNewCandidates() throws IOException {
+        while (hasMoreFirstSpans && firstSpans.doc() == candidateListDocNum
+                && firstSpans.start() < secondSpans.end()) {
+
+            if (firstSpans.end() + maxDistance > secondSpans.start())
+                candidateList.add(new CandidateSpan(firstSpans));
+
+            hasMoreFirstSpans = firstSpans.next();
+        }
+    }
+
+    @Override
+    protected boolean findMatch() throws IOException {
+        CandidateSpan candidateSpan = candidateList.get(candidateListIndex);
+        if (minDistance == 0
+                &&
+                // intersection
+                candidateSpan.getStart() < secondSpans.end()
+                && secondSpans.start() < candidateSpan.getEnd()) {
+
+            setMatchProperties(candidateSpan, true);
+            return true;
+        }
+
+        int actualDistance = secondSpans.start() - candidateSpan.getEnd() + 1;
+        if (candidateSpan.getStart() < secondSpans.start()
+                && minDistance <= actualDistance
+                && actualDistance <= maxDistance) {
+
+            setMatchProperties(candidateSpan, false);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public long cost() {
+        CandidateSpan candidateSpan = candidateList.get(candidateListIndex);
+        return candidateSpan.getCost() + secondSpans.cost();
+    }
+
+    @Override
+    protected boolean isSecondSpanValid() throws IOException {
+        return true;
+    }
 }
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/UnorderedDistanceSpans.java b/src/main/java/de/ids_mannheim/korap/query/spans/UnorderedDistanceSpans.java
index 7934f34..8f68c8d 100644
--- a/src/main/java/de/ids_mannheim/korap/query/spans/UnorderedDistanceSpans.java
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/UnorderedDistanceSpans.java
@@ -15,227 +15,283 @@
 
 import de.ids_mannheim.korap.query.SpanDistanceQuery;
 
-/** Enumeration of span matches, whose two child spans have a specific range 
- * 	of distance (within a min and a max distance) and can be in any order. 
- * 	
- * 	@author margaretha
+/**
+ * Enumeration of span matches, whose two child spans have a specific range of
+ * distance (within a minimum and a maximum distance) and can occur in any
+ * order.
+ * 
+ * @author margaretha
  * */
-public abstract class UnorderedDistanceSpans extends DistanceSpans{
+public abstract class UnorderedDistanceSpans extends DistanceSpans {
 
-	protected int minDistance, maxDistance;
-	protected boolean hasMoreFirstSpans, hasMoreSecondSpans;
-	protected List<CandidateSpan> firstSpanList, secondSpanList;	
-	protected List<CandidateSpan> matchList;
-	private long matchCost;
-	private int matchListSpanNum; 	
-	protected int currentDocNum;
+    protected int minDistance, maxDistance;
+    protected boolean hasMoreFirstSpans, hasMoreSecondSpans;
+    protected List<CandidateSpan> firstSpanList, secondSpanList;
+    protected List<CandidateSpan> matchList;
+    private long matchCost;
+    private int matchListSpanNum;
+    protected int currentDocNum;
 
-    // This advices the java compiler to ignore all loggings
-    public static final boolean DEBUG = false;
+    /**
+     * Constructs an UnorderedDistanceSpans for the given
+     * {@link SpanDistanceQuery}.
+     * 
+     * @param query a SpanDistanceQuery
+     * @param context
+     * @param acceptDocs
+     * @param termContexts
+     * @throws IOException
+     */
+    public UnorderedDistanceSpans(SpanDistanceQuery query,
+            AtomicReaderContext context, Bits acceptDocs,
+            Map<Term, TermContext> termContexts) throws IOException {
+        super(query, context, acceptDocs, termContexts);
+        minDistance = query.getMinDistance();
+        maxDistance = query.getMaxDistance();
 
-	public UnorderedDistanceSpans(SpanDistanceQuery query,
-			AtomicReaderContext context, Bits acceptDocs,
-			Map<Term, TermContext> termContexts) throws IOException {
-		super(query, context, acceptDocs, termContexts);
-		minDistance = query.getMinDistance();
-		maxDistance =  query.getMaxDistance();
-		
-		firstSpanList = new ArrayList<CandidateSpan>();
-		secondSpanList = new ArrayList<CandidateSpan>();
-		matchList = new ArrayList<CandidateSpan>();
-		
-		hasMoreFirstSpans = firstSpans.next();
-		hasMoreSecondSpans = secondSpans.next();
-		hasMoreSpans = hasMoreFirstSpans && hasMoreSecondSpans;
-	}
+        firstSpanList = new ArrayList<CandidateSpan>();
+        secondSpanList = new ArrayList<CandidateSpan>();
+        matchList = new ArrayList<CandidateSpan>();
 
-	@Override
-	protected boolean advance() throws IOException {		
-		while (hasMoreSpans || !matchList.isEmpty()){			
-			if (!matchList.isEmpty()){
-				setMatchProperties();				
-				return true;
-			} 			
-			if (prepareLists()) setMatchList();
-		}
-		return false;
-	}
-	
-	/** Add the next possible first and second spans. Both the spans must be in the
-	 * 	same document. In UnorderedElementDistanceSpans, a span that is not in 
-	 * 	an element, is not added to its candidate list. The element must also be in
-	 * 	the same document.
-	 *   
-	 * 	@return true iff at least one of the candidate lists can be filled.
-	 * */
-	protected abstract boolean prepareLists() throws IOException;
-	
-	/** Set the list of matches between the span having the smallest position, and 
-	 * 	its candidates. Simply remove the span if it does not have any candidates.
-	 * */
-	protected void setMatchList() throws IOException {
-		
-		hasMoreFirstSpans = setCandidateList(firstSpanList,firstSpans,
-				hasMoreFirstSpans,secondSpanList);
-		hasMoreSecondSpans = setCandidateList(secondSpanList,secondSpans,
-				hasMoreSecondSpans,firstSpanList);
-//		System.out.println("--------------------");
-//		System.out.println("firstSpanList:");
-//		for (CandidateSpan cs: firstSpanList) {
-//			System.out.println(cs.getStart() +" "+ cs.getEnd());
-//		}
-//		
-//		System.out.println("secondSpanList:");
-//		for (CandidateSpan cs: secondSpanList) {
-//			System.out.println(cs.getStart() +" "+ cs.getEnd());
-//		}
-		
-		CandidateSpan currentFirstSpan, currentSecondSpan;
-		if (!firstSpanList.isEmpty() && !secondSpanList.isEmpty()){
-			
-			currentFirstSpan = firstSpanList.get(0)	;
-			currentSecondSpan = secondSpanList.get(0);
-			
-			if (currentFirstSpan.getEnd() < currentSecondSpan.getEnd() ||					 
-					isLastCandidateSmaller(currentFirstSpan, currentSecondSpan)){
-			    if (DEBUG)
-				log.trace("current target: "+firstSpanList.get(0).getStart() +" "+firstSpanList.get(0).getEnd());				
-//				System.out.println("candidates:");
-//				for (CandidateSpan cs: secondSpanList) {
-//					System.out.println(cs.getStart() +" "+ cs.getEnd());
-//				}
-				
-				matchList = findMatches(currentFirstSpan, secondSpanList);
-				setMatchFirstSpan(currentFirstSpan);
-				matchListSpanNum = 2;
-				updateList(firstSpanList);
-			}			
-			else {
-			    if (DEBUG)
-				log.trace("current target: "+secondSpanList.get(0).getStart() +" "+secondSpanList.get(0).getEnd());
-//				System.out.println("candidates:");
-//				for (CandidateSpan cs: firstSpanList) {
-//					System.out.println(cs.getStart() +" "+ cs.getEnd());
-//				}
-				
-				matchList = findMatches(currentSecondSpan, firstSpanList);
-				setMatchSecondSpan(currentSecondSpan);
-				matchListSpanNum = 1;
-				updateList(secondSpanList);
-			}
-		}
-		else if (firstSpanList.isEmpty()){
-		    if (DEBUG) {
-			log.trace("current target: "+secondSpanList.get(0).getStart() +" "+secondSpanList.get(0).getEnd());
-			log.trace("candidates: empty");
-		    };
-			updateList(secondSpanList);			
-		}
-		else{
-		    if (DEBUG) {
-			log.trace("current target: "+firstSpanList.get(0).getStart() +" "+firstSpanList.get(0).getEnd());
-			log.trace("candidates: empty");
-		    };
-			updateList(firstSpanList);			
-		}
-	}
-	
-	private boolean isLastCandidateSmaller(CandidateSpan currentFirstSpan, CandidateSpan 
-			currentSecondSpan){
-		if (currentFirstSpan.getEnd() == currentSecondSpan.getEnd() ){
-			int secondEnd = secondSpanList.get(secondSpanList.size()-1).getEnd();
-			int firstEnd = firstSpanList.get(firstSpanList.size()-1).getEnd();
-			return (secondEnd < firstEnd ? true : false);
-		}
-		
-		return false;
-	}
-	
-	protected abstract void updateList(List<CandidateSpan> candidateList);
-	
-	/** Set the candidate list for the first element in the target list.
-	 * @return true iff the spans enumeration still has a next element 
-	 * to be a candidate
-	 */
-	protected abstract boolean setCandidateList(List<CandidateSpan> 
-			candidateList, Spans candidate, boolean hasMoreCandidates,
-			List<CandidateSpan> targetList) throws IOException;
-	
-	/** Search all matches between the target span and its candidates in the candidate 
-	 * 	list.
-	 * 	@return the matches in a list 
-	 * */
-	protected abstract List<CandidateSpan> findMatches(CandidateSpan target, 
-			List<CandidateSpan> candidateList);
-	
-	/** Compute match properties and create a candidate span match 
-	 * 	to be added to the match list.
-	 * 	@return a candidate span match 
-	 * */
-	protected CandidateSpan createMatchCandidate(CandidateSpan target,
-			CandidateSpan cs, boolean isDistanceZero) {
-		
-		int start = Math.min(target.getStart(), cs.getStart());
-		int end = Math.max(target.getEnd(),cs.getEnd());
-		int doc = target.getDoc();
-		long cost = target.getCost() + cs.getCost();
-		
-		Collection<byte[]> payloads = new LinkedList<byte[]>();
-		if (collectPayloads) {
-			if (target.getPayloads() != null){
-				payloads.addAll(target.getPayloads());
-			}
-			if (cs.getPayloads() != null){
-				payloads.addAll(cs.getPayloads());
-			}
-		}
-		
-		CandidateSpan match = new CandidateSpan(start,end,doc,cost,payloads);
-		match.setChildSpan(cs);
-		return match;
-	}
+        hasMoreFirstSpans = firstSpans.next();
+        hasMoreSecondSpans = secondSpans.next();
+        hasMoreSpans = hasMoreFirstSpans && hasMoreSecondSpans;
+    }
 
-	/** Assign the first candidate span in the match list as the current span match.
-	 * */
-	private void setMatchProperties() {
-		CandidateSpan cs = matchList.get(0);
-		matchDocNumber = cs.getDoc();
-		matchStartPosition = cs.getStart();
-		matchEndPosition = cs.getEnd();
-		matchCost = cs.getCost();
-		matchPayload.addAll(cs.getPayloads());
-		matchList.remove(0);
-		
-		if (matchListSpanNum == 1)
-			setMatchFirstSpan(cs.getChildSpan());
-		else setMatchSecondSpan(cs.getChildSpan());
+    @Override
+    protected boolean advance() throws IOException {
+        while (hasMoreSpans || !matchList.isEmpty()) {
+            if (!matchList.isEmpty()) {
+                setMatchProperties();
+                return true;
+            }
+            if (prepareLists())
+                setMatchList();
+        }
+        return false;
+    }
 
-		if (DEBUG) {
-		    log.trace("Match doc#={} start={} end={}",matchDocNumber,matchStartPosition,matchEndPosition);
-		    log.trace("firstspan "+getMatchFirstSpan().getStart()+" "+ getMatchFirstSpan().getEnd());
-		    log.trace("secondspan "+getMatchSecondSpan().getStart()+" "+ getMatchSecondSpan().getEnd());
-		};
-	}
+    /**
+     * Updates the firstSpanList and secondSpanList by adding the next possible
+     * first and second spans. Both the spans must be in the same document. In
+     * UnorderedElementDistanceSpans, a span that is not in an element (distance
+     * unit), is not added to its candidate list. The element must also be in
+     * the same document.
+     * 
+     * @return <code>true</code> if at least one of the candidate lists can be
+     *         filled, <code>false</code> otherwise.
+     * @throws IOException
+     */
+    protected abstract boolean prepareLists() throws IOException;
 
-	@Override
-	public boolean skipTo(int target) throws IOException {
-		if (hasMoreSpans && (secondSpans.doc() < target)){
-  			if (!secondSpans.skipTo(target)){
-  				hasMoreSpans = false;
-  				return false;
-  			}  			
-  		}
-		
-		firstSpanList.clear();
-		secondSpanList.clear();
-		matchPayload.clear();
-		isStartEnumeration=false;
-		return advance();		
-	}
+    /**
+     * Sets the list of matches for the span having the smallest position (i.e.
+     * between the first and the second spans), and its candidates (i.e. its
+     * counterparts). The candidates also must have smaller positions. Simply
+     * remove the span if it does not have any candidates.
+     * 
+     * @throws IOException
+     */
+    protected void setMatchList() throws IOException {
 
-	@Override
-	public long cost() {
-		return matchCost;
-	}
+        hasMoreFirstSpans = setCandidateList(firstSpanList, firstSpans,
+                hasMoreFirstSpans, secondSpanList);
+        hasMoreSecondSpans = setCandidateList(secondSpanList, secondSpans,
+                hasMoreSecondSpans, firstSpanList);
+        // System.out.println("--------------------");
+        // System.out.println("firstSpanList:");
+        // for (CandidateSpan cs: firstSpanList) {
+        // System.out.println(cs.getStart() +" "+ cs.getEnd());
+        // }
+        //
+        // System.out.println("secondSpanList:");
+        // for (CandidateSpan cs: secondSpanList) {
+        // System.out.println(cs.getStart() +" "+ cs.getEnd());
+        // }
+
+        CandidateSpan currentFirstSpan, currentSecondSpan;
+        if (!firstSpanList.isEmpty() && !secondSpanList.isEmpty()) {
+
+            currentFirstSpan = firstSpanList.get(0);
+            currentSecondSpan = secondSpanList.get(0);
+
+            if (currentFirstSpan.getEnd() < currentSecondSpan.getEnd()
+                    || isLastCandidateSmaller(currentFirstSpan,
+                            currentSecondSpan)) {
+                // log.trace("current target: "
+                // + firstSpanList.get(0).getStart() + " "
+                // + firstSpanList.get(0).getEnd());
+                // System.out.println("candidates:");
+                // for (CandidateSpan cs: secondSpanList) {
+                // System.out.println(cs.getStart() +" "+ cs.getEnd());
+                // }
+
+                matchList = findMatches(currentFirstSpan, secondSpanList);
+                setMatchFirstSpan(currentFirstSpan);
+                matchListSpanNum = 2;
+                updateList(firstSpanList);
+            } else {
+                // log.trace("current target: "
+                // + secondSpanList.get(0).getStart() + " "
+                // + secondSpanList.get(0).getEnd());
+                // System.out.println("candidates:");
+                // for (CandidateSpan cs: firstSpanList) {
+                // System.out.println(cs.getStart() +" "+ cs.getEnd());
+                // }
+
+                matchList = findMatches(currentSecondSpan, firstSpanList);
+                setMatchSecondSpan(currentSecondSpan);
+                matchListSpanNum = 1;
+                updateList(secondSpanList);
+            }
+        } else if (firstSpanList.isEmpty()) {
+            // log.trace("current target: " + secondSpanList.get(0).getStart()
+            // + " " + secondSpanList.get(0).getEnd());
+            // log.trace("candidates: empty");
+            updateList(secondSpanList);
+        } else {
+            // log.trace("current target: " + firstSpanList.get(0).getStart()
+            // + " " + firstSpanList.get(0).getEnd());
+            // log.trace("candidates: empty");
+            updateList(firstSpanList);
+        }
+    }
+
+    /**
+     * Tells if the last candidate from the secondSpanList has a smaller end
+     * position than the end position of the the last candidate from the
+     * firstSpanList.
+     * 
+     * @param currentFirstSpan the current firstspan
+     * @param currentSecondSpan the current secondspan
+     * @return <code>true</code> if the end position of the last candidate from
+     *         the secondSpanList is smaller than that from the firstSpanList,
+     *         <code>false</code> otherwise.
+     */
+    private boolean isLastCandidateSmaller(CandidateSpan currentFirstSpan,
+            CandidateSpan currentSecondSpan) {
+        if (currentFirstSpan.getEnd() == currentSecondSpan.getEnd()) {
+            int secondEnd = secondSpanList.get(secondSpanList.size() - 1)
+                    .getEnd();
+            int firstEnd = firstSpanList.get(firstSpanList.size() - 1).getEnd();
+            return (secondEnd < firstEnd ? true : false);
+        }
+
+        return false;
+    }
+
+    /**
+     * Performs an update based on the given candidateList. In
+     * {@link UnorderedTokenDistanceSpans}, the first candidate in the
+     * candidateList is simply removed. In {@link UnorderedElementDistanceSpans}
+     * , the elementList is also updated.
+     * 
+     * @param candidateList a candidateList
+     */
+    protected abstract void updateList(List<CandidateSpan> candidateList);
+
+    /**
+     * Sets the candidate list for the first element in the target list and
+     * tells if the the specified spans has finished or not.
+     * 
+     * @param candidateList a list of candidate spans
+     * @param candidate a Spans
+     * @param hasMoreCandidates a boolean
+     * @param targetList a list of target spans
+     * @return <code>true</code> if the span enumeration still has a next
+     *         element to be a candidate, <code>false</code> otherwise.
+     * @throws IOException
+     */
+    protected abstract boolean setCandidateList(
+            List<CandidateSpan> candidateList, Spans candidate,
+            boolean hasMoreCandidates, List<CandidateSpan> targetList)
+            throws IOException;
+
+    /**
+     * Finds all matches between the target span and its candidates in the
+     * candidate list.
+     * 
+     * @param target a target span
+     * @param candidateList a candidate list
+     * @return the matches in a list
+     */
+    protected abstract List<CandidateSpan> findMatches(CandidateSpan target,
+            List<CandidateSpan> candidateList);
+
+    /**
+     * Computes match properties and creates a candidate span match to be added
+     * to the match list.
+     * 
+     * @return a candidate span match
+     * */
+    protected CandidateSpan createMatchCandidate(CandidateSpan target,
+            CandidateSpan cs, boolean isDistanceZero) {
+
+        int start = Math.min(target.getStart(), cs.getStart());
+        int end = Math.max(target.getEnd(), cs.getEnd());
+        int doc = target.getDoc();
+        long cost = target.getCost() + cs.getCost();
+
+        Collection<byte[]> payloads = new LinkedList<byte[]>();
+        if (collectPayloads) {
+            if (target.getPayloads() != null) {
+                payloads.addAll(target.getPayloads());
+            }
+            if (cs.getPayloads() != null) {
+                payloads.addAll(cs.getPayloads());
+            }
+        }
+
+        CandidateSpan match = new CandidateSpan(start, end, doc, cost, payloads);
+        match.setChildSpan(cs);
+        return match;
+    }
+
+    /**
+     * Assigns the first candidate span in the match list as the current span
+     * match, and removes it from the matchList.
+     * */
+    private void setMatchProperties() {
+        CandidateSpan cs = matchList.get(0);
+        matchDocNumber = cs.getDoc();
+        matchStartPosition = cs.getStart();
+        matchEndPosition = cs.getEnd();
+        matchCost = cs.getCost();
+        matchPayload.addAll(cs.getPayloads());
+        matchList.remove(0);
+
+        if (matchListSpanNum == 1)
+            setMatchFirstSpan(cs.getChildSpan());
+        else
+            setMatchSecondSpan(cs.getChildSpan());
+
+        // log.trace("Match doc#={} start={} end={}", matchDocNumber,
+        // matchStartPosition, matchEndPosition);
+        // log.trace("firstspan " + getMatchFirstSpan().getStart() + " "
+        // + getMatchFirstSpan().getEnd());
+        // log.trace("secondspan " + getMatchSecondSpan().getStart() + " "
+        // + getMatchSecondSpan().getEnd());
+    }
+
+    @Override
+    public boolean skipTo(int target) throws IOException {
+        if (hasMoreSpans && (secondSpans.doc() < target)) {
+            if (!secondSpans.skipTo(target)) {
+                hasMoreSpans = false;
+                return false;
+            }
+        }
+
+        firstSpanList.clear();
+        secondSpanList.clear();
+        matchPayload.clear();
+        isStartEnumeration = false;
+        return advance();
+    }
+
+    @Override
+    public long cost() {
+        return matchCost;
+    }
 
 }
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/UnorderedElementDistanceSpans.java b/src/main/java/de/ids_mannheim/korap/query/spans/UnorderedElementDistanceSpans.java
index a0c4474..102b5c9 100644
--- a/src/main/java/de/ids_mannheim/korap/query/spans/UnorderedElementDistanceSpans.java
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/UnorderedElementDistanceSpans.java
@@ -14,213 +14,248 @@
 
 import de.ids_mannheim.korap.query.SpanDistanceQuery;
 
-/**	Enumeration of span matches, whose two child spans have a specific range 
- * 	of distance (within a min and a max distance) and can be in any order. 
- * 	The unit distance is an element position, where element can be a sentence 
- * 	or a paragraph.
+/**
+ * Enumeration of span matches, whose two child spans have a specific range of
+ * distance (within a min and a max distance) and can be in any order. The unit
+ * distance is an element, which can be a sentence or a paragraph for instance.
+ * The distance is the difference between the positions of elements containing
+ * the spans.
  * 
  * @author margaretha
  * */
-public class UnorderedElementDistanceSpans extends UnorderedDistanceSpans{
-	
-	private Spans elements;	
-	private boolean hasMoreElements;
-	private int elementPosition;
-	
-	// contains all previous elements whose position is greater than the last 
-	// target span
-	private List<CandidateSpan> elementList;
-	
-	public UnorderedElementDistanceSpans(SpanDistanceQuery query,
-			AtomicReaderContext context, Bits acceptDocs,
-			Map<Term, TermContext> termContexts) throws IOException {
-		super(query, context, acceptDocs, termContexts);		
-		elements = query.getElementQuery().
-	  			getSpans(context, acceptDocs, termContexts);	  		
-  		hasMoreElements = elements.next();  		
-  		elementPosition=0;  		
-  		elementList = new ArrayList<CandidateSpan>();  		
-	}
-	
-	@Override
-	protected boolean prepareLists() throws IOException {
-		
-		if (firstSpanList.isEmpty() && secondSpanList.isEmpty()){			
-			if (hasMoreFirstSpans && hasMoreSecondSpans && hasMoreElements &&
-					findSameDoc(firstSpans, secondSpans, elements)){				
-				
-				if (currentDocNum != firstSpans.doc()){
-					currentDocNum = firstSpans.doc();
-					elementList.clear();
-				}				
-								
-				hasMoreFirstSpans = addSpan(firstSpans,firstSpanList,hasMoreFirstSpans);
-				hasMoreSecondSpans = addSpan(secondSpans, secondSpanList, hasMoreSecondSpans);												
-			}
-			else {
-				hasMoreSpans = false;
-				return false;
-			}
-		}		
-		else if (firstSpanList.isEmpty() && hasMoreFirstSpans && 
-				firstSpans.doc() == currentDocNum){
-			hasMoreFirstSpans = addSpan(firstSpans,firstSpanList,hasMoreFirstSpans);
-		}
-		else if (secondSpanList.isEmpty() && hasMoreSecondSpans && 
-				secondSpans.doc() == currentDocNum){
-			hasMoreSecondSpans = addSpan(secondSpans, secondSpanList, hasMoreSecondSpans);
-		}
-		
-		return true;
-	}
-	
-	private boolean addSpan(Spans span, List<CandidateSpan> list, boolean hasMoreSpan) 
-			throws IOException {
-		int position;
-		while (hasMoreSpan && span.doc() == currentDocNum){
-			position = findElementPosition(span);
-			if (position != -1){
-				list.add(new CandidateSpan(span,position));
-				hasMoreSpan = span.next();
-				return hasMoreSpan;
-			}
-			hasMoreSpan = span.next();
-		}
-		return hasMoreSpan;
-	}
-	
-	
-	/** Find the element position of the span in the element list or by advancing 
-	 * 	the element spans until encountering the span.
-	 * 
-	 * 	@return the element position
-	 * */
-	private int findElementPosition(Spans span) throws IOException {
-		// Check in the element list
-		if (!elementList.isEmpty() && 
-				span.end() <= elementList.get(elementList.size()-1).getEnd()){
-			
-			for (CandidateSpan e : elementList)
-				if (e.getEnd() >= span.end() && e.getStart() <= span.start()){
-					return e.getPosition();
-				}
-			return -1; // The span is not in an element.
-		}
-		
-		return ( advanceElementTo(span) ? elementPosition : -1 );			
-	}
-		
-	/** Advance the element spans until encountering the given span.
-	 * 	
-	 * @return true iff such an element is found, false if the span is not in 
-	 * 	an element.
-	 * */
-	private boolean advanceElementTo(Spans span) throws IOException {
-		while (hasMoreElements && 
-				elements.doc() == currentDocNum &&
-				elements.start() < span.end()){
-			
-			if (span.start() >= elements.start() &&
-					span.end() <= elements.end()){
-				return true;
-			}
-			elementList.add(new CandidateSpan(elements,elementPosition));
-			hasMoreElements = elements.next();
-			elementPosition++;			
-		}
-		
-		return false; // invalid
-	}
+public class UnorderedElementDistanceSpans extends UnorderedDistanceSpans {
 
-	@Override
-	protected boolean setCandidateList(List<CandidateSpan> 
-			candidateList, Spans candidate, boolean hasMoreCandidates,
-			List<CandidateSpan> targetList) throws IOException {
-		 
-		if (!targetList.isEmpty()){			
-			CandidateSpan cs;
-			CandidateSpan target = targetList.get(0);
-			int position;
-			while (hasMoreCandidates && candidate.doc() == target.getDoc()){
-				position = findElementPosition(candidate); 
-				if (position != -1){
-					cs = new CandidateSpan(candidate,position);
-					
-					if (isWithinMaxDistance(target, cs)){
-						candidateList.add(cs);						
-					}
-					else break;
-				}
-				hasMoreCandidates = candidate.next();
-			}
-		}
-		return hasMoreCandidates;
-	}
-	
-	/** Check if the target and candidate spans are not too far from each other.
-	 *  
-	 *  @return true iff the target and candidate spans are within the maximum
-	 *  distance
-	 * */
-	protected boolean isWithinMaxDistance(CandidateSpan target, CandidateSpan candidate) {
-		int candidatePos = candidate.getPosition();
-		int targetPos = target.getPosition();
-		
-		// left candidate
-		if (candidatePos < targetPos && 
-				candidatePos + maxDistance < targetPos){
-			return false;
-		}
-		// right candidate
-		if (candidatePos > targetPos &&
-				targetPos + maxDistance < candidatePos){
-			return false;
-		}
-		return true;
-	}
-	
-	@Override
-	protected List<CandidateSpan> findMatches(CandidateSpan target, List<CandidateSpan> 
-			candidateList) {
-		
-		List<CandidateSpan> matches = new ArrayList<>();
-		
-		int actualDistance;
-		int targetPos = target.getPosition();		
-		
-		for (CandidateSpan cs : candidateList){						
-			actualDistance = Math.abs( targetPos - cs.getPosition() );
-			
-			if (minDistance == 0 && actualDistance == 0){
-				matches.add(createMatchCandidate(target,cs,true));
-				continue;
-			}
-			 
-			if (minDistance <= actualDistance && actualDistance <= maxDistance)
-				matches.add(createMatchCandidate(target, cs, false));
-		}		
-		return matches;
-	}
+    private Spans elements;
+    private boolean hasMoreElements;
+    private int elementPosition;
 
-	@Override
-	protected void updateList(List<CandidateSpan> candidateList) {
-		updateElementList(candidateList.get(0).getPosition());
-		candidateList.remove(0);
-	}
-	
-	/** Reduce the number of elements kept in the element list by removing the element
-	 * 	whose position is smaller than or identical to the position of the last target 
-	 * 	span.
-	 * */
-	private void updateElementList(int position){
-		Iterator<CandidateSpan> i = elementList.iterator();
-		CandidateSpan e;
-		while(i.hasNext()){
-			e = i.next();			
-			if (e.getPosition() <= position) {
-				i.remove();
-			}
-			break;
-		}
-	}
+    // contains all previous elements whose position is greater than the last
+    // target span
+    private List<CandidateSpan> elementList;
+
+    /**
+     * Constructs an UnorderedElementDistanceSpans for the given
+     * {@link SpanDistanceQuery}.
+     * 
+     * @param query a SpanDistanceQuery
+     * @param context
+     * @param acceptDocs
+     * @param termContexts
+     * @throws IOException
+     */
+    public UnorderedElementDistanceSpans(SpanDistanceQuery query,
+            AtomicReaderContext context, Bits acceptDocs,
+            Map<Term, TermContext> termContexts) throws IOException {
+        super(query, context, acceptDocs, termContexts);
+        elements = query.getElementQuery().getSpans(context, acceptDocs,
+                termContexts);
+        hasMoreElements = elements.next();
+        elementPosition = 0;
+        elementList = new ArrayList<CandidateSpan>();
+    }
+
+    @Override
+    protected boolean prepareLists() throws IOException {
+
+        if (firstSpanList.isEmpty() && secondSpanList.isEmpty()) {
+            if (hasMoreFirstSpans && hasMoreSecondSpans && hasMoreElements
+                    && findSameDoc(firstSpans, secondSpans, elements)) {
+
+                if (currentDocNum != firstSpans.doc()) {
+                    currentDocNum = firstSpans.doc();
+                    elementList.clear();
+                }
+
+                hasMoreFirstSpans = addSpan(firstSpans, firstSpanList,
+                        hasMoreFirstSpans);
+                hasMoreSecondSpans = addSpan(secondSpans, secondSpanList,
+                        hasMoreSecondSpans);
+            } else {
+                hasMoreSpans = false;
+                return false;
+            }
+        } else if (firstSpanList.isEmpty() && hasMoreFirstSpans
+                && firstSpans.doc() == currentDocNum) {
+            hasMoreFirstSpans = addSpan(firstSpans, firstSpanList,
+                    hasMoreFirstSpans);
+        } else if (secondSpanList.isEmpty() && hasMoreSecondSpans
+                && secondSpans.doc() == currentDocNum) {
+            hasMoreSecondSpans = addSpan(secondSpans, secondSpanList,
+                    hasMoreSecondSpans);
+        }
+
+        return true;
+    }
+
+    /**
+     * Adds all the spans occurring in the current document, as CandidateSpans
+     * to the specified candidate list, and tells if the enumeration of the
+     * spans has finished, or not.
+     * 
+     * @param span a Span
+     * @param list a candidateList
+     * @param hasMoreSpan a boolean describing if the span enumeration has
+     *        finished or not.
+     * @return <code>true</code> if the the span enumeration has finished,
+     *         <code>false</code> otherwise.
+     * @throws IOException
+     */
+    private boolean addSpan(Spans span, List<CandidateSpan> list,
+            boolean hasMoreSpan) throws IOException {
+        int position;
+        while (hasMoreSpan && span.doc() == currentDocNum) {
+            position = findElementPosition(span);
+            if (position != -1) {
+                list.add(new CandidateSpan(span, position));
+                hasMoreSpan = span.next();
+                return hasMoreSpan;
+            }
+            hasMoreSpan = span.next();
+        }
+        return hasMoreSpan;
+    }
+
+    /**
+     * Finds the element position of the specified span in the element list or
+     * by advancing the element spans until encountering the span.
+     * 
+     * @param span a Span
+     * @return the element position
+     * @throws IOException
+     */
+    private int findElementPosition(Spans span) throws IOException {
+        // Check in the element list
+        if (!elementList.isEmpty()
+                && span.end() <= elementList.get(elementList.size() - 1)
+                        .getEnd()) {
+
+            for (CandidateSpan e : elementList)
+                if (e.getEnd() >= span.end() && e.getStart() <= span.start()) {
+                    return e.getPosition();
+                }
+            return -1; // The span is not in an element.
+        }
+
+        return (advanceElementTo(span) ? elementPosition : -1);
+    }
+
+    /**
+     * Advances the element spans until encountering the given span.
+     * 
+     * @param span
+     * @return <code>true</code> if such an element is found, <code>false</code>
+     *         if the span is not in an element.
+     * @throws IOException
+     */
+    private boolean advanceElementTo(Spans span) throws IOException {
+        while (hasMoreElements && elements.doc() == currentDocNum
+                && elements.start() < span.end()) {
+
+            if (span.start() >= elements.start()
+                    && span.end() <= elements.end()) {
+                return true;
+            }
+            elementList.add(new CandidateSpan(elements, elementPosition));
+            hasMoreElements = elements.next();
+            elementPosition++;
+        }
+
+        return false; // invalid
+    }
+
+    @Override
+    protected boolean setCandidateList(List<CandidateSpan> candidateList,
+            Spans candidate, boolean hasMoreCandidates,
+            List<CandidateSpan> targetList) throws IOException {
+
+        if (!targetList.isEmpty()) {
+            CandidateSpan cs;
+            CandidateSpan target = targetList.get(0);
+            int position;
+            while (hasMoreCandidates && candidate.doc() == target.getDoc()) {
+                position = findElementPosition(candidate);
+                if (position != -1) {
+                    cs = new CandidateSpan(candidate, position);
+
+                    if (isWithinMaxDistance(target, cs)) {
+                        candidateList.add(cs);
+                    } else
+                        break;
+                }
+                hasMoreCandidates = candidate.next();
+            }
+        }
+        return hasMoreCandidates;
+    }
+
+    /**
+     * Tells if the target and candidate spans are not too far from each other
+     * (within the maximum distance).
+     * 
+     * @return <code>true</code> if the target and candidate spans are within
+     *         the maximum distance, <code>false</code> otherwise.
+     * */
+    protected boolean isWithinMaxDistance(CandidateSpan target,
+            CandidateSpan candidate) {
+        int candidatePos = candidate.getPosition();
+        int targetPos = target.getPosition();
+
+        // left candidate
+        if (candidatePos < targetPos && candidatePos + maxDistance < targetPos) {
+            return false;
+        }
+        // right candidate
+        if (candidatePos > targetPos && targetPos + maxDistance < candidatePos) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    protected List<CandidateSpan> findMatches(CandidateSpan target,
+            List<CandidateSpan> candidateList) {
+
+        List<CandidateSpan> matches = new ArrayList<>();
+
+        int actualDistance;
+        int targetPos = target.getPosition();
+
+        for (CandidateSpan cs : candidateList) {
+            actualDistance = Math.abs(targetPos - cs.getPosition());
+
+            if (minDistance == 0 && actualDistance == 0) {
+                matches.add(createMatchCandidate(target, cs, true));
+                continue;
+            }
+
+            if (minDistance <= actualDistance && actualDistance <= maxDistance)
+                matches.add(createMatchCandidate(target, cs, false));
+        }
+        return matches;
+    }
+
+    @Override
+    protected void updateList(List<CandidateSpan> candidateList) {
+        updateElementList(candidateList.get(0).getPosition());
+        candidateList.remove(0);
+    }
+
+    /**
+     * Reduces the number of elements kept in the element list by removing the
+     * elements whose position is smaller than or identical to the position of
+     * the last target span.
+     * 
+     * @param position the last target span position
+     */
+    private void updateElementList(int position) {
+        Iterator<CandidateSpan> i = elementList.iterator();
+        CandidateSpan e;
+        while (i.hasNext()) {
+            e = i.next();
+            if (e.getPosition() <= position) {
+                i.remove();
+            }
+            break;
+        }
+    }
 }
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/UnorderedTokenDistanceSpans.java b/src/main/java/de/ids_mannheim/korap/query/spans/UnorderedTokenDistanceSpans.java
index 5f1abd9..bc9f0e5 100644
--- a/src/main/java/de/ids_mannheim/korap/query/spans/UnorderedTokenDistanceSpans.java
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/UnorderedTokenDistanceSpans.java
@@ -13,114 +13,129 @@
 
 import de.ids_mannheim.korap.query.SpanDistanceQuery;
 
-/** Enumeration of span matches, whose two child spans have a specific range 
- * 	of distance (within a min and a max distance) and can be in any order. 
- * 	The unit distance is a token position.
+/**
+ * Enumeration of span matches, whose two child spans have a specific range of
+ * distance (within a min and a max distance) and can be in any order. The unit
+ * distance is a token position.
  * 
  * @author margaretha
  * */
-public class UnorderedTokenDistanceSpans extends UnorderedDistanceSpans{
+public class UnorderedTokenDistanceSpans extends UnorderedDistanceSpans {
 
-	public UnorderedTokenDistanceSpans(SpanDistanceQuery query,
-			AtomicReaderContext context, Bits acceptDocs,
-			Map<Term, TermContext> termContexts) throws IOException {
-		super(query, context, acceptDocs, termContexts);		
-	}
-	
-	@Override
-	protected boolean prepareLists() throws IOException {		
-		
-		if (firstSpanList.isEmpty() && secondSpanList.isEmpty()){
-			if (hasMoreFirstSpans && hasMoreSecondSpans &&
-					ensureSameDoc(firstSpans, secondSpans)){			
-				firstSpanList.add(new CandidateSpan(firstSpans));
-				secondSpanList.add(new CandidateSpan(secondSpans));
-				currentDocNum = firstSpans.doc();
-				hasMoreFirstSpans = firstSpans.next();
-				hasMoreSecondSpans = secondSpans.next();				
-			}
-			else { 
-				hasMoreSpans = false;
-				return false;
-			}
-		}
-		else if (firstSpanList.isEmpty() && hasMoreFirstSpans && 
-				firstSpans.doc() == currentDocNum){
-			firstSpanList.add(new CandidateSpan(firstSpans));
-			hasMoreFirstSpans = firstSpans.next();
-		}
-		else if (secondSpanList.isEmpty() && hasMoreSecondSpans && 
-				secondSpans.doc() == currentDocNum){
-			secondSpanList.add(new CandidateSpan(secondSpans));
-			hasMoreSecondSpans = secondSpans.next();
-		}
-		return true;
-	}
-	
-	@Override
-	protected boolean setCandidateList(List<CandidateSpan> 
-			candidateList, Spans candidate, boolean hasMoreCandidates,
-			List<CandidateSpan> targetList) throws IOException {
-		
-		if (!targetList.isEmpty()){
-			CandidateSpan target = targetList.get(0);
-			while (hasMoreCandidates && candidate.doc() == target.getDoc()
-					&& isWithinMaxDistance(target,candidate)){
-				candidateList.add(new CandidateSpan(candidate));
-				hasMoreCandidates = candidate.next();
-			}
-		}
-		return hasMoreCandidates;
-	}
-	
-	/** Check if the target and candidate spans are not too far from each other.
-	 *  @return true iff the target and candidate spans are within the maximum
-	 *  distance
-	 * */
-	protected boolean isWithinMaxDistance(CandidateSpan target, Spans candidate) {
-		// left candidate
-		if (candidate.end() < target.getStart() && 
-				candidate.end() + maxDistance <= target.getStart()){
-			return false;
-		}
-		// right candidate
-		if (candidate.start() > target.getEnd() &&
-				target.getEnd() + maxDistance <= candidate.start()){
-			return false;
-		}
-		return true;
-	}
-	
-	@Override	
-	protected List<CandidateSpan> findMatches(CandidateSpan target, List<CandidateSpan> 
-			candidateList) {
-		
-		List<CandidateSpan> matches = new ArrayList<>();		
-		int actualDistance;
-		for (CandidateSpan cs : candidateList){
-			if (minDistance == 0 &&
-					//intersection
-					target.getStart() < cs.getEnd() &&
-					cs.getStart() < target.getEnd()){
-				matches.add(createMatchCandidate(target,cs,true));
-				continue;
-			}
-			
-			// left candidate
-			if (cs.getEnd() < target.getStart())
-				actualDistance = target.getStart() - cs.getEnd() +1;			 
-			else // right candidate
-				actualDistance = cs.getStart() - target.getEnd() +1;
-			 
-			if (minDistance <= actualDistance && actualDistance <= maxDistance)
-				matches.add(createMatchCandidate(target, cs, false));			
-		}		
-		return matches;
-	}
+    /**
+     * Constructs an UnorderedTokenDistanceSpans for the given
+     * SpanDistanceQuery.
+     * 
+     * @param query a SpanDistanceQuery
+     * @param context
+     * @param acceptDocs
+     * @param termContexts
+     * @throws IOException
+     */
+    public UnorderedTokenDistanceSpans(SpanDistanceQuery query,
+            AtomicReaderContext context, Bits acceptDocs,
+            Map<Term, TermContext> termContexts) throws IOException {
+        super(query, context, acceptDocs, termContexts);
+    }
 
-	@Override
-	protected void updateList(List<CandidateSpan> candidateList) {
-		candidateList.remove(0);		
-	}
+    @Override
+    protected boolean prepareLists() throws IOException {
+
+        if (firstSpanList.isEmpty() && secondSpanList.isEmpty()) {
+            if (hasMoreFirstSpans && hasMoreSecondSpans
+                    && ensureSameDoc(firstSpans, secondSpans)) {
+                firstSpanList.add(new CandidateSpan(firstSpans));
+                secondSpanList.add(new CandidateSpan(secondSpans));
+                currentDocNum = firstSpans.doc();
+                hasMoreFirstSpans = firstSpans.next();
+                hasMoreSecondSpans = secondSpans.next();
+            } else {
+                hasMoreSpans = false;
+                return false;
+            }
+        } else if (firstSpanList.isEmpty() && hasMoreFirstSpans
+                && firstSpans.doc() == currentDocNum) {
+            firstSpanList.add(new CandidateSpan(firstSpans));
+            hasMoreFirstSpans = firstSpans.next();
+        } else if (secondSpanList.isEmpty() && hasMoreSecondSpans
+                && secondSpans.doc() == currentDocNum) {
+            secondSpanList.add(new CandidateSpan(secondSpans));
+            hasMoreSecondSpans = secondSpans.next();
+        }
+        return true;
+    }
+
+    @Override
+    protected boolean setCandidateList(List<CandidateSpan> candidateList,
+            Spans candidate, boolean hasMoreCandidates,
+            List<CandidateSpan> targetList) throws IOException {
+
+        if (!targetList.isEmpty()) {
+            CandidateSpan target = targetList.get(0);
+            while (hasMoreCandidates && candidate.doc() == target.getDoc()
+                    && isWithinMaxDistance(target, candidate)) {
+                candidateList.add(new CandidateSpan(candidate));
+                hasMoreCandidates = candidate.next();
+            }
+        }
+        return hasMoreCandidates;
+    }
+
+    /**
+     * Tells if the target and candidate spans are not too far from each other
+     * (within the maximum distance).
+     * 
+     * @param target a target span
+     * @param candidate a candidate span
+     * @return <code>true</code> if the target and candidate spans are within
+     *         the maximum distance, <code>false</code> otherwise.
+     */
+    protected boolean isWithinMaxDistance(CandidateSpan target, Spans candidate) {
+        // left candidate
+        if (candidate.end() < target.getStart()
+                && candidate.end() + maxDistance <= target.getStart()) {
+            return false;
+        }
+        // right candidate
+        if (candidate.start() > target.getEnd()
+                && target.getEnd() + maxDistance <= candidate.start()) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    protected List<CandidateSpan> findMatches(CandidateSpan target,
+            List<CandidateSpan> candidateList) {
+
+        List<CandidateSpan> matches = new ArrayList<>();
+        int actualDistance;
+        for (CandidateSpan cs : candidateList) {
+            if (minDistance == 0
+                    &&
+                    // intersection
+                    target.getStart() < cs.getEnd()
+                    && cs.getStart() < target.getEnd()) {
+                matches.add(createMatchCandidate(target, cs, true));
+                continue;
+            }
+
+            // left candidate
+            if (cs.getEnd() < target.getStart())
+                actualDistance = target.getStart() - cs.getEnd() + 1;
+            else
+                // right candidate
+                actualDistance = cs.getStart() - target.getEnd() + 1;
+
+            if (minDistance <= actualDistance && actualDistance <= maxDistance)
+                matches.add(createMatchCandidate(target, cs, false));
+        }
+        return matches;
+    }
+
+    @Override
+    protected void updateList(List<CandidateSpan> candidateList) {
+        candidateList.remove(0);
+    }
 
 }