merge trunk

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene2878@1645537 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dev-tools/idea/lucene/highlighter/highlighter.iml b/dev-tools/idea/lucene/highlighter/highlighter.iml
index 0787fb8..8a45e23 100644
--- a/dev-tools/idea/lucene/highlighter/highlighter.iml
+++ b/dev-tools/idea/lucene/highlighter/highlighter.iml
@@ -12,6 +12,7 @@
     <orderEntry type="sourceFolder" forTests="false" />
     <orderEntry type="library" scope="TEST" name="JUnit" level="project" />
     <orderEntry type="module" scope="TEST" module-name="lucene-test-framework" />
+    <orderEntry type="module" scope="TEST" module-name="codecs" />
     <orderEntry type="module" module-name="memory" />
     <orderEntry type="module" module-name="misc" />
     <orderEntry type="module" module-name="queries" />
diff --git a/lucene/analysis/common/src/test/org/apache/lucene/analysis/sinks/TestTeeSinkTokenFilter.java b/lucene/analysis/common/src/test/org/apache/lucene/analysis/sinks/TestTeeSinkTokenFilter.java
index 4c277de..6f03606 100644
--- a/lucene/analysis/common/src/test/org/apache/lucene/analysis/sinks/TestTeeSinkTokenFilter.java
+++ b/lucene/analysis/common/src/test/org/apache/lucene/analysis/sinks/TestTeeSinkTokenFilter.java
@@ -31,7 +31,7 @@
 import org.apache.lucene.document.FieldType;
 import org.apache.lucene.document.TextField;
 import org.apache.lucene.index.DirectoryReader;
-import org.apache.lucene.index.DocsAndPositionsEnum;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.index.Terms;
@@ -111,7 +111,7 @@
     TermsEnum termsEnum = vector.iterator(null);
     termsEnum.next();
     assertEquals(2, termsEnum.totalTermFreq());
-    DocsAndPositionsEnum positions = termsEnum.docsAndPositions(null, null);
+    DocsEnum positions = termsEnum.docsAndPositions(null, null);
     assertTrue(positions.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
     assertEquals(2, positions.freq());
     positions.nextPosition();
diff --git a/lucene/analysis/common/src/test/org/apache/lucene/analysis/standard/TestClassicAnalyzer.java b/lucene/analysis/common/src/test/org/apache/lucene/analysis/standard/TestClassicAnalyzer.java
index 3b3706f..a136b0d 100644
--- a/lucene/analysis/common/src/test/org/apache/lucene/analysis/standard/TestClassicAnalyzer.java
+++ b/lucene/analysis/common/src/test/org/apache/lucene/analysis/standard/TestClassicAnalyzer.java
@@ -7,7 +7,7 @@
 import org.apache.lucene.document.Field;
 import org.apache.lucene.document.TextField;
 import org.apache.lucene.index.DirectoryReader;
-import org.apache.lucene.index.DocsAndPositionsEnum;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.index.IndexWriterConfig;
@@ -281,7 +281,7 @@
 
     // Make sure position is still incremented when
     // massive term is skipped:
-    DocsAndPositionsEnum tps = MultiFields.getTermPositionsEnum(reader,
+    DocsEnum tps = MultiFields.getTermPositionsEnum(reader,
                                                                 MultiFields.getLiveDocs(reader),
                                                                 "content",
                                                                 new BytesRef("another"));
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/blockterms/BlockTermsReader.java b/lucene/codecs/src/java/org/apache/lucene/codecs/blockterms/BlockTermsReader.java
index 06c90db..563955a 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/blockterms/BlockTermsReader.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/blockterms/BlockTermsReader.java
@@ -29,7 +29,7 @@
 import org.apache.lucene.codecs.FieldsProducer;
 import org.apache.lucene.codecs.PostingsReaderBase;
 import org.apache.lucene.index.CorruptIndexException;
-import org.apache.lucene.index.DocsAndPositionsEnum;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.IndexFileNames;
@@ -659,7 +659,7 @@
       }
 
       @Override
-      public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
+      public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
         if (fieldInfo.getIndexOptions().compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) < 0) {
           // Positions were not indexed:
           return null;
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/blocktreeords/OrdsIntersectTermsEnum.java b/lucene/codecs/src/java/org/apache/lucene/codecs/blocktreeords/OrdsIntersectTermsEnum.java
index 0eb9709..3e0f5f9 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/blocktreeords/OrdsIntersectTermsEnum.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/blocktreeords/OrdsIntersectTermsEnum.java
@@ -17,10 +17,7 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-
 import org.apache.lucene.codecs.blocktreeords.FSTOrdsOutputs.Output;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexOptions;
 import org.apache.lucene.index.TermState;
@@ -35,6 +32,8 @@
 import org.apache.lucene.util.automaton.RunAutomaton;
 import org.apache.lucene.util.fst.FST;
 
+import java.io.IOException;
+
 // NOTE: cannot seek!
 final class OrdsIntersectTermsEnum extends TermsEnum {
   final IndexInput in;
@@ -209,7 +208,7 @@
   }
 
   @Override
-  public DocsAndPositionsEnum docsAndPositions(Bits skipDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
+  public DocsEnum docsAndPositions(Bits skipDocs, DocsEnum reuse, int flags) throws IOException {
     if (fr.fieldInfo.getIndexOptions().compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) < 0) {
       // Positions were not indexed:
       return null;
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/blocktreeords/OrdsSegmentTermsEnum.java b/lucene/codecs/src/java/org/apache/lucene/codecs/blocktreeords/OrdsSegmentTermsEnum.java
index 8bdd248..ef3cacb 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/blocktreeords/OrdsSegmentTermsEnum.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/blocktreeords/OrdsSegmentTermsEnum.java
@@ -20,12 +20,8 @@
 //import java.io.*;
 //import java.nio.charset.StandardCharsets;
 
-import java.io.IOException;
-import java.io.PrintStream;
-
 import org.apache.lucene.codecs.BlockTermState;
 import org.apache.lucene.codecs.blocktreeords.FSTOrdsOutputs.Output;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexOptions;
 import org.apache.lucene.index.TermState;
@@ -42,6 +38,9 @@
 import org.apache.lucene.util.fst.FST;
 import org.apache.lucene.util.fst.Util;
 
+import java.io.IOException;
+import java.io.PrintStream;
+
 /** Iterates through terms in this field. */
 public final class OrdsSegmentTermsEnum extends TermsEnum {
 
@@ -937,7 +936,7 @@
   }
 
   @Override
-  public DocsAndPositionsEnum docsAndPositions(Bits skipDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
+  public DocsEnum docsAndPositions(Bits skipDocs, DocsEnum reuse, int flags) throws IOException {
     if (fr.fieldInfo.getIndexOptions().compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) < 0) {
       // Positions were not indexed:
       return null;
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/bloom/BloomFilteringPostingsFormat.java b/lucene/codecs/src/java/org/apache/lucene/codecs/bloom/BloomFilteringPostingsFormat.java
index fc292b6..a1e48f3 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/bloom/BloomFilteringPostingsFormat.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/bloom/BloomFilteringPostingsFormat.java
@@ -17,21 +17,11 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.Map;
-
 import org.apache.lucene.codecs.CodecUtil;
 import org.apache.lucene.codecs.FieldsConsumer;
 import org.apache.lucene.codecs.FieldsProducer;
 import org.apache.lucene.codecs.PostingsFormat;
 import org.apache.lucene.codecs.bloom.FuzzySet.ContainsResult;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.Fields;
@@ -51,6 +41,15 @@
 import org.apache.lucene.util.RamUsageEstimator;
 import org.apache.lucene.util.automaton.CompiledAutomaton;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
 /**
  * <p>
  * A {@link PostingsFormat} useful for low doc-frequency fields such as primary
@@ -381,11 +380,10 @@
       public long totalTermFreq() throws IOException {
         return delegate().totalTermFreq();
       }
-      
 
       @Override
-      public DocsAndPositionsEnum docsAndPositions(Bits liveDocs,
-          DocsAndPositionsEnum reuse, int flags) throws IOException {
+      public DocsEnum docsAndPositions(Bits liveDocs,
+          DocsEnum reuse, int flags) throws IOException {
         return delegate().docsAndPositions(liveDocs, reuse, flags);
       }
 
@@ -394,6 +392,7 @@
           throws IOException {
         return delegate().docs(liveDocs, reuse, flags);
       }
+
     }
 
     @Override
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/memory/DirectPostingsFormat.java b/lucene/codecs/src/java/org/apache/lucene/codecs/memory/DirectPostingsFormat.java
index 578642f..1aea6f2 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/memory/DirectPostingsFormat.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/memory/DirectPostingsFormat.java
@@ -27,7 +27,6 @@
 import org.apache.lucene.codecs.FieldsProducer;
 import org.apache.lucene.codecs.PostingsFormat;
 import org.apache.lucene.codecs.lucene50.Lucene50PostingsFormat; // javadocs
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.Fields;
@@ -50,7 +49,7 @@
 import org.apache.lucene.util.automaton.RunAutomaton;
 import org.apache.lucene.util.automaton.Transition;
 
-// TODO: 
+// TODO:
 //   - build depth-N prefix hash?
 //   - or: longer dense skip lists than just next byte?
 
@@ -61,7 +60,7 @@
  *  <p><b>WARNING</b>: This is
  *  exceptionally RAM intensive: it makes no effort to
  *  compress the postings data, storing terms as separate
- *  byte[] and postings as separate int[], but as a result it 
+ *  byte[] and postings as separate int[], but as a result it
  *  gives substantial increase in search performance.
  *
  *  <p>This postings format supports {@link TermsEnum#ord}
@@ -88,7 +87,7 @@
   public DirectPostingsFormat() {
     this(DEFAULT_MIN_SKIP_COUNT, DEFAULT_LOW_FREQ_CUTOFF);
   }
-  
+
   /** minSkipCount is how many terms in a row must have the
    *  same prefix before we put a skip pointer down.  Terms
    *  with docFreq &lt;= lowFreqCutoff will use a single int[]
@@ -99,7 +98,7 @@
     this.minSkipCount = minSkipCount;
     this.lowFreqCutoff = lowFreqCutoff;
   }
-  
+
   @Override
   public FieldsConsumer fieldsConsumer(SegmentWriteState state) throws IOException {
     return PostingsFormat.forName("Lucene50").fieldsConsumer(state);
@@ -160,7 +159,7 @@
       }
       return sizeInBytes;
     }
-    
+
     @Override
     public Iterable<? extends Accountable> getChildResources() {
       return Accountables.namedAccountables("field", fields);
@@ -205,9 +204,14 @@
       @Override
       public long ramBytesUsed() {
         return BASE_RAM_BYTES_USED +
-            ((postings!=null) ? RamUsageEstimator.sizeOf(postings) : 0) + 
+            ((postings!=null) ? RamUsageEstimator.sizeOf(postings) : 0) +
             ((payloads!=null) ? RamUsageEstimator.sizeOf(payloads) : 0);
       }
+
+      @Override
+      public Iterable<? extends Accountable> getChildResources() {
+        return Collections.emptyList();
+      }
     }
 
     // TODO: maybe specialize into prx/no-prx/no-frq cases?
@@ -231,30 +235,35 @@
 
       @Override
       public long ramBytesUsed() {
-         long sizeInBytes = BASE_RAM_BYTES_USED;
-         sizeInBytes += (docIDs!=null)? RamUsageEstimator.sizeOf(docIDs) : 0;
-         sizeInBytes += (freqs!=null)? RamUsageEstimator.sizeOf(freqs) : 0;
-         
-         if(positions != null) {
-           sizeInBytes += RamUsageEstimator.shallowSizeOf(positions);
-           for(int[] position : positions) {
-             sizeInBytes += (position!=null) ? RamUsageEstimator.sizeOf(position) : 0;
-           }
-         }
-         
-         if (payloads != null) {
-           sizeInBytes += RamUsageEstimator.shallowSizeOf(payloads);
-           for(byte[][] payload : payloads) {
-             if(payload != null) {
-               sizeInBytes += RamUsageEstimator.shallowSizeOf(payload);
-               for(byte[] pload : payload) {
-                 sizeInBytes += (pload!=null) ? RamUsageEstimator.sizeOf(pload) : 0; 
-               }
-             }
-           }
-         }
-         
-         return sizeInBytes;
+        long sizeInBytes = BASE_RAM_BYTES_USED;
+        sizeInBytes += (docIDs!=null)? RamUsageEstimator.sizeOf(docIDs) : 0;
+        sizeInBytes += (freqs!=null)? RamUsageEstimator.sizeOf(freqs) : 0;
+
+        if(positions != null) {
+          sizeInBytes += RamUsageEstimator.shallowSizeOf(positions);
+          for(int[] position : positions) {
+            sizeInBytes += (position!=null) ? RamUsageEstimator.sizeOf(position) : 0;
+          }
+        }
+
+        if (payloads != null) {
+          sizeInBytes += RamUsageEstimator.shallowSizeOf(payloads);
+          for(byte[][] payload : payloads) {
+            if(payload != null) {
+              sizeInBytes += RamUsageEstimator.shallowSizeOf(payload);
+              for(byte[] pload : payload) {
+                sizeInBytes += (pload!=null) ? RamUsageEstimator.sizeOf(pload) : 0;
+              }
+            }
+          }
+        }
+
+        return sizeInBytes;
+      }
+
+      @Override
+      public Iterable<? extends Accountable> getChildResources() {
+        return Collections.emptyList();
       }
     }
 
@@ -312,7 +321,7 @@
       }
       terms = new TermAndSkip[numTerms];
       termOffsets = new int[1+numTerms];
-      
+
       byte[] termBytes = new byte[1024];
 
       this.minSkipCount = minSkipCount;
@@ -324,7 +333,7 @@
 
       BytesRef term;
       DocsEnum docsEnum = null;
-      DocsAndPositionsEnum docsAndPositionsEnum = null;
+      DocsEnum docsAndPositionsEnum = null;
       final TermsEnum termsEnum = termsIn.iterator(null);
       int termOffset = 0;
 
@@ -411,7 +420,7 @@
           }
 
           final int[] postings = scratch.get();
-        
+
           ent = new LowFreqTerm(postings, payloads, docFreq, (int) totalTermFreq);
         } else {
           final int[] docs = new int[docFreq];
@@ -523,18 +532,23 @@
       sizeInBytes += ((skips!=null) ? RamUsageEstimator.sizeOf(skips) : 0);
       sizeInBytes += ((skipOffsets!=null) ? RamUsageEstimator.sizeOf(skipOffsets) : 0);
       sizeInBytes += ((sameCounts!=null) ? RamUsageEstimator.sizeOf(sameCounts) : 0);
-      
+
       if(terms!=null) {
         sizeInBytes += RamUsageEstimator.shallowSizeOf(terms);
         for(TermAndSkip termAndSkip : terms) {
           sizeInBytes += (termAndSkip!=null) ? termAndSkip.ramBytesUsed() : 0;
         }
       }
-      
+
       return sizeInBytes;
     }
 
     @Override
+    public Iterable<? extends Accountable> getChildResources() {
+      return Collections.emptyList();
+    }
+
+    @Override
     public String toString() {
       return "DirectTerms(terms=" + terms.length + ",postings=" + sumDocFreq + ",positions=" + sumTotalTermFreq + ",docs=" + docCount + ")";
     }
@@ -546,7 +560,7 @@
       int upto = termOffsets[ord];
       final int termLen = termOffsets[1+ord] - upto;
       int otherUpto = other.offset;
-      
+
       final int stop = upto + Math.min(termLen, other.length);
       while (upto < stop) {
         int diff = (termBytes[upto++] & 0xFF) - (otherBytes[otherUpto++] & 0xFF);
@@ -554,7 +568,7 @@
           return diff;
         }
       }
-    
+
       // One is a prefix of the other, or, they are equal:
       return termLen - other.length;
     }
@@ -706,7 +720,7 @@
     public boolean hasPositions() {
       return hasPos;
     }
-    
+
     @Override
     public boolean hasPayloads() {
       return hasPayloads;
@@ -854,10 +868,13 @@
       }
 
       @Override
-      public DocsEnum docs(Bits liveDocs, DocsEnum reuse, int flags) {
+      public DocsEnum docs(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
         // TODO: implement reuse
         // it's hairy!
 
+        if ((flags & DocsEnum.FLAG_POSITIONS) >= DocsEnum.FLAG_POSITIONS)
+          return docsAndPositions(liveDocs, reuse, flags);
+
         if (terms[termOrd] instanceof LowFreqTerm) {
           final int[] postings = ((LowFreqTerm) terms[termOrd]).postings;
           if (hasFreq) {
@@ -927,7 +944,7 @@
       }
 
       @Override
-      public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) {
+      public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) {
         if (!hasPos) {
           return null;
         }
@@ -1202,7 +1219,7 @@
           // if (DEBUG) {
           //   System.out.println("  term=" + new BytesRef(termBytes, termOffset, termLength).utf8ToString() + " skips=" + Arrays.toString(skips));
           // }
-        
+
           assert termOrd < state.changeOrd;
 
           assert stateUpto <= termLength: "term.length=" + termLength + "; stateUpto=" + stateUpto;
@@ -1335,7 +1352,7 @@
             compiledAutomaton.automaton.initTransition(nextState, states[stateUpto].transition);
             states[stateUpto].transitionUpto = -1;
             states[stateUpto].transitionMax = -1;
-            
+
             if (stateUpto == termLength) {
               // if (DEBUG) {
               //   System.out.println("  term ends after push");
@@ -1455,6 +1472,8 @@
       public DocsEnum docs(Bits liveDocs, DocsEnum reuse, int flags) {
         // TODO: implement reuse
         // it's hairy!
+        if ((flags & DocsEnum.FLAG_POSITIONS) >= DocsEnum.FLAG_POSITIONS)
+          return docsAndPositions(liveDocs, reuse, flags);
 
         if (terms[termOrd] instanceof LowFreqTerm) {
           final int[] postings = ((LowFreqTerm) terms[termOrd]).postings;
@@ -1484,7 +1503,7 @@
       }
 
       @Override
-      public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) {
+      public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) {
         if (!hasPos) {
           return null;
         }
@@ -1572,12 +1591,43 @@
     }
 
     @Override
+    public int nextPosition() throws IOException {
+      assert false;
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
+    @Override
     public int advance(int target) throws IOException {
       // Linear scan, but this is low-freq term so it won't
       // be costly:
       return slowAdvance(target);
     }
-    
+
     @Override
     public long cost() {
       return postings.length;
@@ -1640,12 +1690,43 @@
     }
 
     @Override
+    public int nextPosition() throws IOException {
+      assert false;
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
+    @Override
     public int advance(int target) throws IOException {
       // Linear scan, but this is low-freq term so it won't
       // be costly:
       return slowAdvance(target);
     }
-    
+
     @Override
     public long cost() {
       return postings.length / 2;
@@ -1687,7 +1768,7 @@
       //   System.out.println("  nextDoc freq=" + freq + " upto=" + upto + " vs " + postings.length);
       // }
       if (liveDocs == null) {
-        if (upto < postings.length) {   
+        if (upto < postings.length) {
           freq = postings[upto+1];
           assert freq > 0;
           return postings[upto];
@@ -1724,12 +1805,43 @@
     }
 
     @Override
+    public int nextPosition() throws IOException {
+      assert false; // should be using LowFreqDocsAndPositionsEnum
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
+    @Override
     public int advance(int target) throws IOException {
       // Linear scan, but this is low-freq term so it won't
       // be costly:
       return slowAdvance(target);
     }
-    
+
     @Override
     public long cost() {
       // TODO: could do a better estimate
@@ -1737,7 +1849,7 @@
     }
   }
 
-  private final static class LowFreqDocsAndPositionsEnum extends DocsAndPositionsEnum {
+  private final static class LowFreqDocsAndPositionsEnum extends DocsEnum {
     private int[] postings;
     private final Bits liveDocs;
     private final int posMult;
@@ -1748,6 +1860,7 @@
     private int docID;
     private int freq;
     private int skipPositions;
+    private int pos;
     private int startOffset;
     private int endOffset;
     private int lastPayloadOffset;
@@ -1772,10 +1885,11 @@
       }
     }
 
-    public DocsAndPositionsEnum reset(int[] postings, byte[] payloadBytes) {
+    public DocsEnum reset(int[] postings, byte[] payloadBytes) {
       this.postings = postings;
       upto = 0;
       skipPositions = 0;
+      pos = -1;
       startOffset = -1;
       endOffset = -1;
       docID = -1;
@@ -1786,6 +1900,7 @@
 
     @Override
     public int nextDoc() {
+      pos = -1;
       if (hasPayloads) {
         for(int i=0;i<skipPositions;i++) {
           upto++;
@@ -1842,9 +1957,10 @@
 
     @Override
     public int nextPosition() {
-      assert skipPositions > 0;
+      if (skipPositions <= 0)
+        return NO_MORE_POSITIONS;
       skipPositions--;
-      final int pos = postings[upto++];
+      pos = postings[upto++];
       if (hasOffsets) {
         startOffset = postings[upto++];
         endOffset = postings[upto++];
@@ -1858,6 +1974,16 @@
     }
 
     @Override
+    public int startPosition() throws IOException {
+      return pos;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return pos;
+    }
+
+    @Override
     public int startOffset() {
       return startOffset;
     }
@@ -1883,7 +2009,7 @@
         return null;
       }
     }
-    
+
     @Override
     public long cost() {
       // TODO: could do a better estimate
@@ -1956,6 +2082,36 @@
     }
 
     @Override
+    public int nextPosition() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
     public int advance(int target) {
       /*
       upto++;
@@ -2062,7 +2218,7 @@
         return docID = docIDs[upto];
       }
     }
-    
+
     @Override
     public long cost() {
       return docIDs.length;
@@ -2070,7 +2226,7 @@
   }
 
   // TODO: specialize offsets and not
-  private final static class HighFreqDocsAndPositionsEnum extends DocsAndPositionsEnum {
+  private final static class HighFreqDocsAndPositionsEnum extends DocsEnum {
     private int[] docIDs;
     private int[] freqs;
     private int[][] positions;
@@ -2105,7 +2261,7 @@
       return liveDocs;
     }
 
-    public DocsAndPositionsEnum reset(int[] docIDs, int[] freqs, int[][] positions, byte[][][] payloads) {
+    public DocsEnum reset(int[] docIDs, int[] freqs, int[][] positions, byte[][][] payloads) {
       this.docIDs = docIDs;
       this.freqs = freqs;
       this.positions = positions;
@@ -2119,7 +2275,7 @@
       upto++;
       if (liveDocs == null) {
         if (upto < docIDs.length) {
-          posUpto = -posJump;   
+          posUpto = -posJump;
           curPositions = positions[upto];
           return docID = docIDs[upto];
         }
@@ -2150,10 +2306,22 @@
     @Override
     public int nextPosition() {
       posUpto += posJump;
+      if (posUpto >= curPositions.length)
+        return NO_MORE_POSITIONS;
       return curPositions[posUpto];
     }
 
     @Override
+    public int startPosition() throws IOException {
+      return curPositions[posUpto];
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return startPosition();
+    }
+
+    @Override
     public int startOffset() {
       if (hasOffsets) {
         return curPositions[posUpto+1];
@@ -2300,7 +2468,7 @@
         return payload;
       }
     }
-    
+
     @Override
     public long cost() {
       return docIDs.length;
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/memory/FSTOrdTermsReader.java b/lucene/codecs/src/java/org/apache/lucene/codecs/memory/FSTOrdTermsReader.java
index 90c3525..3211e2f 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/memory/FSTOrdTermsReader.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/memory/FSTOrdTermsReader.java
@@ -17,22 +17,16 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.TreeMap;
-
+import org.apache.lucene.codecs.BlockTermState;
+import org.apache.lucene.codecs.CodecUtil;
+import org.apache.lucene.codecs.FieldsProducer;
+import org.apache.lucene.codecs.PostingsReaderBase;
 import org.apache.lucene.index.CorruptIndexException;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
-import org.apache.lucene.index.IndexOptions;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.FieldInfos;
 import org.apache.lucene.index.IndexFileNames;
+import org.apache.lucene.index.IndexOptions;
 import org.apache.lucene.index.SegmentInfo;
 import org.apache.lucene.index.SegmentReadState;
 import org.apache.lucene.index.TermState;
@@ -41,26 +35,31 @@
 import org.apache.lucene.store.ByteArrayDataInput;
 import org.apache.lucene.store.ChecksumIndexInput;
 import org.apache.lucene.store.IndexInput;
-import org.apache.lucene.util.ArrayUtil;
-import org.apache.lucene.util.automaton.ByteRunAutomaton;
-import org.apache.lucene.util.automaton.CompiledAutomaton;
 import org.apache.lucene.util.Accountable;
 import org.apache.lucene.util.Accountables;
+import org.apache.lucene.util.ArrayUtil;
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.BytesRefBuilder;
 import org.apache.lucene.util.IOUtils;
 import org.apache.lucene.util.RamUsageEstimator;
+import org.apache.lucene.util.automaton.ByteRunAutomaton;
+import org.apache.lucene.util.automaton.CompiledAutomaton;
 import org.apache.lucene.util.fst.BytesRefFSTEnum;
 import org.apache.lucene.util.fst.BytesRefFSTEnum.InputOutput;
 import org.apache.lucene.util.fst.FST;
 import org.apache.lucene.util.fst.Outputs;
 import org.apache.lucene.util.fst.PositiveIntOutputs;
 import org.apache.lucene.util.fst.Util;
-import org.apache.lucene.codecs.BlockTermState;
-import org.apache.lucene.codecs.FieldsProducer;
-import org.apache.lucene.codecs.PostingsReaderBase;
-import org.apache.lucene.codecs.CodecUtil;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeMap;
 
 /** 
  * FST-based terms dictionary reader.
@@ -433,12 +432,8 @@
       }
 
       @Override
-      public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
-        if (!hasPositions()) {
-          return null;
-        }
-        decodeMetaData();
-        return postingsReader.docsAndPositions(fieldInfo, state, liveDocs, reuse, flags);
+      public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
+        return docs(liveDocs, reuse, flags);
       }
 
       // TODO: this can be achieved by making use of Util.getByOutput()
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/memory/FSTTermsReader.java b/lucene/codecs/src/java/org/apache/lucene/codecs/memory/FSTTermsReader.java
index ee8629c..60a9c4c 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/memory/FSTTermsReader.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/memory/FSTTermsReader.java
@@ -17,21 +17,16 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.TreeMap;
-
+import org.apache.lucene.codecs.BlockTermState;
+import org.apache.lucene.codecs.CodecUtil;
+import org.apache.lucene.codecs.FieldsProducer;
+import org.apache.lucene.codecs.PostingsReaderBase;
 import org.apache.lucene.index.CorruptIndexException;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
-import org.apache.lucene.index.IndexOptions;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.FieldInfos;
 import org.apache.lucene.index.IndexFileNames;
+import org.apache.lucene.index.IndexOptions;
 import org.apache.lucene.index.SegmentInfo;
 import org.apache.lucene.index.SegmentReadState;
 import org.apache.lucene.index.TermState;
@@ -39,25 +34,29 @@
 import org.apache.lucene.index.TermsEnum;
 import org.apache.lucene.store.ByteArrayDataInput;
 import org.apache.lucene.store.IndexInput;
-import org.apache.lucene.util.ArrayUtil;
-import org.apache.lucene.util.automaton.ByteRunAutomaton;
-import org.apache.lucene.util.automaton.CompiledAutomaton;
 import org.apache.lucene.util.Accountable;
 import org.apache.lucene.util.Accountables;
+import org.apache.lucene.util.ArrayUtil;
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.BytesRefBuilder;
 import org.apache.lucene.util.IOUtils;
 import org.apache.lucene.util.RamUsageEstimator;
+import org.apache.lucene.util.automaton.ByteRunAutomaton;
+import org.apache.lucene.util.automaton.CompiledAutomaton;
 import org.apache.lucene.util.fst.BytesRefFSTEnum;
 import org.apache.lucene.util.fst.BytesRefFSTEnum.InputOutput;
 import org.apache.lucene.util.fst.FST;
 import org.apache.lucene.util.fst.Outputs;
 import org.apache.lucene.util.fst.Util;
-import org.apache.lucene.codecs.BlockTermState;
-import org.apache.lucene.codecs.FieldsProducer;
-import org.apache.lucene.codecs.PostingsReaderBase;
-import org.apache.lucene.codecs.CodecUtil;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeMap;
 
 /**
  * FST-based terms dictionary reader.
@@ -296,12 +295,8 @@
       }
 
       @Override
-      public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
-        if (!hasPositions()) {
-          return null;
-        }
-        decodeMetaData();
-        return postingsReader.docsAndPositions(fieldInfo, state, liveDocs, reuse, flags);
+      public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
+        return docs(liveDocs, reuse, flags);
       }
 
       @Override
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/memory/MemoryDocValuesProducer.java b/lucene/codecs/src/java/org/apache/lucene/codecs/memory/MemoryDocValuesProducer.java
index 8e2189b..1254057 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/memory/MemoryDocValuesProducer.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/memory/MemoryDocValuesProducer.java
@@ -17,20 +17,11 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicLong;
-
 import org.apache.lucene.codecs.CodecUtil;
 import org.apache.lucene.codecs.DocValuesProducer;
 import org.apache.lucene.index.BinaryDocValues;
 import org.apache.lucene.index.CorruptIndexException;
 import org.apache.lucene.index.DocValues;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.FieldInfos;
@@ -67,6 +58,14 @@
 import org.apache.lucene.util.packed.MonotonicBlockPackedReader;
 import org.apache.lucene.util.packed.PackedInts;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
 /**
  * Reader for {@link MemoryDocValuesFormat}
  */
@@ -897,7 +896,7 @@
     }
 
     @Override
-    public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
+    public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
       throw new UnsupportedOperationException();
     }
   }
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/memory/MemoryPostingsFormat.java b/lucene/codecs/src/java/org/apache/lucene/codecs/memory/MemoryPostingsFormat.java
index 1c96d9d..bc75435 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/memory/MemoryPostingsFormat.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/memory/MemoryPostingsFormat.java
@@ -30,13 +30,12 @@
 import org.apache.lucene.codecs.PostingsFormat;
 import org.apache.lucene.codecs.TermStats;
 import org.apache.lucene.index.CorruptIndexException;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInfo;
-import org.apache.lucene.index.IndexOptions;
 import org.apache.lucene.index.FieldInfos;
 import org.apache.lucene.index.Fields;
 import org.apache.lucene.index.IndexFileNames;
+import org.apache.lucene.index.IndexOptions;
 import org.apache.lucene.index.SegmentReadState;
 import org.apache.lucene.index.SegmentWriteState;
 import org.apache.lucene.index.Terms;
@@ -317,7 +316,7 @@
         long sumTotalTermFreq = 0;
         long sumDocFreq = 0;
         DocsEnum docsEnum = null;
-        DocsAndPositionsEnum posEnum = null;
+        DocsEnum posEnum = null;
         int enumFlags;
 
         IndexOptions indexOptions = fieldInfo.getIndexOptions();
@@ -332,15 +331,16 @@
           enumFlags = DocsEnum.FLAG_FREQS;
         } else if (writeOffsets == false) {
           if (writePayloads) {
-            enumFlags = DocsAndPositionsEnum.FLAG_PAYLOADS;
-          } else {
-            enumFlags = 0;
+            enumFlags = DocsEnum.FLAG_PAYLOADS;
+          }
+          else {
+            enumFlags = DocsEnum.FLAG_POSITIONS;
           }
         } else {
           if (writePayloads) {
-            enumFlags = DocsAndPositionsEnum.FLAG_PAYLOADS | DocsAndPositionsEnum.FLAG_OFFSETS;
+            enumFlags = DocsEnum.FLAG_PAYLOADS | DocsEnum.FLAG_OFFSETS;
           } else {
-            enumFlags = DocsAndPositionsEnum.FLAG_OFFSETS;
+            enumFlags = DocsEnum.FLAG_OFFSETS;
           }
         }
 
@@ -539,14 +539,44 @@
     public int freq() {
       return freq;
     }
-    
+
+    @Override
+    public int nextPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
     @Override
     public long cost() {
       return numDocs;
     }
   }
 
-  private final static class FSTDocsAndPositionsEnum extends DocsAndPositionsEnum {
+  private final static class FSTDocsAndPositionsEnum extends DocsEnum {
     private final boolean storePayloads;
     private byte[] buffer = new byte[16];
     private final ByteArrayDataInput in = new ByteArrayDataInput(buffer);
@@ -660,7 +690,8 @@
     @Override
     public int nextPosition() {
       //System.out.println("    nextPos storePayloads=" + storePayloads + " this=" + this);
-      assert posPending > 0;
+      if (posPending <= 0)
+        return NO_MORE_POSITIONS;
       posPending--;
       if (!storePayloads) {
         pos += in.readVInt();
@@ -695,6 +726,16 @@
     }
 
     @Override
+    public int startPosition() throws IOException {
+      return pos;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return pos;
+    }
+
+    @Override
     public int startOffset() {
       return startOffset;
     }
@@ -802,6 +843,10 @@
     
     @Override
     public DocsEnum docs(Bits liveDocs, DocsEnum reuse, int flags) {
+
+      if ((flags & DocsEnum.FLAG_POSITIONS) >= DocsEnum.FLAG_POSITIONS)
+        return docsAndPositions(liveDocs, reuse, flags);
+
       decodeMetaData();
       FSTDocsEnum docsEnum;
 
@@ -817,7 +862,7 @@
     }
 
     @Override
-    public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) {
+    public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) {
 
       boolean hasOffsets = field.getIndexOptions().compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS) >= 0;
       if (field.getIndexOptions().compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) < 0) {
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextFieldsReader.java b/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextFieldsReader.java
index 1b17a84..cc9ef3e 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextFieldsReader.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextFieldsReader.java
@@ -26,7 +26,6 @@
 import java.util.TreeMap;
 
 import org.apache.lucene.codecs.FieldsProducer;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.FieldInfos;
@@ -70,8 +69,8 @@
 class SimpleTextFieldsReader extends FieldsProducer {
 
   private static final long BASE_RAM_BYTES_USED =
-        RamUsageEstimator.shallowSizeOfInstance(SimpleTextFieldsReader.class)
-      + RamUsageEstimator.shallowSizeOfInstance(TreeMap.class);
+      RamUsageEstimator.shallowSizeOfInstance(SimpleTextFieldsReader.class)
+          + RamUsageEstimator.shallowSizeOfInstance(TreeMap.class);
 
   private final TreeMap<String,Long> fields;
   private final IndexInput in;
@@ -92,12 +91,12 @@
       }
     }
   }
-  
+
   private TreeMap<String,Long> readFields(IndexInput in) throws IOException {
     ChecksumIndexInput input = new BufferedChecksumIndexInput(in);
     BytesRefBuilder scratch = new BytesRefBuilder();
     TreeMap<String,Long> fields = new TreeMap<>();
-    
+
     while (true) {
       SimpleTextUtil.readLine(input, scratch);
       if (scratch.get().equals(END)) {
@@ -205,9 +204,13 @@
     public long totalTermFreq() {
       return indexOptions == IndexOptions.DOCS ? -1 : totalTermFreq;
     }
- 
+
     @Override
     public DocsEnum docs(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
+
+      if ((flags & DocsEnum.FLAG_POSITIONS) >= DocsEnum.FLAG_POSITIONS)
+        return docsAndPositions(liveDocs, reuse, flags);
+
       SimpleTextDocsEnum docsEnum;
       if (reuse != null && reuse instanceof SimpleTextDocsEnum && ((SimpleTextDocsEnum) reuse).canReuse(SimpleTextFieldsReader.this.in)) {
         docsEnum = (SimpleTextDocsEnum) reuse;
@@ -218,7 +221,7 @@
     }
 
     @Override
-    public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
+    public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
 
       if (indexOptions.compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) < 0) {
         // Positions were not indexed
@@ -230,7 +233,7 @@
         docsAndPositionsEnum = (SimpleTextDocsAndPositionsEnum) reuse;
       } else {
         docsAndPositionsEnum = new SimpleTextDocsAndPositionsEnum();
-      } 
+      }
       return docsAndPositionsEnum.reset(docsStart, liveDocs, indexOptions, docFreq);
     }
   }
@@ -245,7 +248,7 @@
     private final BytesRefBuilder scratch = new BytesRefBuilder();
     private final CharsRefBuilder scratchUTF16 = new CharsRefBuilder();
     private int cost;
-    
+
     public SimpleTextDocsEnum() {
       this.inStart = SimpleTextFieldsReader.this.in;
       this.in = this.inStart.clone();
@@ -276,6 +279,37 @@
     }
 
     @Override
+    public int nextPosition() throws IOException {
+      assert false;
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
+    @Override
     public int nextDoc() throws IOException {
       if (docID == NO_MORE_DOCS) {
         return docID;
@@ -327,14 +361,14 @@
       // Naive -- better to index skip data
       return slowAdvance(target);
     }
-    
+
     @Override
     public long cost() {
       return cost;
     }
   }
 
-  private class SimpleTextDocsAndPositionsEnum extends DocsAndPositionsEnum {
+  private class SimpleTextDocsAndPositionsEnum extends DocsEnum {
     private final IndexInput inStart;
     private final IndexInput in;
     private int docID = -1;
@@ -344,6 +378,7 @@
     private final BytesRefBuilder scratch2 = new BytesRefBuilder();
     private final CharsRefBuilder scratchUTF16 = new CharsRefBuilder();
     private final CharsRefBuilder scratchUTF16_2 = new CharsRefBuilder();
+    private int pos;
     private BytesRef payload;
     private long nextDocStart;
     private boolean readOffsets;
@@ -436,10 +471,12 @@
 
     @Override
     public int nextPosition() throws IOException {
-      final int pos;
       if (readPositions) {
         SimpleTextUtil.readLine(in, scratch);
-        assert StringHelper.startsWith(scratch.get(), POS): "got line=" + scratch.get().utf8ToString();
+        if (!StringHelper.startsWith(scratch.get(), POS)) {
+          pos = NO_MORE_POSITIONS;
+          return pos;
+        }
         scratchUTF16_2.copyUTF8Bytes(scratch.bytes(), POS.length, scratch.length()-POS.length);
         pos = ArrayUtil.parseInt(scratchUTF16_2.chars(), 0, scratchUTF16_2.length());
       } else {
@@ -474,6 +511,16 @@
     }
 
     @Override
+    public int startPosition() throws IOException {
+      return pos;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return pos;
+    }
+
+    @Override
     public int startOffset() throws IOException {
       return startOffset;
     }
@@ -487,7 +534,7 @@
     public BytesRef getPayload() {
       return payload;
     }
-    
+
     @Override
     public long cost() {
       return cost;
@@ -505,9 +552,9 @@
   }
 
   private static final long TERMS_BASE_RAM_BYTES_USED =
-        RamUsageEstimator.shallowSizeOfInstance(SimpleTextTerms.class)
-      + RamUsageEstimator.shallowSizeOfInstance(BytesRef.class)
-      + RamUsageEstimator.shallowSizeOfInstance(CharsRef.class);
+      RamUsageEstimator.shallowSizeOfInstance(SimpleTextTerms.class)
+          + RamUsageEstimator.shallowSizeOfInstance(BytesRef.class)
+          + RamUsageEstimator.shallowSizeOfInstance(CharsRef.class);
   private class SimpleTextTerms extends Terms implements Accountable {
     private final long termsStart;
     private final FieldInfo fieldInfo;
@@ -532,7 +579,7 @@
       final Builder<PairOutputs.Pair<Long,PairOutputs.Pair<Long,Long>>> b;
       final PairOutputs<Long,Long> outputsInner = new PairOutputs<>(posIntOutputs, posIntOutputs);
       final PairOutputs<Long,PairOutputs.Pair<Long,Long>> outputs = new PairOutputs<>(posIntOutputs,
-                                                                                                                      outputsInner);
+          outputsInner);
       b = new Builder<>(FST.INPUT_TYPE.BYTE1, outputs);
       IndexInput in = SimpleTextFieldsReader.this.in.clone();
       in.seek(termsStart);
@@ -547,8 +594,8 @@
         if (scratch.get().equals(END) || StringHelper.startsWith(scratch.get(), FIELD)) {
           if (lastDocsStart != -1) {
             b.add(Util.toIntsRef(lastTerm.get(), scratchIntsRef),
-                  outputs.newPair(lastDocsStart,
-                                  outputsInner.newPair((long) docFreq, totalTermFreq)));
+                outputs.newPair(lastDocsStart,
+                    outputsInner.newPair((long) docFreq, totalTermFreq)));
             sumTotalTermFreq += totalTermFreq;
           }
           break;
@@ -564,7 +611,7 @@
         } else if (StringHelper.startsWith(scratch.get(), TERM)) {
           if (lastDocsStart != -1) {
             b.add(Util.toIntsRef(lastTerm.get(), scratchIntsRef), outputs.newPair(lastDocsStart,
-                                                                            outputsInner.newPair((long) docFreq, totalTermFreq)));
+                outputsInner.newPair((long) docFreq, totalTermFreq)));
           }
           lastDocsStart = in.getFilePointer();
           final int len = scratch.length() - TERM.length;
@@ -651,7 +698,7 @@
     public boolean hasPositions() {
       return fieldInfo.getIndexOptions().compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) >= 0;
     }
-    
+
     @Override
     public boolean hasPayloads() {
       return fieldInfo.hasPayloads();
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextFieldsWriter.java b/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextFieldsWriter.java
index 656713d..26a012c 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextFieldsWriter.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextFieldsWriter.java
@@ -17,10 +17,7 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-
 import org.apache.lucene.codecs.FieldsConsumer;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.FieldInfos;
@@ -32,8 +29,10 @@
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.BytesRefBuilder;
 
+import java.io.IOException;
+
 class SimpleTextFieldsWriter extends FieldsConsumer {
-  
+
   private IndexOutput out;
   private final BytesRefBuilder scratch = new BytesRefBuilder();
   private final SegmentWriteState writeState;
@@ -81,10 +80,10 @@
       if (hasPositions) {
         
         if (hasPayloads) {
-          flags = flags | DocsAndPositionsEnum.FLAG_PAYLOADS;
+          flags = flags | DocsEnum.FLAG_PAYLOADS;
         }
         if (hasOffsets) {
-          flags = flags | DocsAndPositionsEnum.FLAG_OFFSETS;
+          flags = flags | DocsEnum.FLAG_OFFSETS;
         }
       } else {
         if (hasFreqs) {
@@ -93,7 +92,6 @@
       }
 
       TermsEnum termsEnum = terms.iterator(null);
-      DocsAndPositionsEnum posEnum = null;
       DocsEnum docsEnum = null;
 
       // for each term in field
@@ -104,8 +102,7 @@
         }
 
         if (hasPositions) {
-          posEnum = termsEnum.docsAndPositions(null, posEnum, flags);
-          docsEnum = posEnum;
+          docsEnum = termsEnum.docsAndPositions(null, docsEnum, flags);
         } else {
           docsEnum = termsEnum.docs(null, docsEnum, flags);
         }
@@ -154,15 +151,15 @@
 
               // for each pos in field+term+doc
               for(int i=0;i<freq;i++) {
-                int position = posEnum.nextPosition();
+                int position = docsEnum.nextPosition();
 
                 write(POS);
                 write(Integer.toString(position));
                 newline();
 
                 if (hasOffsets) {
-                  int startOffset = posEnum.startOffset();
-                  int endOffset = posEnum.endOffset();
+                  int startOffset = docsEnum.startOffset();
+                  int endOffset = docsEnum.endOffset();
                   assert endOffset >= startOffset;
                   assert startOffset >= lastStartOffset: "startOffset=" + startOffset + " lastStartOffset=" + lastStartOffset;
                   lastStartOffset = startOffset;
@@ -174,7 +171,7 @@
                   newline();
                 }
 
-                BytesRef payload = posEnum.getPayload();
+                BytesRef payload = docsEnum.getPayload();
 
                 if (payload != null && payload.length > 0) {
                   assert payload.length != 0;
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextTermVectorsReader.java b/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextTermVectorsReader.java
index 641ff6c..3fbb384 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextTermVectorsReader.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextTermVectorsReader.java
@@ -25,7 +25,6 @@
 import java.util.TreeMap;
 
 import org.apache.lucene.codecs.TermVectorsReader;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.Fields;
 import org.apache.lucene.index.IndexFileNames;
@@ -59,15 +58,15 @@
 public class SimpleTextTermVectorsReader extends TermVectorsReader {
 
   private static final long BASE_RAM_BYTES_USED =
-        RamUsageEstimator.shallowSizeOfInstance(SimpleTextTermVectorsReader.class)
-      + RamUsageEstimator.shallowSizeOfInstance(BytesRef.class)
-      + RamUsageEstimator.shallowSizeOfInstance(CharsRef.class);
+      RamUsageEstimator.shallowSizeOfInstance(SimpleTextTermVectorsReader.class)
+          + RamUsageEstimator.shallowSizeOfInstance(BytesRef.class)
+          + RamUsageEstimator.shallowSizeOfInstance(CharsRef.class);
 
   private long offsets[]; /* docid -> offset in .vec file */
   private IndexInput in;
   private BytesRefBuilder scratch = new BytesRefBuilder();
   private CharsRefBuilder scratchUTF16 = new CharsRefBuilder();
-  
+
   public SimpleTextTermVectorsReader(Directory directory, SegmentInfo si, IOContext context) throws IOException {
     boolean success = false;
     try {
@@ -82,15 +81,15 @@
     }
     readIndex(si.getDocCount());
   }
-  
+
   // used by clone
   SimpleTextTermVectorsReader(long offsets[], IndexInput in) {
     this.offsets = offsets;
     this.in = in;
   }
-  
-  // we don't actually write a .tvx-like index, instead we read the 
-  // vectors file in entirety up-front and save the offsets 
+
+  // we don't actually write a .tvx-like index, instead we read the
+  // vectors file in entirety up-front and save the offsets
   // so we can seek to the data later.
   private void readIndex(int maxDoc) throws IOException {
     ChecksumIndexInput input = new BufferedChecksumIndexInput(in);
@@ -106,7 +105,7 @@
     SimpleTextUtil.checkFooter(input);
     assert upto == offsets.length;
   }
-  
+
   @Override
   public Fields get(int doc) throws IOException {
     SortedMap<String,SimpleTVTerms> fields = new TreeMap<>();
@@ -122,30 +121,30 @@
       assert StringHelper.startsWith(scratch.get(), FIELD);
       // skip fieldNumber:
       parseIntAt(FIELD.length);
-      
+
       readLine();
       assert StringHelper.startsWith(scratch.get(), FIELDNAME);
       String fieldName = readString(FIELDNAME.length, scratch);
-      
+
       readLine();
       assert StringHelper.startsWith(scratch.get(), FIELDPOSITIONS);
       boolean positions = Boolean.parseBoolean(readString(FIELDPOSITIONS.length, scratch));
-      
+
       readLine();
       assert StringHelper.startsWith(scratch.get(), FIELDOFFSETS);
       boolean offsets = Boolean.parseBoolean(readString(FIELDOFFSETS.length, scratch));
-      
+
       readLine();
       assert StringHelper.startsWith(scratch.get(), FIELDPAYLOADS);
       boolean payloads = Boolean.parseBoolean(readString(FIELDPAYLOADS.length, scratch));
-      
+
       readLine();
       assert StringHelper.startsWith(scratch.get(), FIELDTERMCOUNT);
       int termCount = parseIntAt(FIELDTERMCOUNT.length);
-      
+
       SimpleTVTerms terms = new SimpleTVTerms(offsets, positions, payloads);
       fields.put(fieldName, terms);
-      
+
       BytesRefBuilder term = new BytesRefBuilder();
       for (int j = 0; j < termCount; j++) {
         readLine();
@@ -154,14 +153,14 @@
         term.grow(termLength);
         term.setLength(termLength);
         System.arraycopy(scratch.bytes(), TERMTEXT.length, term.bytes(), 0, termLength);
-        
+
         SimpleTVPostings postings = new SimpleTVPostings();
         terms.terms.put(term.toBytesRef(), postings);
-        
+
         readLine();
         assert StringHelper.startsWith(scratch.get(), TERMFREQ);
         postings.freq = parseIntAt(TERMFREQ.length);
-        
+
         if (positions || offsets) {
           if (positions) {
             postings.positions = new int[postings.freq];
@@ -169,12 +168,12 @@
               postings.payloads = new BytesRef[postings.freq];
             }
           }
-        
+
           if (offsets) {
             postings.startOffsets = new int[postings.freq];
             postings.endOffsets = new int[postings.freq];
           }
-          
+
           for (int k = 0; k < postings.freq; k++) {
             if (positions) {
               readLine();
@@ -192,12 +191,12 @@
                 }
               }
             }
-            
+
             if (offsets) {
               readLine();
               assert StringHelper.startsWith(scratch.get(), STARTOFFSET);
               postings.startOffsets[k] = parseIntAt(STARTOFFSET.length);
-              
+
               readLine();
               assert StringHelper.startsWith(scratch.get(), ENDOFFSET);
               postings.endOffsets[k] = parseIntAt(ENDOFFSET.length);
@@ -216,11 +215,11 @@
     }
     return new SimpleTextTermVectorsReader(offsets, in.clone());
   }
-  
+
   @Override
   public void close() throws IOException {
     try {
-      IOUtils.close(in); 
+      IOUtils.close(in);
     } finally {
       in = null;
       offsets = null;
@@ -230,20 +229,20 @@
   private void readLine() throws IOException {
     SimpleTextUtil.readLine(in, scratch);
   }
-  
+
   private int parseIntAt(int offset) {
     scratchUTF16.copyUTF8Bytes(scratch.bytes(), offset, scratch.length()-offset);
     return ArrayUtil.parseInt(scratchUTF16.chars(), 0, scratchUTF16.length());
   }
-  
+
   private String readString(int offset, BytesRefBuilder scratch) {
     scratchUTF16.copyUTF8Bytes(scratch.bytes(), offset, scratch.length()-offset);
     return scratchUTF16.toString();
   }
-  
+
   private class SimpleTVFields extends Fields {
     private final SortedMap<String,SimpleTVTerms> fields;
-    
+
     SimpleTVFields(SortedMap<String,SimpleTVTerms> fields) {
       this.fields = fields;
     }
@@ -263,20 +262,20 @@
       return fields.size();
     }
   }
-  
+
   private static class SimpleTVTerms extends Terms {
     final SortedMap<BytesRef,SimpleTVPostings> terms;
     final boolean hasOffsets;
     final boolean hasPositions;
     final boolean hasPayloads;
-    
+
     SimpleTVTerms(boolean hasOffsets, boolean hasPositions, boolean hasPayloads) {
       this.hasOffsets = hasOffsets;
       this.hasPositions = hasPositions;
       this.hasPayloads = hasPayloads;
       terms = new TreeMap<>();
     }
-    
+
     @Override
     public TermsEnum iterator(TermsEnum reuse) throws IOException {
       // TODO: reuse
@@ -317,13 +316,13 @@
     public boolean hasPositions() {
       return hasPositions;
     }
-    
+
     @Override
     public boolean hasPayloads() {
       return hasPayloads;
     }
   }
-  
+
   private static class SimpleTVPostings {
     private int freq;
     private int positions[];
@@ -331,17 +330,17 @@
     private int endOffsets[];
     private BytesRef payloads[];
   }
-  
+
   private static class SimpleTVTermsEnum extends TermsEnum {
     SortedMap<BytesRef,SimpleTVPostings> terms;
     Iterator<Map.Entry<BytesRef,SimpleTextTermVectorsReader.SimpleTVPostings>> iterator;
     Map.Entry<BytesRef,SimpleTextTermVectorsReader.SimpleTVPostings> current;
-    
+
     SimpleTVTermsEnum(SortedMap<BytesRef,SimpleTVPostings> terms) {
       this.terms = terms;
       this.iterator = terms.entrySet().iterator();
     }
-    
+
     @Override
     public SeekStatus seekCeil(BytesRef text) throws IOException {
       iterator = terms.tailMap(text).entrySet().iterator();
@@ -396,7 +395,7 @@
     }
 
     @Override
-    public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
+    public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
       SimpleTVPostings postings = current.getValue();
       if (postings.positions == null && postings.startOffsets == null) {
         return null;
@@ -407,7 +406,7 @@
       return e;
     }
   }
-  
+
   // note: these two enum classes are exactly like the Default impl...
   private static class SimpleTVDocsEnum extends DocsEnum {
     private boolean didNext;
@@ -422,6 +421,37 @@
     }
 
     @Override
+    public int nextPosition() throws IOException {
+      assert false;
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
+    @Override
     public int docID() {
       return doc;
     }
@@ -447,14 +477,14 @@
       this.doc = -1;
       didNext = false;
     }
-    
+
     @Override
     public long cost() {
       return 1;
     }
   }
-  
-  private static class SimpleTVDocsAndPositionsEnum extends DocsAndPositionsEnum {
+
+  private static class SimpleTVDocsAndPositionsEnum extends DocsEnum {
     private boolean didNext;
     private int doc = -1;
     private int nextPos;
@@ -512,17 +542,29 @@
 
     @Override
     public int nextPosition() {
-      assert (positions != null && nextPos < positions.length) ||
-        startOffsets != null && nextPos < startOffsets.length;
       if (positions != null) {
+        if (nextPos >= positions.length)
+          return NO_MORE_POSITIONS;
         return positions[nextPos++];
       } else {
+        if (nextPos >= startOffsets.length)
+          return NO_MORE_POSITIONS;
         nextPos++;
         return -1;
       }
     }
 
     @Override
+    public int startPosition() throws IOException {
+      return positions[nextPos-1];
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return positions[nextPos-1];
+    }
+
+    @Override
     public int startOffset() {
       if (startOffsets == null) {
         return -1;
@@ -539,7 +581,7 @@
         return endOffsets[nextPos-1];
       }
     }
-    
+
     @Override
     public long cost() {
       return 1;
@@ -550,7 +592,7 @@
   public long ramBytesUsed() {
     return BASE_RAM_BYTES_USED + RamUsageEstimator.sizeOf(offsets);
   }
-  
+
   @Override
   public String toString() {
     return getClass().getSimpleName();
diff --git a/lucene/core/src/java/org/apache/lucene/analysis/Token.java b/lucene/core/src/java/org/apache/lucene/analysis/Token.java
index f0a66f5..13cc5f2 100644
--- a/lucene/core/src/java/org/apache/lucene/analysis/Token.java
+++ b/lucene/core/src/java/org/apache/lucene/analysis/Token.java
@@ -20,7 +20,7 @@
 import org.apache.lucene.analysis.tokenattributes.FlagsAttribute;
 import org.apache.lucene.analysis.tokenattributes.PackedTokenAttributeImpl;
 import org.apache.lucene.analysis.tokenattributes.PayloadAttribute;
-import org.apache.lucene.index.DocsAndPositionsEnum; // for javadoc
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.util.Attribute;
 import org.apache.lucene.util.AttributeFactory;
 import org.apache.lucene.util.AttributeImpl;
@@ -43,7 +43,7 @@
   with type "eos".  The default token type is "word".  
   <p>
   A Token can optionally have metadata (a.k.a. payload) in the form of a variable
-  length byte array. Use {@link DocsAndPositionsEnum#getPayload()} to retrieve the 
+  length byte array. Use {@link DocsEnum#getPayload()} to retrieve the 
   payloads from the index.
   
   <br><br>
diff --git a/lucene/core/src/java/org/apache/lucene/analysis/tokenattributes/PayloadAttribute.java b/lucene/core/src/java/org/apache/lucene/analysis/tokenattributes/PayloadAttribute.java
index 8793c94..0029ccf 100644
--- a/lucene/core/src/java/org/apache/lucene/analysis/tokenattributes/PayloadAttribute.java
+++ b/lucene/core/src/java/org/apache/lucene/analysis/tokenattributes/PayloadAttribute.java
@@ -17,7 +17,7 @@
  * limitations under the License.
  */
 
-import org.apache.lucene.index.DocsAndPositionsEnum; // javadocs
+import org.apache.lucene.index.DocsEnum; // javadocs
 import org.apache.lucene.util.Attribute;
 import org.apache.lucene.util.BytesRef;
 
@@ -33,7 +33,7 @@
  * best to use the minimum number of bytes necessary. Some codec implementations
  * may optimize payload storage when all payloads have the same length.
  * 
- * @see DocsAndPositionsEnum
+ * @see DocsEnum
  */
 public interface PayloadAttribute extends Attribute {
   /**
diff --git a/lucene/core/src/java/org/apache/lucene/analysis/tokenattributes/PositionIncrementAttribute.java b/lucene/core/src/java/org/apache/lucene/analysis/tokenattributes/PositionIncrementAttribute.java
index 9afd2f9..a7a7cd9 100644
--- a/lucene/core/src/java/org/apache/lucene/analysis/tokenattributes/PositionIncrementAttribute.java
+++ b/lucene/core/src/java/org/apache/lucene/analysis/tokenattributes/PositionIncrementAttribute.java
@@ -43,7 +43,7 @@
  *
  * </ul>
  * 
- * @see org.apache.lucene.index.DocsAndPositionsEnum
+ * @see org.apache.lucene.index.DocsEnum
  */
 public interface PositionIncrementAttribute extends Attribute {
   /** Set the position increment. The default value is one.
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/PostingsReaderBase.java b/lucene/core/src/java/org/apache/lucene/codecs/PostingsReaderBase.java
index 5681c19..c8d78fa 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/PostingsReaderBase.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/PostingsReaderBase.java
@@ -20,7 +20,6 @@
 import java.io.Closeable;
 import java.io.IOException;
 
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.SegmentReadState;
@@ -32,7 +31,7 @@
 /** The core terms dictionaries (BlockTermsReader,
  *  BlockTreeTermsReader) interact with a single instance
  *  of this class to manage creation of {@link DocsEnum} and
- *  {@link DocsAndPositionsEnum} instances.  It provides an
+ *  {@link DocsEnum} instances.  It provides an
  *  IndexInput (termsIn) where this class may read any
  *  previously stored data that it had written in its
  *  corresponding {@link PostingsWriterBase} at indexing
@@ -70,8 +69,9 @@
 
   /** Must fully consume state, since after this call that
    *  TermState may be reused. */
-  public abstract DocsAndPositionsEnum docsAndPositions(FieldInfo fieldInfo, BlockTermState state, Bits skipDocs, DocsAndPositionsEnum reuse,
+  public abstract DocsEnum docsAndPositions(FieldInfo fieldInfo, BlockTermState state, Bits skipDocs, DocsEnum reuse,
                                                         int flags) throws IOException;
+  // TODO merge this into docs()?
   
   /** 
    * Checks consistency of this reader.
@@ -81,7 +81,7 @@
    * @lucene.internal
    */
   public abstract void checkIntegrity() throws IOException;
-  
+
   @Override
   public abstract void close() throws IOException;
 }
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/PostingsWriterBase.java b/lucene/core/src/java/org/apache/lucene/codecs/PostingsWriterBase.java
index 0dc7bb5..82063a0 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/PostingsWriterBase.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/PostingsWriterBase.java
@@ -17,12 +17,8 @@
  * limitations under the License.
  */
 
-import java.io.Closeable;
-import java.io.IOException;
-
 import org.apache.lucene.codecs.blocktree.BlockTreeTermsWriter;
-import org.apache.lucene.index.DocsAndPositionsEnum; // javadocs
-import org.apache.lucene.index.DocsEnum; // javadocs
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.SegmentWriteState;
 import org.apache.lucene.index.TermsEnum;
@@ -31,6 +27,9 @@
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.FixedBitSet;
 
+import java.io.Closeable;
+import java.io.IOException;
+
 /**
  * Class that plugs into term dictionaries, such as {@link
  * BlockTreeTermsWriter}, and handles writing postings.
@@ -54,8 +53,8 @@
   public abstract void init(IndexOutput termsOut, SegmentWriteState state) throws IOException;
 
   /** Write all postings for one term; use the provided
-   *  {@link TermsEnum} to pull a {@link DocsEnum} or {@link
-   *  DocsAndPositionsEnum}.  This method should not
+   *  {@link TermsEnum} to pull a {@link DocsEnum}.
+   *  This method should not
    *  re-position the {@code TermsEnum}!  It is already
    *  positioned on the term that should be written.  This
    *  method must set the bit in the provided {@link
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/PushPostingsWriterBase.java b/lucene/core/src/java/org/apache/lucene/codecs/PushPostingsWriterBase.java
index 35ebba1..a0bfab5 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/PushPostingsWriterBase.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/PushPostingsWriterBase.java
@@ -19,7 +19,6 @@
 
 import java.io.IOException;
 
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.IndexOptions;
@@ -43,7 +42,6 @@
 
   // Reused in writeTerm
   private DocsEnum docsEnum;
-  private DocsAndPositionsEnum posEnum;
   private int enumFlags;
 
   /** {@link FieldInfo} of current field being written. */
@@ -103,15 +101,15 @@
       enumFlags = DocsEnum.FLAG_FREQS;
     } else if (writeOffsets == false) {
       if (writePayloads) {
-        enumFlags = DocsAndPositionsEnum.FLAG_PAYLOADS;
+        enumFlags = DocsEnum.FLAG_PAYLOADS;
       } else {
-        enumFlags = 0;
+        enumFlags = DocsEnum.FLAG_POSITIONS;
       }
     } else {
       if (writePayloads) {
-        enumFlags = DocsAndPositionsEnum.FLAG_PAYLOADS | DocsAndPositionsEnum.FLAG_OFFSETS;
+        enumFlags = DocsEnum.FLAG_PAYLOADS | DocsEnum.FLAG_OFFSETS;
       } else {
-        enumFlags = DocsAndPositionsEnum.FLAG_OFFSETS;
+        enumFlags = DocsEnum.FLAG_OFFSETS;
       }
     }
 
@@ -124,8 +122,7 @@
     if (writePositions == false) {
       docsEnum = termsEnum.docs(null, docsEnum, enumFlags);
     } else {
-      posEnum = termsEnum.docsAndPositions(null, posEnum, enumFlags);
-      docsEnum = posEnum;
+      docsEnum = termsEnum.docsAndPositions(null, docsEnum, enumFlags);
     }
     assert docsEnum != null;
 
@@ -149,13 +146,13 @@
 
       if (writePositions) {
         for(int i=0;i<freq;i++) {
-          int pos = posEnum.nextPosition();
-          BytesRef payload = writePayloads ? posEnum.getPayload() : null;
+          int pos = docsEnum.nextPosition();
+          BytesRef payload = writePayloads ? docsEnum.getPayload() : null;
           int startOffset;
           int endOffset;
           if (writeOffsets) {
-            startOffset = posEnum.startOffset();
-            endOffset = posEnum.endOffset();
+            startOffset = docsEnum.startOffset();
+            endOffset = docsEnum.endOffset();
           } else {
             startOffset = -1;
             endOffset = -1;
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/TermVectorsReader.java b/lucene/core/src/java/org/apache/lucene/codecs/TermVectorsReader.java
index f31a377..7f06458 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/TermVectorsReader.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/TermVectorsReader.java
@@ -21,7 +21,7 @@
 import java.io.IOException;
 
 import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; // javadocs
-import org.apache.lucene.index.DocsAndPositionsEnum; // javadocs
+import org.apache.lucene.index.DocsEnum; // javadocs
 import org.apache.lucene.index.Fields;
 import org.apache.lucene.util.Accountable;
 
@@ -40,7 +40,7 @@
   /** Returns term vectors for this document, or null if
    *  term vectors were not indexed. If offsets are
    *  available they are in an {@link OffsetAttribute}
-   *  available from the {@link DocsAndPositionsEnum}. */
+   *  available from the {@link DocsEnum}. */
   public abstract Fields get(int doc) throws IOException;
   
   /** 
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/TermVectorsWriter.java b/lucene/core/src/java/org/apache/lucene/codecs/TermVectorsWriter.java
index 4ae2237..b5f67fb 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/TermVectorsWriter.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/TermVectorsWriter.java
@@ -17,11 +17,7 @@
  * limitations under the License.
  */
 
-import java.io.Closeable;
-import java.io.IOException;
-import java.util.Iterator;
-
-import org.apache.lucene.index.DocsAndPositionsEnum;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.FieldInfos;
 import org.apache.lucene.index.Fields;
@@ -34,6 +30,10 @@
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.BytesRefBuilder;
 
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Iterator;
+
 /**
  * Codec API for writing term vectors:
  * <p>
@@ -226,7 +226,7 @@
     String lastFieldName = null;
     
     TermsEnum termsEnum = null;
-    DocsAndPositionsEnum docsAndPositionsEnum = null;
+    DocsEnum docsAndPositionsEnum = null;
     
     int fieldCount = 0;
     for(String fieldName : vectors) {
@@ -283,7 +283,7 @@
             
             final BytesRef payload = docsAndPositionsEnum.getPayload();
 
-            assert !hasPositions || pos >= 0;
+            assert !hasPositions || pos >= 0 ;
             addPosition(pos, startOffset, endOffset, payload);
           }
         }
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/blocktree/IntersectTermsEnum.java b/lucene/core/src/java/org/apache/lucene/codecs/blocktree/IntersectTermsEnum.java
index 952d226..1508f3d 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/blocktree/IntersectTermsEnum.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/blocktree/IntersectTermsEnum.java
@@ -17,9 +17,6 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexOptions;
 import org.apache.lucene.index.TermState;
@@ -36,6 +33,8 @@
 import org.apache.lucene.util.fst.FST;
 import org.apache.lucene.util.fst.Outputs;
 
+import java.io.IOException;
+
 // NOTE: cannot seek!
 final class IntersectTermsEnum extends TermsEnum {
   final IndexInput in;
@@ -209,7 +208,7 @@
   }
 
   @Override
-  public DocsAndPositionsEnum docsAndPositions(Bits skipDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
+  public DocsEnum docsAndPositions(Bits skipDocs, DocsEnum reuse, int flags) throws IOException {
     if (fr.fieldInfo.getIndexOptions().compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) < 0) {
       // Positions were not indexed:
       return null;
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/blocktree/SegmentTermsEnum.java b/lucene/core/src/java/org/apache/lucene/codecs/blocktree/SegmentTermsEnum.java
index df67b07..21acefb 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/blocktree/SegmentTermsEnum.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/blocktree/SegmentTermsEnum.java
@@ -17,11 +17,7 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.io.PrintStream;
-
 import org.apache.lucene.codecs.BlockTermState;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexOptions;
 import org.apache.lucene.index.TermState;
@@ -36,6 +32,9 @@
 import org.apache.lucene.util.fst.FST;
 import org.apache.lucene.util.fst.Util;
 
+import java.io.IOException;
+import java.io.PrintStream;
+
 /** Iterates through terms in this field */
 final class SegmentTermsEnum extends TermsEnum {
 
@@ -994,7 +993,7 @@
   }
 
   @Override
-  public DocsAndPositionsEnum docsAndPositions(Bits skipDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
+  public DocsEnum docsAndPositions(Bits skipDocs, DocsEnum reuse, int flags) throws IOException {
     if (fr.fieldInfo.getIndexOptions().compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) < 0) {
       // Positions were not indexed:
       return null;
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingTermVectorsReader.java b/lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingTermVectorsReader.java
index a545e9a..aa6b7d0 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingTermVectorsReader.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingTermVectorsReader.java
@@ -17,18 +17,6 @@
  * limitations under the License.
  */
 
-import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.BLOCK_SIZE;
-import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.CODEC_SFX_DAT;
-import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.CODEC_SFX_IDX;
-import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.FLAGS_BITS;
-import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.OFFSETS;
-import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.PAYLOADS;
-import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.POSITIONS;
-import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.VECTORS_EXTENSION;
-import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.VECTORS_INDEX_EXTENSION;
-import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.VERSION_CURRENT;
-import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.VERSION_START;
-
 import java.io.Closeable;
 import java.io.IOException;
 import java.util.Collections;
@@ -38,7 +26,6 @@
 import org.apache.lucene.codecs.CodecUtil;
 import org.apache.lucene.codecs.TermVectorsReader;
 import org.apache.lucene.index.CorruptIndexException;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.FieldInfos;
@@ -63,6 +50,17 @@
 import org.apache.lucene.util.packed.BlockPackedReaderIterator;
 import org.apache.lucene.util.packed.PackedInts;
 
+import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.BLOCK_SIZE;
+import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.CODEC_SFX_DAT;
+import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.CODEC_SFX_IDX;
+import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.FLAGS_BITS;
+import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.OFFSETS;
+import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.PAYLOADS;
+import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.POSITIONS;
+import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.VECTORS_EXTENSION;
+import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.VECTORS_INDEX_EXTENSION;
+import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.VERSION_CURRENT;
+import static org.apache.lucene.codecs.compressing.CompressingTermVectorsWriter.VERSION_START;
 
 /**
  * {@link TermVectorsReader} for {@link CompressingTermVectorsFormat}.
@@ -915,17 +913,17 @@
     }
 
     @Override
-    public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
+    public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
       if (positions == null && startOffsets == null) {
         return null;
       }
       // TODO: slightly sheisty
-      return (DocsAndPositionsEnum) docs(liveDocs, reuse, flags);
+      return docs(liveDocs, reuse, flags);
     }
 
   }
 
-  private static class TVDocsEnum extends DocsAndPositionsEnum {
+  private static class TVDocsEnum extends DocsEnum {
 
     private Bits liveDocs;
     private int doc = -1;
@@ -1000,6 +998,16 @@
     }
 
     @Override
+    public int startPosition() throws IOException {
+      return positions[positionIndex + i];
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return positions[positionIndex + i];
+    }
+
+    @Override
     public int startOffset() throws IOException {
       checkPosition();
       if (startOffsets == null) {
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene50/Lucene50DocValuesProducer.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene50/Lucene50DocValuesProducer.java
index efae5d4..720769a 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/lucene50/Lucene50DocValuesProducer.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene50/Lucene50DocValuesProducer.java
@@ -17,41 +17,11 @@
  * limitations under the License.
  */
 
-import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.BINARY_FIXED_UNCOMPRESSED;
-import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.BINARY_PREFIX_COMPRESSED;
-import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.BINARY_VARIABLE_UNCOMPRESSED;
-import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.CONST_COMPRESSED;
-import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.DELTA_COMPRESSED;
-import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.GCD_COMPRESSED;
-import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.MONOTONIC_COMPRESSED;
-import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.SORTED_SINGLE_VALUED;
-import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.SORTED_WITH_ADDRESSES;
-import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.TABLE_COMPRESSED;
-import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.INTERVAL_SHIFT;
-import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.INTERVAL_COUNT;
-import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.INTERVAL_MASK;
-import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.REVERSE_INTERVAL_SHIFT;
-import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.REVERSE_INTERVAL_MASK;
-import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.BLOCK_INTERVAL_SHIFT;
-import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.BLOCK_INTERVAL_MASK;
-import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.ALL_LIVE;
-import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.ALL_MISSING;
-
-import java.io.Closeable; // javadocs
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicLong;
-
 import org.apache.lucene.codecs.CodecUtil;
 import org.apache.lucene.codecs.DocValuesProducer;
 import org.apache.lucene.index.BinaryDocValues;
 import org.apache.lucene.index.CorruptIndexException;
 import org.apache.lucene.index.DocValues;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.FieldInfos;
@@ -77,6 +47,35 @@
 import org.apache.lucene.util.packed.DirectReader;
 import org.apache.lucene.util.packed.MonotonicBlockPackedReader;
 
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.ALL_LIVE;
+import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.ALL_MISSING;
+import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.BINARY_FIXED_UNCOMPRESSED;
+import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.BINARY_PREFIX_COMPRESSED;
+import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.BINARY_VARIABLE_UNCOMPRESSED;
+import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.BLOCK_INTERVAL_MASK;
+import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.BLOCK_INTERVAL_SHIFT;
+import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.CONST_COMPRESSED;
+import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.DELTA_COMPRESSED;
+import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.GCD_COMPRESSED;
+import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.INTERVAL_COUNT;
+import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.INTERVAL_MASK;
+import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.INTERVAL_SHIFT;
+import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.MONOTONIC_COMPRESSED;
+import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.REVERSE_INTERVAL_MASK;
+import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.REVERSE_INTERVAL_SHIFT;
+import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.SORTED_SINGLE_VALUED;
+import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.SORTED_WITH_ADDRESSES;
+import static org.apache.lucene.codecs.lucene50.Lucene50DocValuesConsumer.TABLE_COMPRESSED;
+
 /** reader for {@link Lucene50DocValuesFormat} */
 class Lucene50DocValuesProducer extends DocValuesProducer implements Closeable {
   private final Map<String,NumericEntry> numerics = new HashMap<>();
@@ -1145,7 +1144,7 @@
       }
       
       @Override
-      public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
+      public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
         throw new UnsupportedOperationException();
       }
     }
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene50/Lucene50PostingsReader.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene50/Lucene50PostingsReader.java
index 5b4fc05..605dd34 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/lucene50/Lucene50PostingsReader.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene50/Lucene50PostingsReader.java
@@ -24,7 +24,6 @@
 import org.apache.lucene.codecs.CodecUtil;
 import org.apache.lucene.codecs.PostingsReaderBase;
 import org.apache.lucene.codecs.lucene50.Lucene50PostingsFormat.IntBlockTermState;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.IndexFileNames;
@@ -194,30 +193,38 @@
     
   @Override
   public DocsEnum docs(FieldInfo fieldInfo, BlockTermState termState, Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
-    BlockDocsEnum docsEnum;
-    if (reuse instanceof BlockDocsEnum) {
-      docsEnum = (BlockDocsEnum) reuse;
-      if (!docsEnum.canReuse(docIn, fieldInfo)) {
+    if ((flags & DocsEnum.FLAG_POSITIONS) < DocsEnum.FLAG_POSITIONS) {
+      BlockDocsEnum docsEnum;
+      if (reuse instanceof BlockDocsEnum) {
+        docsEnum = (BlockDocsEnum) reuse;
+        if (!docsEnum.canReuse(docIn, fieldInfo)) {
+          docsEnum = new BlockDocsEnum(fieldInfo);
+        }
+      } else {
         docsEnum = new BlockDocsEnum(fieldInfo);
       }
-    } else {
-      docsEnum = new BlockDocsEnum(fieldInfo);
+      return docsEnum.reset(liveDocs, (IntBlockTermState) termState, flags);
     }
-    return docsEnum.reset(liveDocs, (IntBlockTermState) termState, flags);
+
+    return docsAndPositions(fieldInfo, termState, liveDocs, reuse, flags);
   }
 
   // TODO: specialize to liveDocs vs not
   
   @Override
-  public DocsAndPositionsEnum docsAndPositions(FieldInfo fieldInfo, BlockTermState termState, Bits liveDocs,
-                                               DocsAndPositionsEnum reuse, int flags)
+  public DocsEnum docsAndPositions(FieldInfo fieldInfo, BlockTermState termState, Bits liveDocs,
+                                               DocsEnum reuse, int flags)
     throws IOException {
 
+    boolean indexHasPositions = fieldInfo.getIndexOptions().compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) >= 0;
     boolean indexHasOffsets = fieldInfo.getIndexOptions().compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS) >= 0;
     boolean indexHasPayloads = fieldInfo.hasPayloads();
 
-    if ((!indexHasOffsets || (flags & DocsAndPositionsEnum.FLAG_OFFSETS) == 0) &&
-        (!indexHasPayloads || (flags & DocsAndPositionsEnum.FLAG_PAYLOADS) == 0)) {
+    if (!indexHasPositions)
+      return null;
+
+    if ((!indexHasOffsets || (flags & DocsEnum.FLAG_OFFSETS) == 0) &&
+        (!indexHasPayloads || (flags & DocsEnum.FLAG_PAYLOADS) == 0)) {
       BlockDocsAndPositionsEnum docsAndPositionsEnum;
       if (reuse instanceof BlockDocsAndPositionsEnum) {
         docsAndPositionsEnum = (BlockDocsAndPositionsEnum) reuse;
@@ -337,6 +344,37 @@
     }
 
     @Override
+    public int nextPosition() throws IOException {
+      assert false;   // shouldn't be calling nextPosition() on this
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
+    @Override
     public int docID() {
       return doc;
     }
@@ -472,7 +510,7 @@
   }
 
 
-  final class BlockDocsAndPositionsEnum extends DocsAndPositionsEnum {
+  final class BlockDocsAndPositionsEnum extends DocsEnum {
     
     private final byte[] encoded;
 
@@ -550,7 +588,7 @@
         indexHasPayloads == fieldInfo.hasPayloads();
     }
     
-    public DocsAndPositionsEnum reset(Bits liveDocs, IntBlockTermState termState) throws IOException {
+    public DocsEnum reset(Bits liveDocs, IntBlockTermState termState) throws IOException {
       this.liveDocs = liveDocs;
 
       docFreq = termState.docFreq;
@@ -769,6 +807,10 @@
 
     @Override
     public int nextPosition() throws IOException {
+
+      if (posPendingCount == 0)
+        return NO_MORE_POSITIONS;
+
       if (posPendingFP != -1) {
         posIn.seek(posPendingFP);
         posPendingFP = -1;
@@ -792,6 +834,16 @@
     }
 
     @Override
+    public int startPosition() {
+      return position;
+    }
+
+    @Override
+    public int endPosition() {
+      return position;
+    }
+
+    @Override
     public int startOffset() {
       return -1;
     }
@@ -813,7 +865,7 @@
   }
 
   // Also handles payloads + offsets
-  final class EverythingEnum extends DocsAndPositionsEnum {
+  final class EverythingEnum extends DocsEnum {
     
     private final byte[] encoded;
 
@@ -960,8 +1012,8 @@
         lastPosBlockFP = posTermStartFP + termState.lastPosBlockOffset;
       }
 
-      this.needsOffsets = (flags & DocsAndPositionsEnum.FLAG_OFFSETS) != 0;
-      this.needsPayloads = (flags & DocsAndPositionsEnum.FLAG_PAYLOADS) != 0;
+      this.needsOffsets = (flags & DocsEnum.FLAG_OFFSETS) != 0;
+      this.needsPayloads = (flags & DocsEnum.FLAG_PAYLOADS) != 0;
 
       doc = -1;
       accum = 0;
@@ -1228,6 +1280,9 @@
 
     @Override
     public int nextPosition() throws IOException {
+      if (posPendingCount == 0)
+        return NO_MORE_POSITIONS;
+
       if (posPendingFP != -1) {
         posIn.seek(posPendingFP);
         posPendingFP = -1;
@@ -1272,6 +1327,16 @@
     }
 
     @Override
+    public int startPosition() {
+      return position;
+    }
+
+    @Override
+    public int endPosition() {
+      return position;
+    }
+
+    @Override
     public int startOffset() {
       return startOffset;
     }
diff --git a/lucene/core/src/java/org/apache/lucene/index/CheckIndex.java b/lucene/core/src/java/org/apache/lucene/index/CheckIndex.java
index 299b488..74fbc63 100644
--- a/lucene/core/src/java/org/apache/lucene/index/CheckIndex.java
+++ b/lucene/core/src/java/org/apache/lucene/index/CheckIndex.java
@@ -884,7 +884,7 @@
     
     DocsEnum docs = null;
     DocsEnum docsAndFreqs = null;
-    DocsAndPositionsEnum postings = null;
+    DocsEnum postings = null;
     
     String lastField = null;
     for (String field : fields) {
@@ -1807,11 +1807,11 @@
       }
 
       DocsEnum docs = null;
-      DocsAndPositionsEnum postings = null;
+      DocsEnum postings = null;
 
       // Only used if crossCheckTermVectors is true:
       DocsEnum postingsDocs = null;
-      DocsAndPositionsEnum postingsPostings = null;
+      DocsEnum postingsPostings = null;
 
       final Bits liveDocs = reader.getLiveDocs();
 
diff --git a/lucene/core/src/java/org/apache/lucene/index/DocsAndPositionsEnum.java b/lucene/core/src/java/org/apache/lucene/index/DocsAndPositionsEnum.java
deleted file mode 100644
index 60ac2bb..0000000
--- a/lucene/core/src/java/org/apache/lucene/index/DocsAndPositionsEnum.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package org.apache.lucene.index;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import java.io.IOException;
-
-import org.apache.lucene.util.Bits; // javadocs
-import org.apache.lucene.util.BytesRef;
-
-/** Also iterates through positions. */
-public abstract class DocsAndPositionsEnum extends DocsEnum {
-  
-  /** Flag to pass to {@link TermsEnum#docsAndPositions(Bits,DocsAndPositionsEnum,int)}
-   *  if you require offsets in the returned enum. */
-  public static final int FLAG_OFFSETS = 0x1;
-
-  /** Flag to pass to  {@link TermsEnum#docsAndPositions(Bits,DocsAndPositionsEnum,int)}
-   *  if you require payloads in the returned enum. */
-  public static final int FLAG_PAYLOADS = 0x2;
-
-  /** Sole constructor. (For invocation by subclass 
-   * constructors, typically implicit.) */
-  protected DocsAndPositionsEnum() {
-  }
-
-  /** Returns the next position.  You should only call this
-   *  up to {@link DocsEnum#freq()} times else
-   *  the behavior is not defined.  If positions were not
-   *  indexed this will return -1; this only happens if
-   *  offsets were indexed and you passed needsOffset=true
-   *  when pulling the enum.  */
-  public abstract int nextPosition() throws IOException;
-
-  /** Returns start offset for the current position, or -1
-   *  if offsets were not indexed. */
-  public abstract int startOffset() throws IOException;
-
-  /** Returns end offset for the current position, or -1 if
-   *  offsets were not indexed. */
-  public abstract int endOffset() throws IOException;
-
-  /** Returns the payload at this position, or null if no
-   *  payload was indexed. You should not modify anything 
-   *  (neither members of the returned BytesRef nor bytes 
-   *  in the byte[]). */
-  public abstract BytesRef getPayload() throws IOException;
-}
diff --git a/lucene/core/src/java/org/apache/lucene/index/DocsEnum.java b/lucene/core/src/java/org/apache/lucene/index/DocsEnum.java
index bfc00a3..ce2eb20 100644
--- a/lucene/core/src/java/org/apache/lucene/index/DocsEnum.java
+++ b/lucene/core/src/java/org/apache/lucene/index/DocsEnum.java
@@ -21,7 +21,8 @@
 
 import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.util.AttributeSource;
-import org.apache.lucene.util.Bits; // javadocs
+import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
 
 /** Iterates through the documents and term freqs.
  *  NOTE: you must first call {@link #nextDoc} before using
@@ -30,9 +31,7 @@
   
   /**
    * Flag to pass to {@link TermsEnum#docs(Bits,DocsEnum,int)} if you don't
-   * require term frequencies in the returned enum. When passed to
-   * {@link TermsEnum#docsAndPositions(Bits,DocsAndPositionsEnum,int)} means
-   * that no offsets and payloads will be returned.
+   * require term frequencies in the returned enum.
    */
   public static final int FLAG_NONE = 0x0;
 
@@ -40,6 +39,20 @@
    *  if you require term frequencies in the returned enum. */
   public static final int FLAG_FREQS = 0x1;
 
+  /** Flag to pass to {@link TermsEnum#docs(Bits,DocsEnum,int)}
+   * if you require term positions in the returned enum. */
+  public static final int FLAG_POSITIONS = 0x3;
+  
+  /** Flag to pass to {@link TermsEnum#docs(Bits,DocsEnum,int)}
+   *  if you require offsets in the returned enum. */
+  public static final int FLAG_OFFSETS = 0x7;
+
+  /** Flag to pass to  {@link TermsEnum#docs(Bits,DocsEnum,int)}
+   *  if you require payloads in the returned enum. */
+  public static final int FLAG_PAYLOADS = 0xB;
+
+  public static final int NO_MORE_POSITIONS = -1;
+
   private AttributeSource atts = null;
 
   /** Sole constructor. (For invocation by subclass 
@@ -64,4 +77,31 @@
     if (atts == null) atts = new AttributeSource();
     return atts;
   }
+
+  /**
+   * Returns the next position.  If there are no more
+   * positions, or the iterator does not support positions,
+   * this will return DocsEnum.NO_MORE_POSITIONS */
+  public abstract int nextPosition() throws IOException;
+
+  /** Returns current starting position, or NO_MORE_POSITIONS if not supported */
+  public abstract int startPosition() throws IOException;
+
+  /** Returns current ending position, or NO_MORE_POSITIONS if not supported */
+  public abstract int endPosition() throws IOException;
+
+  /** Returns start offset for the current position, or -1
+   *  if offsets were not indexed. */
+  public abstract int startOffset() throws IOException;
+
+  /** Returns end offset for the current position, or -1 if
+   *  offsets were not indexed. */
+  public abstract int endOffset() throws IOException;
+
+  /** Returns the payload at this position, or null if no
+   *  payload was indexed. You should not modify anything 
+   *  (neither members of the returned BytesRef nor bytes 
+   *  in the byte[]). */
+  public abstract BytesRef getPayload() throws IOException;
+
 }
diff --git a/lucene/core/src/java/org/apache/lucene/index/FilterLeafReader.java b/lucene/core/src/java/org/apache/lucene/index/FilterLeafReader.java
index 47422a9..d8f4780 100644
--- a/lucene/core/src/java/org/apache/lucene/index/FilterLeafReader.java
+++ b/lucene/core/src/java/org/apache/lucene/index/FilterLeafReader.java
@@ -17,14 +17,14 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.util.Iterator;
-
 import org.apache.lucene.search.CachingWrapperFilter;
 import org.apache.lucene.util.AttributeSource;
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.BytesRef;
 
+import java.io.IOException;
+import java.util.Iterator;
+
 /**  A <code>FilterLeafReader</code> contains another LeafReader, which it
  * uses as its basic source of data, possibly transforming the data along the
  * way or providing additional functionality. The class
@@ -220,7 +220,7 @@
     }
 
     @Override
-    public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
+    public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
       return in.docsAndPositions(liveDocs, reuse, flags);
     }
   }
@@ -267,58 +267,21 @@
     }
 
     @Override
-    public long cost() {
-      return in.cost();
-    }
-  }
-
-  /** Base class for filtering {@link DocsAndPositionsEnum} implementations. */
-  public static class FilterDocsAndPositionsEnum extends DocsAndPositionsEnum {
-    /** The underlying DocsAndPositionsEnum instance. */
-    protected final DocsAndPositionsEnum in;
-
-    /**
-     * Create a new FilterDocsAndPositionsEnum
-     * @param in the underlying DocsAndPositionsEnum instance.
-     */
-    public FilterDocsAndPositionsEnum(DocsAndPositionsEnum in) {
-      if (in == null) {
-        throw new NullPointerException("incoming DocsAndPositionsEnum cannot be null");
-      }
-      this.in = in;
-    }
-
-    @Override
-    public AttributeSource attributes() {
-      return in.attributes();
-    }
-
-    @Override
-    public int docID() {
-      return in.docID();
-    }
-
-    @Override
-    public int freq() throws IOException {
-      return in.freq();
-    }
-
-    @Override
-    public int nextDoc() throws IOException {
-      return in.nextDoc();
-    }
-
-    @Override
-    public int advance(int target) throws IOException {
-      return in.advance(target);
-    }
-
-    @Override
     public int nextPosition() throws IOException {
       return in.nextPosition();
     }
 
     @Override
+    public int startPosition() throws IOException {
+      return in.startPosition();
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return in.endPosition();
+    }
+
+    @Override
     public int startOffset() throws IOException {
       return in.startOffset();
     }
@@ -332,7 +295,7 @@
     public BytesRef getPayload() throws IOException {
       return in.getPayload();
     }
-    
+
     @Override
     public long cost() {
       return in.cost();
diff --git a/lucene/core/src/java/org/apache/lucene/index/FilteredTermsEnum.java b/lucene/core/src/java/org/apache/lucene/index/FilteredTermsEnum.java
index b6bfcc4..df4e0d8 100644
--- a/lucene/core/src/java/org/apache/lucene/index/FilteredTermsEnum.java
+++ b/lucene/core/src/java/org/apache/lucene/index/FilteredTermsEnum.java
@@ -184,7 +184,7 @@
   }
     
   @Override
-  public DocsAndPositionsEnum docsAndPositions(Bits bits, DocsAndPositionsEnum reuse, int flags) throws IOException {
+  public DocsEnum docsAndPositions(Bits bits, DocsEnum reuse, int flags) throws IOException {
     return tenum.docsAndPositions(bits, reuse, flags);
   }
   
diff --git a/lucene/core/src/java/org/apache/lucene/index/FreqProxFields.java b/lucene/core/src/java/org/apache/lucene/index/FreqProxFields.java
index fe5d31f..74dbff5 100644
--- a/lucene/core/src/java/org/apache/lucene/index/FreqProxFields.java
+++ b/lucene/core/src/java/org/apache/lucene/index/FreqProxFields.java
@@ -24,7 +24,7 @@
 import java.util.Map;
 
 import org.apache.lucene.index.FreqProxTermsWriterPerField.FreqProxPostingsArray;
-import org.apache.lucene.util.AttributeSource; // javadocs
+import org.apache.lucene.util.AttributeSource;
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.BytesRefBuilder;
@@ -256,7 +256,7 @@
     }
 
     @Override
-    public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) {
+    public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) {
       if (liveDocs != null) {
         throw new IllegalArgumentException("liveDocs must be null");
       }
@@ -268,7 +268,7 @@
         throw new IllegalArgumentException("did not index positions");
       }
 
-      if (!terms.hasOffsets && (flags & DocsAndPositionsEnum.FLAG_OFFSETS) != 0) {
+      if (!terms.hasOffsets && (flags & DocsEnum.FLAG_OFFSETS) == DocsEnum.FLAG_OFFSETS) {
         // Caller wants offsets but we didn't index them;
         // don't lie:
         throw new IllegalArgumentException("did not index offsets");
@@ -348,6 +348,36 @@
     }
 
     @Override
+    public int nextPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
+    @Override
     public int nextDoc() throws IOException {
       if (reader.eof()) {
         if (ended) {
@@ -389,7 +419,7 @@
     }
   }
 
-  private static class FreqProxDocsAndPositionsEnum extends DocsAndPositionsEnum {
+  private static class FreqProxDocsAndPositionsEnum extends DocsEnum {
 
     final FreqProxTermsWriterPerField terms;
     final FreqProxPostingsArray postingsArray;
@@ -501,6 +531,16 @@
     }
 
     @Override
+    public int startPosition() throws IOException {
+      return pos;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return pos;
+    }
+
+    @Override
     public int startOffset() {
       if (!readOffsets) {
         throw new IllegalStateException("offsets were not indexed");
diff --git a/lucene/core/src/java/org/apache/lucene/index/LeafReader.java b/lucene/core/src/java/org/apache/lucene/index/LeafReader.java
index 2f9c604..a6a1dce 100644
--- a/lucene/core/src/java/org/apache/lucene/index/LeafReader.java
+++ b/lucene/core/src/java/org/apache/lucene/index/LeafReader.java
@@ -17,11 +17,10 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-
-import org.apache.lucene.index.IndexReader.ReaderClosedListener;
 import org.apache.lucene.util.Bits;
 
+import java.io.IOException;
+
 /** {@code LeafReader} is an abstract class, providing an interface for accessing an
  index.  Search of an index is done entirely through this abstract interface,
  so that any subclass which implements it is searchable. IndexReaders implemented
@@ -222,11 +221,11 @@
     return null;
   }
 
-  /** Returns {@link DocsAndPositionsEnum} for the specified
+  /** Returns {@link DocsEnum} for the specified
    *  term.  This will return null if the
    *  field or term does not exist or positions weren't indexed.
-   *  @see TermsEnum#docsAndPositions(Bits, DocsAndPositionsEnum) */
-  public final DocsAndPositionsEnum termPositionsEnum(Term term) throws IOException {
+   *  @see TermsEnum#docsAndPositions(Bits, DocsEnum) */
+  public final DocsEnum termPositionsEnum(Term term) throws IOException {
     assert term.field() != null;
     assert term.bytes() != null;
     final Terms terms = terms(term.field());
diff --git a/lucene/core/src/java/org/apache/lucene/index/MappedMultiFields.java b/lucene/core/src/java/org/apache/lucene/index/MappedMultiFields.java
index fad0eed..1e2d3fe 100644
--- a/lucene/core/src/java/org/apache/lucene/index/MappedMultiFields.java
+++ b/lucene/core/src/java/org/apache/lucene/index/MappedMultiFields.java
@@ -17,10 +17,10 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-
 import org.apache.lucene.util.Bits;
 
+import java.io.IOException;
+
 import static org.apache.lucene.index.FilterLeafReader.FilterFields;
 import static org.apache.lucene.index.FilterLeafReader.FilterTerms;
 import static org.apache.lucene.index.FilterLeafReader.FilterTermsEnum;
@@ -120,7 +120,7 @@
     }
 
     @Override
-    public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
+    public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
       if (liveDocs != null) {
         throw new IllegalArgumentException("liveDocs must be null");
       }
diff --git a/lucene/core/src/java/org/apache/lucene/index/MappingMultiDocsAndPositionsEnum.java b/lucene/core/src/java/org/apache/lucene/index/MappingMultiDocsAndPositionsEnum.java
index bcc3735..e809dc0 100644
--- a/lucene/core/src/java/org/apache/lucene/index/MappingMultiDocsAndPositionsEnum.java
+++ b/lucene/core/src/java/org/apache/lucene/index/MappingMultiDocsAndPositionsEnum.java
@@ -17,11 +17,11 @@
  * limitations under the License.
  */
 
-import org.apache.lucene.util.BytesRef;
-import org.apache.lucene.index.MultiDocsAndPositionsEnum.EnumWithSlice;
-
 import java.io.IOException;
 
+import org.apache.lucene.index.MultiDocsAndPositionsEnum.EnumWithSlice;
+import org.apache.lucene.util.BytesRef;
+
 /**
  * Exposes flex API, merged from flex API of sub-segments,
  * remapping docIDs (this is used for segment merging).
@@ -29,12 +29,12 @@
  * @lucene.experimental
  */
 
-final class MappingMultiDocsAndPositionsEnum extends DocsAndPositionsEnum {
+final class MappingMultiDocsAndPositionsEnum extends DocsEnum {
   private MultiDocsAndPositionsEnum.EnumWithSlice[] subs;
   int numSubs;
   int upto;
   MergeState.DocMap currentMap;
-  DocsAndPositionsEnum current;
+  DocsEnum current;
   int currentBase;
   int doc = -1;
   private MergeState mergeState;
@@ -122,6 +122,16 @@
   }
 
   @Override
+  public int startPosition() throws IOException {
+    return current.startPosition();
+  }
+
+  @Override
+  public int endPosition() throws IOException {
+    return current.endPosition();
+  }
+
+  @Override
   public int startOffset() throws IOException {
     return current.startOffset();
   }
diff --git a/lucene/core/src/java/org/apache/lucene/index/MappingMultiDocsEnum.java b/lucene/core/src/java/org/apache/lucene/index/MappingMultiDocsEnum.java
index 148ea5c..29dc38c 100644
--- a/lucene/core/src/java/org/apache/lucene/index/MappingMultiDocsEnum.java
+++ b/lucene/core/src/java/org/apache/lucene/index/MappingMultiDocsEnum.java
@@ -17,10 +17,11 @@
  * limitations under the License.
  */
 
-import org.apache.lucene.index.MultiDocsEnum.EnumWithSlice;
-
 import java.io.IOException;
 
+import org.apache.lucene.index.MultiDocsEnum.EnumWithSlice;
+import org.apache.lucene.util.BytesRef;
+
 /**
  * Exposes flex API, merged from flex API of sub-segments,
  * remapping docIDs (this is used for segment merging).
@@ -70,6 +71,36 @@
   }
 
   @Override
+  public int nextPosition() throws IOException {
+    return NO_MORE_POSITIONS;
+  }
+
+  @Override
+  public int startPosition() throws IOException {
+    return NO_MORE_POSITIONS;
+  }
+
+  @Override
+  public int endPosition() throws IOException {
+    return NO_MORE_POSITIONS;
+  }
+
+  @Override
+  public int startOffset() throws IOException {
+    return -1;
+  }
+
+  @Override
+  public int endOffset() throws IOException {
+    return -1;
+  }
+
+  @Override
+  public BytesRef getPayload() throws IOException {
+    return null;
+  }
+
+  @Override
   public int docID() {
     return doc;
   }
diff --git a/lucene/core/src/java/org/apache/lucene/index/MultiDocsAndPositionsEnum.java b/lucene/core/src/java/org/apache/lucene/index/MultiDocsAndPositionsEnum.java
index 33e2127..5c9a5ab 100644
--- a/lucene/core/src/java/org/apache/lucene/index/MultiDocsAndPositionsEnum.java
+++ b/lucene/core/src/java/org/apache/lucene/index/MultiDocsAndPositionsEnum.java
@@ -17,31 +17,31 @@
  * limitations under the License.
  */
 
-import org.apache.lucene.util.BytesRef;
-
 import java.io.IOException;
 import java.util.Arrays;
 
+import org.apache.lucene.util.BytesRef;
+
 /**
  * Exposes flex API, merged from flex API of sub-segments.
  *
  * @lucene.experimental
  */
 
-public final class MultiDocsAndPositionsEnum extends DocsAndPositionsEnum {
+public final class MultiDocsAndPositionsEnum extends DocsEnum {
   private final MultiTermsEnum parent;
-  final DocsAndPositionsEnum[] subDocsAndPositionsEnum;
+  final DocsEnum[] subDocsAndPositionsEnum;
   private final EnumWithSlice[] subs;
   int numSubs;
   int upto;
-  DocsAndPositionsEnum current;
+  DocsEnum current;
   int currentBase;
   int doc = -1;
 
   /** Sole constructor. */
   public MultiDocsAndPositionsEnum(MultiTermsEnum parent, int subReaderCount) {
     this.parent = parent;
-    subDocsAndPositionsEnum = new DocsAndPositionsEnum[subReaderCount];
+    subDocsAndPositionsEnum = new DocsEnum[subReaderCount];
     this.subs = new EnumWithSlice[subReaderCount];
     for (int i = 0; i < subs.length; i++) {
       subs[i] = new EnumWithSlice();
@@ -144,6 +144,16 @@
   }
 
   @Override
+  public int startPosition() throws IOException {
+    return current.startPosition();
+  }
+
+  @Override
+  public int endPosition() throws IOException {
+    return current.endPosition();
+  }
+
+  @Override
   public int startOffset() throws IOException {
     return current.startOffset();
   }
@@ -159,14 +169,14 @@
   }
 
   // TODO: implement bulk read more efficiently than super
-  /** Holds a {@link DocsAndPositionsEnum} along with the
+  /** Holds a {@link DocsEnum} along with the
    *  corresponding {@link ReaderSlice}. */
   public final static class EnumWithSlice {
     EnumWithSlice() {
     }
 
-    /** {@link DocsAndPositionsEnum} for this sub-reader. */
-    public DocsAndPositionsEnum docsAndPositionsEnum;
+    /** {@link DocsEnum} for this sub-reader. */
+    public DocsEnum docsAndPositionsEnum;
 
     /** {@link ReaderSlice} describing how this sub-reader
      *  fits into the composite reader. */
diff --git a/lucene/core/src/java/org/apache/lucene/index/MultiDocsEnum.java b/lucene/core/src/java/org/apache/lucene/index/MultiDocsEnum.java
index 082d266..d76e24e 100644
--- a/lucene/core/src/java/org/apache/lucene/index/MultiDocsEnum.java
+++ b/lucene/core/src/java/org/apache/lucene/index/MultiDocsEnum.java
@@ -21,6 +21,8 @@
 import java.io.IOException;
 import java.util.Arrays;
 
+import org.apache.lucene.util.BytesRef;
+
 /**
  * Exposes {@link DocsEnum}, merged from {@link DocsEnum}
  * API of sub-segments.
@@ -89,6 +91,36 @@
   public int docID() {
     return doc;
   }
+  
+  @Override
+  public int nextPosition() throws IOException {
+    return current.nextPosition();
+  }
+
+  @Override
+  public int startPosition() throws IOException {
+    return current.startPosition();
+  }
+
+  @Override
+  public int endPosition() throws IOException {
+    return current.endPosition();
+  }
+
+  @Override
+  public int startOffset() throws IOException {
+    return current.startOffset();
+  }
+
+  @Override
+  public int endOffset() throws IOException {
+    return current.endOffset();
+  }
+
+  @Override
+  public BytesRef getPayload() throws IOException {
+    return current.getPayload();
+  }
 
   @Override
   public int advance(int target) throws IOException {
diff --git a/lucene/core/src/java/org/apache/lucene/index/MultiFields.java b/lucene/core/src/java/org/apache/lucene/index/MultiFields.java
index 8a6dd0c..c7e2488 100644
--- a/lucene/core/src/java/org/apache/lucene/index/MultiFields.java
+++ b/lucene/core/src/java/org/apache/lucene/index/MultiFields.java
@@ -146,22 +146,22 @@
     return null;
   }
 
-  /** Returns {@link DocsAndPositionsEnum} for the specified
+  /** Returns {@link DocsEnum} for the specified
    *  field and term.  This will return null if the field or
    *  term does not exist or positions were not indexed. 
    *  @see #getTermPositionsEnum(IndexReader, Bits, String, BytesRef, int) */
-  public static DocsAndPositionsEnum getTermPositionsEnum(IndexReader r, Bits liveDocs, String field, BytesRef term) throws IOException {
-    return getTermPositionsEnum(r, liveDocs, field, term, DocsAndPositionsEnum.FLAG_OFFSETS | DocsAndPositionsEnum.FLAG_PAYLOADS);
+  public static DocsEnum getTermPositionsEnum(IndexReader r, Bits liveDocs, String field, BytesRef term) throws IOException {
+    return getTermPositionsEnum(r, liveDocs, field, term, DocsEnum.FLAG_OFFSETS | DocsEnum.FLAG_PAYLOADS);
   }
 
-  /** Returns {@link DocsAndPositionsEnum} for the specified
+  /** Returns {@link DocsEnum} for the specified
    *  field and term, with control over whether offsets and payloads are
    *  required.  Some codecs may be able to optimize
    *  their implementation when offsets and/or payloads are not
    *  required. This will return null if the field or term does not
    *  exist or positions were not indexed. See {@link
-   *  TermsEnum#docsAndPositions(Bits,DocsAndPositionsEnum,int)}. */
-  public static DocsAndPositionsEnum getTermPositionsEnum(IndexReader r, Bits liveDocs, String field, BytesRef term, int flags) throws IOException {
+   *  TermsEnum#docs(Bits,DocsEnum,int)}. */
+  public static DocsEnum getTermPositionsEnum(IndexReader r, Bits liveDocs, String field, BytesRef term, int flags) throws IOException {
     assert field != null;
     assert term != null;
     final Terms terms = getTerms(r, field);
diff --git a/lucene/core/src/java/org/apache/lucene/index/MultiTermsEnum.java b/lucene/core/src/java/org/apache/lucene/index/MultiTermsEnum.java
index 6ae2c7c..5b71d3d 100644
--- a/lucene/core/src/java/org/apache/lucene/index/MultiTermsEnum.java
+++ b/lucene/core/src/java/org/apache/lucene/index/MultiTermsEnum.java
@@ -401,7 +401,7 @@
   }
 
   @Override
-  public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
+  public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
     MultiDocsAndPositionsEnum docsAndPositionsEnum;
     // Can only reuse if incoming enum is also a MultiDocsAndPositionsEnum
     if (reuse != null && reuse instanceof MultiDocsAndPositionsEnum) {
@@ -452,7 +452,7 @@
       }
 
       assert entry.index < docsAndPositionsEnum.subDocsAndPositionsEnum.length: entry.index + " vs " + docsAndPositionsEnum.subDocsAndPositionsEnum.length + "; " + subs.length;
-      final DocsAndPositionsEnum subPostings = entry.terms.docsAndPositions(b, docsAndPositionsEnum.subDocsAndPositionsEnum[entry.index], flags);
+      final DocsEnum subPostings = entry.terms.docsAndPositions(b, docsAndPositionsEnum.subDocsAndPositionsEnum[entry.index], flags);
 
       if (subPostings != null) {
         docsAndPositionsEnum.subDocsAndPositionsEnum[entry.index] = subPostings;
diff --git a/lucene/core/src/java/org/apache/lucene/index/SortedDocValuesTermsEnum.java b/lucene/core/src/java/org/apache/lucene/index/SortedDocValuesTermsEnum.java
index 16427cc..f12e2b8 100644
--- a/lucene/core/src/java/org/apache/lucene/index/SortedDocValuesTermsEnum.java
+++ b/lucene/core/src/java/org/apache/lucene/index/SortedDocValuesTermsEnum.java
@@ -17,12 +17,12 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.BytesRefBuilder;
 
+import java.io.IOException;
+
 /** Implements a {@link TermsEnum} wrapping a provided
  * {@link SortedDocValues}. */
 
@@ -114,7 +114,7 @@
   }
 
   @Override
-  public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
+  public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
     throw new UnsupportedOperationException();
   }
 
diff --git a/lucene/core/src/java/org/apache/lucene/index/SortedSetDocValuesTermsEnum.java b/lucene/core/src/java/org/apache/lucene/index/SortedSetDocValuesTermsEnum.java
index 64dba95..68658ca 100644
--- a/lucene/core/src/java/org/apache/lucene/index/SortedSetDocValuesTermsEnum.java
+++ b/lucene/core/src/java/org/apache/lucene/index/SortedSetDocValuesTermsEnum.java
@@ -17,12 +17,12 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.BytesRefBuilder;
 
+import java.io.IOException;
+
 /** Implements a {@link TermsEnum} wrapping a provided
  * {@link SortedSetDocValues}. */
 
@@ -114,7 +114,7 @@
   }
 
   @Override
-  public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
+  public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
     throw new UnsupportedOperationException();
   }
 
diff --git a/lucene/core/src/java/org/apache/lucene/index/TermContext.java b/lucene/core/src/java/org/apache/lucene/index/TermContext.java
index ada4fc1..e44f0e6 100644
--- a/lucene/core/src/java/org/apache/lucene/index/TermContext.java
+++ b/lucene/core/src/java/org/apache/lucene/index/TermContext.java
@@ -17,11 +17,11 @@
  * limitations under the License.
  */
 
+import org.apache.lucene.util.BytesRef;
+
 import java.io.IOException;
 import java.util.Arrays;
 
-import org.apache.lucene.util.BytesRef;
-
 /**
  * Maintains a {@link IndexReader} {@link TermState} view over
  * {@link IndexReader} instances containing a single term. The
diff --git a/lucene/core/src/java/org/apache/lucene/index/TermsEnum.java b/lucene/core/src/java/org/apache/lucene/index/TermsEnum.java
index 895018b..a9b4436 100644
--- a/lucene/core/src/java/org/apache/lucene/index/TermsEnum.java
+++ b/lucene/core/src/java/org/apache/lucene/index/TermsEnum.java
@@ -17,18 +17,18 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-
 import org.apache.lucene.util.AttributeSource;
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.BytesRefIterator;
 
+import java.io.IOException;
+
 /** Iterator to seek ({@link #seekCeil(BytesRef)}, {@link
  * #seekExact(BytesRef)}) or step through ({@link
  * #next} terms to obtain frequency information ({@link
  * #docFreq}), {@link DocsEnum} or {@link
- * DocsAndPositionsEnum} for the current term ({@link
+ * DocsEnum} for the current term ({@link
  * #docs}.
  * 
  * <p>Term enumerations are always ordered by
@@ -162,20 +162,20 @@
    * @see #docs(Bits, DocsEnum, int) */
   public abstract DocsEnum docs(Bits liveDocs, DocsEnum reuse, int flags) throws IOException;
 
-  /** Get {@link DocsAndPositionsEnum} for the current term.
+  /** Get {@link DocsEnum} for the current term.
    *  Do not call this when the enum is unpositioned.  This
    *  method will return null if positions were not
    *  indexed.
    *  
    *  @param liveDocs unset bits are documents that should not
    *  be returned
-   *  @param reuse pass a prior DocsAndPositionsEnum for possible reuse
-   *  @see #docsAndPositions(Bits, DocsAndPositionsEnum, int) */
-  public final DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse) throws IOException {
-    return docsAndPositions(liveDocs, reuse, DocsAndPositionsEnum.FLAG_OFFSETS | DocsAndPositionsEnum.FLAG_PAYLOADS);
+   *  @param reuse pass a prior DocsEnum for possible reuse
+   **/
+  public final DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse) throws IOException {
+    return docsAndPositions(liveDocs, reuse, DocsEnum.FLAG_OFFSETS | DocsEnum.FLAG_PAYLOADS);
   }
 
-  /** Get {@link DocsAndPositionsEnum} for the current term,
+  /** Get {@link DocsEnum} for the current term,
    *  with control over whether offsets and payloads are
    *  required.  Some codecs may be able to optimize their
    *  implementation when offsets and/or payloads are not required.
@@ -184,11 +184,11 @@
 
    *  @param liveDocs unset bits are documents that should not
    *  be returned
-   *  @param reuse pass a prior DocsAndPositionsEnum for possible reuse
+   *  @param reuse pass a prior DocsEnum for possible reuse
    *  @param flags specifies which optional per-position values you
-   *         require; see {@link DocsAndPositionsEnum#FLAG_OFFSETS} and 
-   *         {@link DocsAndPositionsEnum#FLAG_PAYLOADS}. */
-  public abstract DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) throws IOException;
+   *         require; see {@link DocsEnum#FLAG_OFFSETS} and 
+   *         {@link DocsEnum#FLAG_PAYLOADS}. */
+  public abstract DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) throws IOException;
 
   /**
    * Expert: Returns the TermsEnums internal state to position the TermsEnum
@@ -250,11 +250,6 @@
     }
       
     @Override
-    public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) {
-      throw new IllegalStateException("this method should never be called");
-    }
-      
-    @Override
     public BytesRef next() {
       return null;
     }
@@ -273,5 +268,11 @@
     public void seekExact(BytesRef term, TermState state) {
       throw new IllegalStateException("this method should never be called");
     }
+
+    @Override
+    public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags)
+        throws IOException {
+      throw new IllegalStateException("this method should never be called");
+    }
   };
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java b/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java
index 466dc90..c95e261 100644
--- a/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java
@@ -17,6 +17,15 @@
  * limitations under the License.
  */
 
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.BooleanClause.Occur;
+import org.apache.lucene.search.similarities.Similarity;
+import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.ToStringUtils;
+
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -24,14 +33,6 @@
 import java.util.List;
 import java.util.Set;
 
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.search.BooleanClause.Occur;
-import org.apache.lucene.search.similarities.Similarity;
-import org.apache.lucene.util.Bits;
-import org.apache.lucene.util.ToStringUtils;
-
 /** A Query that matches documents matching boolean combinations of other
   * queries, e.g. {@link TermQuery}s, {@link PhraseQuery}s or other
   * BooleanQuerys.
@@ -242,7 +243,7 @@
       for (Iterator<Weight> wIter = weights.iterator(); wIter.hasNext();) {
         Weight w = wIter.next();
         BooleanClause c = cIter.next();
-        if (w.scorer(context, context.reader().getLiveDocs()) == null) {
+        if (w.scorer(context, DocsEnum.FLAG_FREQS, context.reader().getLiveDocs()) == null) {
           if (c.isRequired()) {
             fail = true;
             Explanation r = new Explanation(0.0f, "no match on required clause (" + c.getQuery().toString() + ")");
@@ -305,13 +306,13 @@
     }
 
     @Override
-    public BulkScorer bulkScorer(LeafReaderContext context, boolean scoreDocsInOrder,
+    public BulkScorer bulkScorer(LeafReaderContext context, boolean scoreDocsInOrder, int flags,
                                  Bits acceptDocs) throws IOException {
 
       if (scoreDocsInOrder || minNrShouldMatch > 1) {
         // TODO: (LUCENE-4872) in some cases BooleanScorer may be faster for minNrShouldMatch
         // but the same is even true of pure conjunctions...
-        return super.bulkScorer(context, scoreDocsInOrder, acceptDocs);
+        return super.bulkScorer(context, scoreDocsInOrder, flags, acceptDocs);
       }
 
       List<BulkScorer> prohibited = new ArrayList<BulkScorer>();
@@ -319,7 +320,7 @@
       Iterator<BooleanClause> cIter = clauses.iterator();
       for (Weight w  : weights) {
         BooleanClause c =  cIter.next();
-        BulkScorer subScorer = w.bulkScorer(context, false, acceptDocs);
+        BulkScorer subScorer = w.bulkScorer(context, false, flags, acceptDocs);
         if (subScorer == null) {
           if (c.isRequired()) {
             return null;
@@ -328,7 +329,7 @@
           // TODO: there are some cases where BooleanScorer
           // would handle conjunctions faster than
           // BooleanScorer2...
-          return super.bulkScorer(context, scoreDocsInOrder, acceptDocs);
+          return super.bulkScorer(context, scoreDocsInOrder, flags, acceptDocs);
         } else if (c.isProhibited()) {
           prohibited.add(subScorer);
         } else {
@@ -340,7 +341,7 @@
     }
 
     @Override
-    public Scorer scorer(LeafReaderContext context, Bits acceptDocs)
+    public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs)
         throws IOException {
       // initially the user provided value,
       // but if minNrShouldMatch == optional.size(),
@@ -353,7 +354,7 @@
       Iterator<BooleanClause> cIter = clauses.iterator();
       for (Weight w  : weights) {
         BooleanClause c =  cIter.next();
-        Scorer subScorer = w.scorer(context, acceptDocs);
+        Scorer subScorer = w.scorer(context, flags, acceptDocs);
         if (subScorer == null) {
           if (c.isRequired()) {
             return null;
@@ -454,8 +455,17 @@
       // scorer() will return an out-of-order scorer if requested.
       return true;
     }
+
+    @Override
+    public String toString() {
+      StringBuffer sb = new StringBuffer("BooleanWeight[");
+      for (Weight weight : weights) {
+        sb.append(weight.toString()).append(",");
+      }
+      return sb.append("]").toString();
+    }
     
-    private Scorer req(List<Scorer> required, boolean disableCoord) {
+    private Scorer req(List<Scorer> required, boolean disableCoord) throws IOException {
       if (required.size() == 1) {
         Scorer req = required.get(0);
         if (!disableCoord && maxCoord > 1) {
diff --git a/lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java b/lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java
index 5c85bdb..daf2bcd 100644
--- a/lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java
@@ -99,7 +99,7 @@
     }
 
   }
-  
+
   static final class Bucket {
     int doc = -1;            // tells if bucket is valid
     double score;             // incremental score
diff --git a/lucene/core/src/java/org/apache/lucene/search/BooleanTopLevelScorers.java b/lucene/core/src/java/org/apache/lucene/search/BooleanTopLevelScorers.java
index 2c49ec7..721ade2 100644
--- a/lucene/core/src/java/org/apache/lucene/search/BooleanTopLevelScorers.java
+++ b/lucene/core/src/java/org/apache/lucene/search/BooleanTopLevelScorers.java
@@ -21,8 +21,6 @@
 import java.util.Collection;
 import java.util.Collections;
 
-import org.apache.lucene.search.Scorer.ChildScorer;
-
 /** Internal document-at-a-time scorers used to deal with stupid coord() computation */
 class BooleanTopLevelScorers {
   
@@ -61,7 +59,7 @@
     private final Scorer req;
     private final Scorer opt;
     
-    CoordinatingConjunctionScorer(Weight weight, float coords[], Scorer req, int reqCount, Scorer opt) {
+    CoordinatingConjunctionScorer(Weight weight, float coords[], Scorer req, int reqCount, Scorer opt) throws IOException {
       super(weight, new Scorer[] { req, opt });
       this.coords = coords;
       this.req = req;
diff --git a/lucene/core/src/java/org/apache/lucene/search/CachingCollector.java b/lucene/core/src/java/org/apache/lucene/search/CachingCollector.java
index 0fe0ea9..c0ca5f4 100644
--- a/lucene/core/src/java/org/apache/lucene/search/CachingCollector.java
+++ b/lucene/core/src/java/org/apache/lucene/search/CachingCollector.java
@@ -17,15 +17,16 @@
  * limitations under the License.
  */
 
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.util.ArrayUtil;
-import org.apache.lucene.util.RamUsageEstimator;
-
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.util.ArrayUtil;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.RamUsageEstimator;
+
 /**
  * Caches all docs, and optionally also scores, coming from
  * a search, and is then able to replay them to another
@@ -74,10 +75,41 @@
     public final int freq() { throw new UnsupportedOperationException(); }
 
     @Override
+    public int nextPosition() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
     public final int nextDoc() { throw new UnsupportedOperationException(); }
 
     @Override
     public long cost() { return 1; }
+
   }
 
   private static class NoScoreCachingCollector extends CachingCollector {
diff --git a/lucene/core/src/java/org/apache/lucene/search/Collector.java b/lucene/core/src/java/org/apache/lucene/search/Collector.java
index 0ac853f..448c08c 100644
--- a/lucene/core/src/java/org/apache/lucene/search/Collector.java
+++ b/lucene/core/src/java/org/apache/lucene/search/Collector.java
@@ -72,5 +72,5 @@
    *          next atomic reader context
    */
   LeafCollector getLeafCollector(LeafReaderContext context) throws IOException;
-
+  
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java b/lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java
index 3e81187..7a48b00 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java
@@ -23,18 +23,22 @@
 import java.util.Comparator;
 
 import org.apache.lucene.util.ArrayUtil;
+import org.apache.lucene.util.BytesRef;
 
 /** Scorer for conjunctions, sets of queries, all of which are required. */
 class ConjunctionScorer extends Scorer {
+
   protected int lastDoc = -1;
   protected final DocsAndFreqs[] docsAndFreqs;
   private final DocsAndFreqs lead;
   private final float coord;
+  private final PositionQueue posQueue;
+  private final Scorer[] scorers; // to preserve order for positional queries
 
   ConjunctionScorer(Weight weight, Scorer[] scorers) {
     this(weight, scorers, 1f);
   }
-  
+
   ConjunctionScorer(Weight weight, Scorer[] scorers, float coord) {
     super(weight);
     this.coord = coord;
@@ -52,6 +56,8 @@
     });
 
     lead = docsAndFreqs[0]; // least frequent DocsEnum leads the intersection
+    posQueue = new PositionQueue(scorers);
+    this.scorers = scorers;
   }
 
   private int doNext(int doc) throws IOException {
@@ -76,6 +82,7 @@
           }
         }
         // success - all DocsEnums are on the same doc
+        posQueue.advanceTo(doc);
         return doc;
       }
       // advance head for next iteration
@@ -109,22 +116,52 @@
     }
     return sum * coord;
   }
-  
+
   @Override
   public int freq() {
     return docsAndFreqs.length;
   }
 
   @Override
+  public int nextPosition() throws IOException {
+    return posQueue.nextPosition();
+  }
+
+  @Override
+  public int startPosition() throws IOException {
+    return posQueue.startPosition();
+  }
+
+  @Override
+  public int endPosition() throws IOException {
+    return posQueue.endPosition();
+  }
+
+  @Override
+  public int startOffset() throws IOException {
+    return posQueue.startOffset();
+  }
+
+  @Override
+  public int endOffset() throws IOException {
+    return posQueue.endOffset();
+  }
+
+  @Override
+  public BytesRef getPayload() throws IOException {
+    return posQueue.getPayload();
+  }
+
+  @Override
   public long cost() {
     return lead.scorer.cost();
   }
 
   @Override
   public Collection<ChildScorer> getChildren() {
-    ArrayList<ChildScorer> children = new ArrayList<>(docsAndFreqs.length);
-    for (DocsAndFreqs docs : docsAndFreqs) {
-      children.add(new ChildScorer(docs.scorer, "MUST"));
+    ArrayList<ChildScorer> children = new ArrayList<>(scorers.length);
+    for (Scorer scorer : scorers) {
+      children.add(new ChildScorer(scorer, "MUST"));
     }
     return children;
   }
@@ -133,7 +170,7 @@
     final long cost;
     final Scorer scorer;
     int doc = -1;
-   
+
     DocsAndFreqs(Scorer scorer) {
       this.scorer = scorer;
       this.cost = scorer.cost();
diff --git a/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java b/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java
index d9e7f32..3079349 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java
@@ -17,17 +17,19 @@
  * limitations under the License.
  */
 
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.util.Bits;
-import org.apache.lucene.util.ToStringUtils;
-
 import java.io.IOException;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Set;
 
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.ToStringUtils;
+
 /**
  * A query that wraps another query or a filter and simply returns a constant score equal to the
  * query boost for every document that matches the filter or query.
@@ -134,14 +136,14 @@
     }
 
     @Override
-    public BulkScorer bulkScorer(LeafReaderContext context, boolean scoreDocsInOrder, Bits acceptDocs) throws IOException {
+    public BulkScorer bulkScorer(LeafReaderContext context, boolean scoreDocsInOrder, int flags, Bits acceptDocs) throws IOException {
       final DocIdSetIterator disi;
       if (filter != null) {
         assert query == null;
-        return super.bulkScorer(context, scoreDocsInOrder, acceptDocs);
+        return super.bulkScorer(context, scoreDocsInOrder, flags, acceptDocs);
       } else {
         assert query != null && innerWeight != null;
-        BulkScorer bulkScorer = innerWeight.bulkScorer(context, scoreDocsInOrder, acceptDocs);
+        BulkScorer bulkScorer = innerWeight.bulkScorer(context, scoreDocsInOrder, flags, acceptDocs);
         if (bulkScorer == null) {
           return null;
         }
@@ -150,24 +152,27 @@
     }
 
     @Override
-    public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
-      final DocIdSetIterator disi;
+    public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
       if (filter != null) {
         assert query == null;
         final DocIdSet dis = filter.getDocIdSet(context, acceptDocs);
         if (dis == null) {
           return null;
         }
-        disi = dis.iterator();
+        final DocIdSetIterator disi = dis.iterator();
+        if (disi == null)
+          return null;
+        return new ConstantDocIdSetIteratorScorer(disi, this, queryWeight);
       } else {
         assert query != null && innerWeight != null;
-        disi = innerWeight.scorer(context, acceptDocs);
+        Scorer scorer = innerWeight.scorer(context, flags, acceptDocs);
+        if (scorer == null) {
+          return null;
+        }
+        return new ConstantScoreScorer(scorer, queryWeight);
       }
 
-      if (disi == null) {
-        return null;
-      }
-      return new ConstantScorer(disi, this, queryWeight);
+
     }
 
     @Override
@@ -177,7 +182,7 @@
 
     @Override
     public Explanation explain(LeafReaderContext context, int doc) throws IOException {
-      final Scorer cs = scorer(context, context.reader().getLiveDocs());
+      final Scorer cs = scorer(context, DocsEnum.FLAG_FREQS, context.reader().getLiveDocs());
       final boolean exists = (cs != null && cs.advance(doc) == doc);
 
       final ComplexExplanation result = new ComplexExplanation();
@@ -221,17 +226,46 @@
         @Override
         public void setScorer(Scorer scorer) throws IOException {
           // we must wrap again here, but using the scorer passed in as parameter:
-          in.setScorer(new ConstantScorer(scorer, weight, theScore));
+          in.setScorer(new ConstantScoreScorer(scorer, theScore));
         }
       };
     }
   }
 
-  protected class ConstantScorer extends Scorer {
+  protected class ConstantScoreScorer extends FilterScorer {
+
+    private final float score;
+
+    public ConstantScoreScorer(Scorer wrapped, float score) {
+      super(wrapped);
+      this.score = score;
+    }
+
+    @Override
+    public int freq() throws IOException {
+      return 1;
+    }
+
+    @Override
+    public float score() throws IOException {
+      return score;
+    }
+
+    @Override
+    public Collection<ChildScorer> getChildren() {
+      if (query != null) {
+        return Collections.singletonList(new ChildScorer(in, "constant"));
+      } else {
+        return Collections.emptyList();
+      }
+    }
+  }
+
+  protected class ConstantDocIdSetIteratorScorer extends Scorer {
     final DocIdSetIterator docIdSetIterator;
     final float theScore;
 
-    public ConstantScorer(DocIdSetIterator docIdSetIterator, Weight w, float theScore) {
+    public ConstantDocIdSetIteratorScorer(DocIdSetIterator docIdSetIterator, Weight w, float theScore) {
       super(w);
       this.theScore = theScore;
       this.docIdSetIterator = docIdSetIterator;
@@ -259,10 +293,40 @@
     }
 
     @Override
+    public int nextPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
+    @Override
     public int advance(int target) throws IOException {
       return docIdSetIterator.advance(target);
     }
-    
+
     @Override
     public long cost() {
       return docIdSetIterator.cost();
diff --git a/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java b/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java
index e27063a..5ab87a3 100644
--- a/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java
@@ -16,6 +16,11 @@
  * limitations under the License.
  */
 
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.util.Bits;
+
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -23,11 +28,6 @@
 import java.util.List;
 import java.util.Set;
 
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.util.Bits;
-
 /**
  * A query that generates the union of documents produced by its subqueries, and that scores each document with the maximum
  * score for that document as produced by any subquery, plus a tie breaking increment for any additional matching subqueries.
@@ -153,11 +153,11 @@
 
     /** Create the scorer used to score our associated DisjunctionMaxQuery */
     @Override
-    public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
+    public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
       List<Scorer> scorers = new ArrayList<>();
       for (Weight w : weights) {
         // we will advance() subscorers
-        Scorer subScorer = w.scorer(context, acceptDocs);
+        Scorer subScorer = w.scorer(context, flags, acceptDocs);
         if (subScorer != null) {
           scorers.add(subScorer);
         }
diff --git a/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxScorer.java b/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxScorer.java
index b5d0a0d..e80242e 100644
--- a/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxScorer.java
@@ -46,6 +46,7 @@
   DisjunctionMaxScorer(Weight weight, float tieBreakerMultiplier, Scorer[] subScorers) {
     super(weight, subScorers);
     this.tieBreakerMultiplier = tieBreakerMultiplier;
+        
   }
   
   @Override
@@ -66,4 +67,5 @@
   protected float getFinal() {
     return scoreMax + (scoreSum - scoreMax) * tieBreakerMultiplier; 
   }
+
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/DisjunctionScorer.java b/lucene/core/src/java/org/apache/lucene/search/DisjunctionScorer.java
index 5b7e2ff..f76aaeb 100644
--- a/lucene/core/src/java/org/apache/lucene/search/DisjunctionScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/DisjunctionScorer.java
@@ -20,23 +20,28 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Locale;
+
+import org.apache.lucene.util.BytesRef;
 
 /**
  * Base class for Scorers that score disjunctions.
  */
 abstract class DisjunctionScorer extends Scorer {
-  private final Scorer subScorers[];
-  private int numScorers;
+  protected final Scorer subScorers[];
 
   /** The document number of the current match. */
   protected int doc = -1;
+  protected int numScorers;
+  protected PositionQueue posQueue;
   /** Number of matching scorers for the current match. */
   protected int freq = -1;
-  
+
   protected DisjunctionScorer(Weight weight, Scorer subScorers[]) {
     super(weight);
     this.subScorers = subScorers;
     this.numScorers = subScorers.length;
+    this.posQueue = new PositionQueue(subScorers);
     if (numScorers <= 1) {
       throw new IllegalArgumentException("There must be at least 2 subScorers");
     }
@@ -115,6 +120,50 @@
   }
 
   @Override
+  public int nextPosition() throws IOException {
+    //System.out.println("Advancing " + this.toString());
+    int pos = posQueue.nextPosition();
+    //System.out.println(this);
+    return pos;
+  }
+
+  @Override
+  public int startPosition() throws IOException {
+    return posQueue.startPosition();
+  }
+
+  @Override
+  public int endPosition() throws IOException {
+    return posQueue.endPosition();
+  }
+
+  @Override
+  public int startOffset() throws IOException {
+    return posQueue.startOffset();
+  }
+
+  @Override
+  public int endOffset() throws IOException {
+    return posQueue.endOffset();
+  }
+
+  @Override
+  public BytesRef getPayload() throws IOException {
+    return posQueue.getPayload();
+  }
+
+  @Override
+  public String toString() {
+    try {
+      return String.format(Locale.ROOT, "DisjScorer[%s] %d(%d)->%d(%d)", weight.toString(),
+                            posQueue.startPosition(),
+                            posQueue.startOffset(), posQueue.endPosition(), posQueue.endOffset());
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  @Override
   public final long cost() {
     long sum = 0;
     for (int i = 0; i < numScorers; i++) {
@@ -143,6 +192,7 @@
       int docID = subScorers[0].docID();
       if (docID != doc) {
         freq = -1;
+        posQueue.advanceTo(docID);
         return doc = docID;
       }
     }
@@ -163,11 +213,12 @@
       int docID = subScorers[0].docID();
       if (docID >= target) {
         freq = -1;
+        posQueue.advanceTo(docID);
         return doc = docID;
       }
     }
   }
-  
+
   // if we haven't already computed freq + score, do so
   private void visitScorers() throws IOException {
     reset();
@@ -209,4 +260,5 @@
   
   /** Return final score */
   protected abstract float getFinal();
+
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/DisjunctionSumScorer.java b/lucene/core/src/java/org/apache/lucene/search/DisjunctionSumScorer.java
index f291695..f775ad6 100644
--- a/lucene/core/src/java/org/apache/lucene/search/DisjunctionSumScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/DisjunctionSumScorer.java
@@ -19,13 +19,14 @@
 
 import java.io.IOException;
 
+
 /** A Scorer for OR like queries, counterpart of <code>ConjunctionScorer</code>.
  * This Scorer implements {@link Scorer#advance(int)} and uses advance() on the given Scorers. 
  */
 final class DisjunctionSumScorer extends DisjunctionScorer { 
   private double score;
   private final float[] coord;
-  
+
   /** Construct a <code>DisjunctionScorer</code>.
    * @param weight The weight to be used.
    * @param subScorers Array of at least two subscorers.
@@ -50,4 +51,5 @@
   protected float getFinal() {
     return (float)score * coord[freq]; 
   }
+
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/ExactPhraseScorer.java b/lucene/core/src/java/org/apache/lucene/search/ExactPhraseScorer.java
index e73b241..6ca2a24 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ExactPhraseScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ExactPhraseScorer.java
@@ -20,47 +20,51 @@
 import java.io.IOException;
 import java.util.Arrays;
 
-import org.apache.lucene.index.*;
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.search.PhraseQuery.TermDocsEnumFactory;
 import org.apache.lucene.search.similarities.Similarity;
+import org.apache.lucene.util.BytesRef;
 
 final class ExactPhraseScorer extends Scorer {
   private final int endMinus1;
-
+  
   private final static int CHUNK = 4096;
-
+  
   private int gen;
   private final int[] counts = new int[CHUNK];
   private final int[] gens = new int[CHUNK];
+  private final int[] offsets = new int[CHUNK];
 
   private final long cost;
 
   private final static class ChunkState {
-    final DocsAndPositionsEnum posEnum;
+    final TermDocsEnumFactory factory;
+    final DocsEnum posEnum;
     final int offset;
     int posUpto;
     int posLimit;
     int pos;
     int lastPos;
 
-    public ChunkState(DocsAndPositionsEnum posEnum, int offset) {
+    public ChunkState(TermDocsEnumFactory factory, DocsEnum posEnum, int offset) {
+      this.factory = factory;
       this.posEnum = posEnum;
       this.offset = offset;
     }
   }
-
+  
   private final ChunkState[] chunkStates;
-  private final DocsAndPositionsEnum lead;
+  private final DocsEnum lead;
 
   private int docID = -1;
-  private int freq;
 
   private final Similarity.SimScorer docScorer;
-  
+
   ExactPhraseScorer(Weight weight, PhraseQuery.PostingsAndFreq[] postings,
                     Similarity.SimScorer docScorer) throws IOException {
     super(weight);
     this.docScorer = docScorer;
-
+    
     chunkStates = new ChunkState[postings.length];
 
     endMinus1 = postings.length-1;
@@ -70,7 +74,7 @@
     cost = lead.cost();
 
     for(int i=0;i<postings.length;i++) {
-      chunkStates[i] = new ChunkState(postings[i].postings, -postings[i].position);
+      chunkStates[i] = new ChunkState(postings[i].factory, postings[i].postings, -postings[i].position);
     }
   }
   
@@ -79,7 +83,7 @@
       // TODO: don't dup this logic from conjunctionscorer :)
       advanceHead: for(;;) {
         for (int i = 1; i < chunkStates.length; i++) {
-          final DocsAndPositionsEnum de = chunkStates[i].posEnum;
+          final DocsEnum de = chunkStates[i].posEnum;
           if (de.docID() < doc) {
             int d = de.advance(doc);
 
@@ -93,7 +97,7 @@
         // all DocsEnums are on the same doc
         if (doc == NO_MORE_DOCS) {
           return doc;
-        } else if (phraseFreq() > 0) {
+        } else if (firstPosition() != NO_MORE_POSITIONS) {
           return doc;            // success: matches phrase
         } else {
           doc = lead.nextDoc();  // doesn't match phrase
@@ -103,7 +107,7 @@
       doc = lead.advance(doc);
     }
   }
-
+  
   @Override
   public int nextDoc() throws IOException {
     return docID = doNext(lead.nextDoc());
@@ -113,51 +117,135 @@
   public int advance(int target) throws IOException {
     return docID = doNext(lead.advance(target));
   }
-
+  
   @Override
   public String toString() {
     return "ExactPhraseScorer(" + weight + ")";
   }
-
-  @Override
-  public int freq() {
-    return freq;
-  }
-
+  
   @Override
   public int docID() {
     return docID;
   }
-
+  
   @Override
-  public float score() {
-    return docScorer.score(docID, freq);
+  public float score() throws IOException {
+    return docScorer.score(docID, phraseFreq());
   }
 
-  private int phraseFreq() throws IOException {
+  private int freq = -1;
 
+  protected int phraseFreq() throws IOException {
+    if (freq != -1)
+      return freq;
     freq = 0;
+    while (nextPosition() != NO_MORE_POSITIONS)
+      freq++;
+    return freq;
+  }
 
-    // init chunks
-    for(int i=0;i<chunkStates.length;i++) {
-      final ChunkState cs = chunkStates[i];
+  private int chunkStart = 0;
+  private int chunkEnd = CHUNK;
+
+  private int posRemaining;
+  private int positionsInChunk;
+  private boolean cached = false;
+
+  private void resetPositions() throws IOException {
+    chunkStart = 0;
+    chunkEnd = CHUNK;
+    posRemaining = 0;
+    cached = false;
+    freq = -1;
+    for (final ChunkState cs : chunkStates) {
       cs.posLimit = cs.posEnum.freq();
       cs.pos = cs.offset + cs.posEnum.nextPosition();
       cs.posUpto = 1;
       cs.lastPos = -1;
     }
+  }
 
-    int chunkStart = 0;
-    int chunkEnd = CHUNK;
+  private int firstPosition() throws IOException {
+    resetPositions();
+    int pos = nextPosition();
+    cached = true;
+    return pos;
+  }
 
-    // process chunk by chunk
-    boolean end = false;
+  @Override
+  public int startPosition() throws IOException {
+    return posQueue[positionsInChunk - posRemaining - 1];
+  }
 
-    // TODO: we could fold in chunkStart into offset and
+  @Override
+  public int startOffset() throws IOException {
+    return offsetQueue[(positionsInChunk - posRemaining - 1) / 2];
+  }
+
+  @Override
+  public int endOffset() throws IOException {
+    return offsetQueue[(positionsInChunk - posRemaining - 1) / 2 + 1];
+  }
+
+  @Override
+  public BytesRef getPayload() throws IOException {
+    return null;  // TODO payloads over intervals
+  }
+
+  @Override
+  public int endPosition() throws IOException {
+    return startPosition() + chunkStates.length - 1;
+  }
+
+  @Override
+  public int freq() throws IOException {
+    return phraseFreq();
+  }
+
+  @Override
+  public int nextPosition() throws IOException {
+    if (cached) {
+      cached = false;
+      return startPosition();
+    }
+
+    if (posRemaining == 0 && !findNextMatches())
+      return NO_MORE_POSITIONS;
+
+    if (posRemaining == 0)
+      return NO_MORE_POSITIONS;
+
+    posRemaining--;
+    return startPosition();
+  }
+
+  int[] posQueue = new int[8];
+  int[] offsetQueue = new int[16];
+
+  private void addPosition(int pos, int beginOffset, int endOffset) {
+    positionsInChunk++;
+    if (posQueue.length < positionsInChunk) {
+      int[] newQueue = new int[posQueue.length * 2];
+      System.arraycopy(posQueue, 0, newQueue, 0, posQueue.length);
+      posQueue = newQueue;
+      int[] newOffsets = new int[posQueue.length * 2];
+      System.arraycopy(offsetQueue, 0, newOffsets, 0, offsetQueue.length);
+      offsetQueue = newOffsets;
+    }
+    posQueue[positionsInChunk - 1] = pos;
+    offsetQueue[(positionsInChunk - 1) * 2] = beginOffset;
+    offsetQueue[(positionsInChunk - 1) * 2 + 1] = endOffset;
+  }
+
+  private boolean findNextMatches() throws IOException {
+
+    // TODO: we could fold in chunkStart into phraseOffset and
     // save one subtract per pos incr
 
-    while(!end) {
+    boolean exhausted = false;
 
+    while (true) {
+      positionsInChunk = 0;
       gen++;
 
       if (gen == 0) {
@@ -166,20 +254,24 @@
         gen++;
       }
 
+      boolean any = false;
+
       // first term
       {
         final ChunkState cs = chunkStates[0];
-        while(cs.pos < chunkEnd) {
+        while (cs.pos < chunkEnd) {
           if (cs.pos > cs.lastPos) {
             cs.lastPos = cs.pos;
             final int posIndex = cs.pos - chunkStart;
             counts[posIndex] = 1;
+            any = true;
             assert gens[posIndex] != gen;
             gens[posIndex] = gen;
+            offsets[posIndex] = cs.posEnum.startOffset();
           }
 
           if (cs.posUpto == cs.posLimit) {
-            end = true;
+            exhausted = true;
             break;
           }
           cs.posUpto++;
@@ -188,11 +280,10 @@
       }
 
       // middle terms
-      boolean any = true;
-      for(int t=1;t<endMinus1;t++) {
+      for (int t = 1; t < endMinus1; t++) {
         final ChunkState cs = chunkStates[t];
         any = false;
-        while(cs.pos < chunkEnd) {
+        while (cs.pos < chunkEnd) {
           if (cs.pos > cs.lastPos) {
             cs.lastPos = cs.pos;
             final int posIndex = cs.pos - chunkStart;
@@ -204,7 +295,7 @@
           }
 
           if (cs.posUpto == cs.posLimit) {
-            end = true;
+            exhausted = true;
             break;
           }
           cs.posUpto++;
@@ -220,24 +311,28 @@
         // petered out for this chunk
         chunkStart += CHUNK;
         chunkEnd += CHUNK;
+        if (exhausted)
+          return false;
         continue;
       }
 
       // last term
 
       {
+        any = false;
         final ChunkState cs = chunkStates[endMinus1];
-        while(cs.pos < chunkEnd) {
+        while (cs.pos < chunkEnd) {
           if (cs.pos > cs.lastPos) {
             cs.lastPos = cs.pos;
             final int posIndex = cs.pos - chunkStart;
-            if (posIndex >= 0 && gens[posIndex] == gen && counts[posIndex] == endMinus1) {
-              freq++;
+            if (posIndex >= 0 && gens[posIndex] == gen
+                && counts[posIndex] == endMinus1) {
+              addPosition(cs.pos, offsets[posIndex], cs.posEnum.endOffset());
+              any = true;
             }
           }
 
           if (cs.posUpto == cs.posLimit) {
-            end = true;
             break;
           }
           cs.posUpto++;
@@ -247,13 +342,17 @@
 
       chunkStart += CHUNK;
       chunkEnd += CHUNK;
-    }
 
-    return freq;
+      if (any) {
+        posRemaining = positionsInChunk;
+        return true;
+      }
+    }
   }
 
   @Override
   public long cost() {
     return cost;
   }
+
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/FakeScorer.java b/lucene/core/src/java/org/apache/lucene/search/FakeScorer.java
index e2a50c8..4b0fbef 100644
--- a/lucene/core/src/java/org/apache/lucene/search/FakeScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/FakeScorer.java
@@ -17,14 +17,17 @@
  * limitations under the License.
  */
 
+import java.io.IOException;
 import java.util.Collection;
 
+import org.apache.lucene.util.BytesRef;
+
 /** Used by {@link BulkScorer}s that need to pass a {@link
  *  Scorer} to {@link LeafCollector#setScorer}. */
-final class FakeScorer extends Scorer {
-  float score;
-  int doc = -1;
-  int freq = 1;
+public final class FakeScorer extends Scorer {
+  public float score;
+  public int doc = -1;
+  public int freq = 1;
 
   public FakeScorer() {
     super(null);
@@ -46,6 +49,36 @@
   }
 
   @Override
+  public int nextPosition() throws IOException {
+    throw new UnsupportedOperationException("FakeScorer doesn't support nextPosition()");
+  }
+
+  @Override
+  public int startPosition() throws IOException {
+    throw new UnsupportedOperationException("FakeScorer doesn't support startPosition()");
+  }
+
+  @Override
+  public int endPosition() throws IOException {
+    throw new UnsupportedOperationException("FakeScorer doesn't support endPosition()");
+  }
+
+  @Override
+  public int startOffset() throws IOException {
+    throw new UnsupportedOperationException("FakeScorer doesn't support startOffset()");
+  }
+
+  @Override
+  public int endOffset() throws IOException {
+    throw new UnsupportedOperationException("FakeScorer doesn't support endOffset()");
+  }
+
+  @Override
+  public BytesRef getPayload() throws IOException {
+    throw new UnsupportedOperationException("FakeScorer doesn't support getPayload()");
+  }
+
+  @Override
   public int nextDoc() {
     throw new UnsupportedOperationException("FakeScorer doesn't support nextDoc()");
   }
diff --git a/lucene/core/src/java/org/apache/lucene/search/FilterScorer.java b/lucene/core/src/java/org/apache/lucene/search/FilterScorer.java
index 88881bd..8a1ecfc 100644
--- a/lucene/core/src/java/org/apache/lucene/search/FilterScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/FilterScorer.java
@@ -18,9 +18,9 @@
  */
 
 import java.io.IOException;
-import java.util.Collection;
 
 import org.apache.lucene.util.AttributeSource;
+import org.apache.lucene.util.BytesRef;
 
 /** 
  * A {@code FilterScorer} contains another {@code Scorer}, which it
@@ -32,13 +32,18 @@
  * further override some of these methods and may also provide additional
  * methods and fields.
  */
-abstract class FilterScorer extends Scorer {
+public abstract class FilterScorer extends Scorer {
   protected final Scorer in;
   
   public FilterScorer(Scorer in) {
     super(in.weight);
     this.in = in;
   }
+
+  public FilterScorer(Scorer in, Weight weight) {
+    super(weight);
+    this.in = in;
+  }
   
   @Override
   public float score() throws IOException {
@@ -61,6 +66,11 @@
   }
 
   @Override
+  public int nextPosition() throws IOException {
+    return in.nextPosition();
+  }
+
+  @Override
   public int advance(int target) throws IOException {
     return in.advance(target);
   }
@@ -71,6 +81,31 @@
   }
 
   @Override
+  public int startPosition() throws IOException {
+    return in.startPosition();
+  }
+
+  @Override
+  public int endPosition() throws IOException {
+    return in.endPosition();
+  }
+
+  @Override
+  public int startOffset() throws IOException {
+    return in.startOffset();
+  }
+
+  @Override
+  public int endOffset() throws IOException {
+    return in.endOffset();
+  }
+
+  @Override
+  public BytesRef getPayload() throws IOException {
+    return in.getPayload();
+  }
+
+  @Override
   public AttributeSource attributes() {
     return in.attributes();
   }
diff --git a/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java b/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java
index 9d791c7..274ac1a 100644
--- a/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java
@@ -17,17 +17,17 @@
  * limitations under the License.
  */
 
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.util.Bits;
-import org.apache.lucene.util.ToStringUtils;
-
 import java.io.IOException;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Set;
 
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.ToStringUtils;
+
 
 /**
  * A query that applies a filter to the results of another query.
@@ -124,7 +124,7 @@
 
       // return a filtering scorer
       @Override
-      public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
+      public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
         assert filter != null;
 
         DocIdSet filterDocIdSet = filter.getDocIdSet(context, acceptDocs);
@@ -133,12 +133,12 @@
           return null;
         }
 
-        return strategy.filteredScorer(context, weight, filterDocIdSet);
+        return strategy.filteredScorer(context, weight, filterDocIdSet, flags);
       }
 
       // return a filtering top scorer
       @Override
-      public BulkScorer bulkScorer(LeafReaderContext context, boolean scoreDocsInOrder, Bits acceptDocs) throws IOException {
+      public BulkScorer bulkScorer(LeafReaderContext context, boolean scoreDocsInOrder, int flags, Bits acceptDocs) throws IOException {
         assert filter != null;
 
         DocIdSet filterDocIdSet = filter.getDocIdSet(context, acceptDocs);
@@ -147,7 +147,8 @@
           return null;
         }
 
-        return strategy.filteredBulkScorer(context, weight, scoreDocsInOrder, filterDocIdSet);
+        return strategy.filteredBulkScorer(context, weight, scoreDocsInOrder, filterDocIdSet, flags);
+
       }
     };
   }
@@ -158,13 +159,13 @@
    * than document scoring or if the filter has a linear running time to compute
    * the next matching doc like exact geo distances.
    */
-  private static final class QueryFirstScorer extends Scorer {
+  private static final class QueryFirstScorer extends FilterScorer {
     private final Scorer scorer;
     private int scorerDoc = -1;
     private final Bits filterBits;
 
     protected QueryFirstScorer(Weight weight, Bits filterBits, Scorer other) {
-      super(weight);
+      super(other, weight);
       this.scorer = other;
       this.filterBits = filterBits;
     }
@@ -189,29 +190,16 @@
         return scorerDoc = doc;
       }
     }
-
     @Override
     public int docID() {
       return scorerDoc;
     }
-    
-    @Override
-    public float score() throws IOException {
-      return scorer.score();
-    }
-    
-    @Override
-    public int freq() throws IOException { return scorer.freq(); }
-    
+
     @Override
     public Collection<ChildScorer> getChildren() {
       return Collections.singleton(new ChildScorer(scorer, "FILTERED"));
     }
 
-    @Override
-    public long cost() {
-      return scorer.cost();
-    }
   }
 
   private static class QueryFirstBulkScorer extends BulkScorer {
@@ -254,7 +242,7 @@
    * jumping past the target document. When both land on the same document, it's
    * collected.
    */
-  private static final class LeapFrogScorer extends Scorer {
+  private static final class LeapFrogScorer extends FilterScorer {
     private final DocIdSetIterator secondary;
     private final DocIdSetIterator primary;
     private final Scorer scorer;
@@ -262,7 +250,7 @@
     private int secondaryDoc = -1;
 
     protected LeapFrogScorer(Weight weight, DocIdSetIterator primary, DocIdSetIterator secondary, Scorer scorer) {
-      super(weight);
+      super(scorer, weight);
       this.primary = primary;
       this.secondary = secondary;
       this.scorer = scorer;
@@ -302,17 +290,7 @@
     public final int docID() {
       return secondaryDoc;
     }
-    
-    @Override
-    public final float score() throws IOException {
-      return scorer.score();
-    }
-    
-    @Override
-    public final int freq() throws IOException {
-      return scorer.freq();
-    }
-    
+
     @Override
     public final Collection<ChildScorer> getChildren() {
       return Collections.singleton(new ChildScorer(scorer, "FILTERED"));
@@ -460,12 +438,13 @@
      *          the {@link org.apache.lucene.index.LeafReaderContext} for which to return the {@link Scorer}.
      * @param weight the {@link FilteredQuery} {@link Weight} to create the filtered scorer.
      * @param docIdSet the filter {@link DocIdSet} to apply
+     * @param flags the low level Posting Features for this scorer.
      * @return a filtered scorer
      * 
      * @throws IOException if an {@link IOException} occurs
      */
     public abstract Scorer filteredScorer(LeafReaderContext context,
-        Weight weight, DocIdSet docIdSet) throws IOException;
+        Weight weight, DocIdSet docIdSet, int flags) throws IOException;
 
     /**
      * Returns a filtered {@link BulkScorer} based on this
@@ -480,8 +459,8 @@
      * @return a filtered top scorer
      */
     public BulkScorer filteredBulkScorer(LeafReaderContext context,
-        Weight weight, boolean scoreDocsInOrder, DocIdSet docIdSet) throws IOException {
-      Scorer scorer = filteredScorer(context, weight, docIdSet);
+        Weight weight, boolean scoreDocsInOrder, DocIdSet docIdSet, int flags) throws IOException {
+      Scorer scorer = filteredScorer(context, weight, docIdSet, flags);
       if (scorer == null) {
         return null;
       }
@@ -489,6 +468,7 @@
       // ignore scoreDocsInOrder:
       return new Weight.DefaultBulkScorer(scorer);
     }
+
   }
   
   /**
@@ -502,7 +482,7 @@
   public static class RandomAccessFilterStrategy extends FilterStrategy {
 
     @Override
-    public Scorer filteredScorer(LeafReaderContext context, Weight weight, DocIdSet docIdSet) throws IOException {
+    public Scorer filteredScorer(LeafReaderContext context, Weight weight, DocIdSet docIdSet, int flags) throws IOException {
       final DocIdSetIterator filterIter = docIdSet.iterator();
       if (filterIter == null) {
         // this means the filter does not accept any documents.
@@ -514,11 +494,11 @@
       final boolean useRandomAccess = filterAcceptDocs != null && useRandomAccess(filterAcceptDocs, filterIter.cost());
       if (useRandomAccess) {
         // if we are using random access, we return the inner scorer, just with other acceptDocs
-        return weight.scorer(context, filterAcceptDocs);
+        return weight.scorer(context, flags, filterAcceptDocs);
       } else {
         // we are gonna advance() this scorer, so we set inorder=true/toplevel=false
         // we pass null as acceptDocs, as our filter has already respected acceptDocs, no need to do twice
-        final Scorer scorer = weight.scorer(context, null);
+        final Scorer scorer = weight.scorer(context, flags, null);
         return (scorer == null) ? null : new LeapFrogScorer(weight, filterIter, scorer, scorer);
       }
     }
@@ -551,14 +531,14 @@
 
     @Override
     public Scorer filteredScorer(LeafReaderContext context,
-        Weight weight, DocIdSet docIdSet) throws IOException {
+        Weight weight, DocIdSet docIdSet, int flags) throws IOException {
       final DocIdSetIterator filterIter = docIdSet.iterator();
       if (filterIter == null) {
         // this means the filter does not accept any documents.
         return null;
       }
       // we pass null as acceptDocs, as our filter has already respected acceptDocs, no need to do twice
-      final Scorer scorer = weight.scorer(context, null);
+      final Scorer scorer = weight.scorer(context, flags, null);
       if (scorer == null) {
         return null;
       }
@@ -588,30 +568,29 @@
     @Override
     public Scorer filteredScorer(final LeafReaderContext context,
         Weight weight,
-        DocIdSet docIdSet) throws IOException {
+        DocIdSet docIdSet, int flags) throws IOException {
       Bits filterAcceptDocs = docIdSet.bits();
       if (filterAcceptDocs == null) {
         // Filter does not provide random-access Bits; we
         // must fallback to leapfrog:
-        return LEAP_FROG_QUERY_FIRST_STRATEGY.filteredScorer(context, weight, docIdSet);
+        return LEAP_FROG_QUERY_FIRST_STRATEGY.filteredScorer(context, weight, docIdSet, flags);
       }
-      final Scorer scorer = weight.scorer(context, null);
-      return scorer == null ? null : new QueryFirstScorer(weight,
-          filterAcceptDocs, scorer);
+      final Scorer scorer = weight.scorer(context, flags, null);
+      return scorer == null ? null : new QueryFirstScorer(weight, filterAcceptDocs, scorer);
     }
 
     @Override
     public BulkScorer filteredBulkScorer(final LeafReaderContext context,
         Weight weight,
         boolean scoreDocsInOrder, // ignored (we always top-score in order)
-        DocIdSet docIdSet) throws IOException {
+        DocIdSet docIdSet, int flags) throws IOException {
       Bits filterAcceptDocs = docIdSet.bits();
       if (filterAcceptDocs == null) {
         // Filter does not provide random-access Bits; we
         // must fallback to leapfrog:
-        return LEAP_FROG_QUERY_FIRST_STRATEGY.filteredBulkScorer(context, weight, scoreDocsInOrder, docIdSet);
+        return LEAP_FROG_QUERY_FIRST_STRATEGY.filteredBulkScorer(context, weight, scoreDocsInOrder, docIdSet, flags);
       }
-      final Scorer scorer = weight.scorer(context, null);
+      final Scorer scorer = weight.scorer(context, flags, null);
       return scorer == null ? null : new QueryFirstBulkScorer(scorer, filterAcceptDocs);
     }
   }
diff --git a/lucene/core/src/java/org/apache/lucene/search/FuzzyTermsEnum.java b/lucene/core/src/java/org/apache/lucene/search/FuzzyTermsEnum.java
index 3199966..8a18443 100644
--- a/lucene/core/src/java/org/apache/lucene/search/FuzzyTermsEnum.java
+++ b/lucene/core/src/java/org/apache/lucene/search/FuzzyTermsEnum.java
@@ -22,7 +22,7 @@
 import java.util.Comparator;
 import java.util.List;
 
-import org.apache.lucene.index.DocsAndPositionsEnum;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FilteredTermsEnum;
 import org.apache.lucene.index.Term;
@@ -271,8 +271,8 @@
   }
   
   @Override
-  public DocsAndPositionsEnum docsAndPositions(Bits liveDocs,
-                                               DocsAndPositionsEnum reuse, int flags) throws IOException {
+  public DocsEnum docsAndPositions(Bits liveDocs,
+                                               DocsEnum reuse, int flags) throws IOException {
     return actualEnum.docsAndPositions(liveDocs, reuse, flags);
   }
   
diff --git a/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java b/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java
index 69010e2..a817311 100644
--- a/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java
+++ b/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java
@@ -32,11 +32,13 @@
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.DirectoryReader; // javadocs
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.MultiFields;
 import org.apache.lucene.index.IndexReaderContext;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.MultiFields;
 import org.apache.lucene.index.ReaderUtil;
 import org.apache.lucene.index.StoredDocument;
 import org.apache.lucene.index.StoredFieldVisitor;
@@ -45,9 +47,8 @@
 import org.apache.lucene.index.Terms;
 import org.apache.lucene.search.similarities.DefaultSimilarity;
 import org.apache.lucene.search.similarities.Similarity;
-import org.apache.lucene.store.NIOFSDirectory;    // javadoc
+import org.apache.lucene.store.NIOFSDirectory;
 import org.apache.lucene.util.ThreadInterruptedException;
-import org.apache.lucene.index.IndexWriter; // javadocs
 
 /** Implements search over a single IndexReader.
  *
@@ -608,7 +609,7 @@
         // continue with the following leaf
         continue;
       }
-      BulkScorer scorer = weight.bulkScorer(ctx, !leafCollector.acceptsDocsOutOfOrder(), ctx.reader().getLiveDocs());
+      BulkScorer scorer = weight.bulkScorer(ctx, !leafCollector.acceptsDocsOutOfOrder(), DocsEnum.FLAG_FREQS, ctx.reader().getLiveDocs());
       if (scorer != null) {
         try {
           scorer.score(leafCollector);
diff --git a/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java b/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java
index d8b751b..65c5aad 100644
--- a/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java
@@ -17,14 +17,15 @@
  * limitations under the License.
  */
 
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.util.ToStringUtils;
-import org.apache.lucene.util.Bits;
-
-import java.util.Set;
 import java.io.IOException;
+import java.util.Set;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.ToStringUtils;
 
 /**
  * A query that matches all documents.
@@ -73,6 +74,36 @@
     }
 
     @Override
+    public int nextPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
+    @Override
     public int advance(int target) throws IOException {
       doc = target-1;
       return nextDoc();
@@ -114,7 +145,7 @@
     }
 
     @Override
-    public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
+    public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
       return new MatchAllScorer(context.reader(), acceptDocs, this, queryWeight);
     }
 
diff --git a/lucene/core/src/java/org/apache/lucene/search/MinShouldMatchSumScorer.java b/lucene/core/src/java/org/apache/lucene/search/MinShouldMatchSumScorer.java
index 3f31ace..220647c 100644
--- a/lucene/core/src/java/org/apache/lucene/search/MinShouldMatchSumScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/MinShouldMatchSumScorer.java
@@ -24,6 +24,7 @@
 import java.util.List;
 
 import org.apache.lucene.util.ArrayUtil;
+import org.apache.lucene.util.BytesRef;
 
 /**
  * A Scorer for OR like queries, counterpart of <code>ConjunctionScorer</code>.
@@ -62,6 +63,8 @@
   
   private final float coord[];
 
+  private final PositionQueue posQueue;
+
   /**
    * Construct a <code>MinShouldMatchSumScorer</code>.
    * 
@@ -110,6 +113,8 @@
     this.coord = coord;
     minheapHeapify();
     assert minheapCheck();
+
+    posQueue = new PositionQueue(subScorers.toArray(new Scorer[subScorers.size()]));
   }
 
   @Override
@@ -145,6 +150,7 @@
         break;
       }
     }
+    posQueue.advanceTo(doc);
     return doc;
   }
   
@@ -231,6 +237,36 @@
     return nrMatchers;
   }
 
+  @Override
+  public int nextPosition() throws IOException {
+    return posQueue.nextPosition();
+  }
+
+  @Override
+  public int startPosition() throws IOException {
+    return posQueue.startPosition();
+  }
+
+  @Override
+  public int endPosition() throws IOException {
+    return posQueue.endPosition();
+  }
+
+  @Override
+  public int startOffset() throws IOException {
+    return posQueue.startOffset();
+  }
+
+  @Override
+  public int endOffset() throws IOException {
+    return posQueue.endOffset();
+  }
+
+  @Override
+  public BytesRef getPayload() throws IOException {
+    return posQueue.getPayload();
+  }
+
   /**
    * Advances to the first match beyond the current whose document number is
    * greater than or equal to a given target. <br>
@@ -261,6 +297,7 @@
     evaluateSmallestDocInHeap();
 
     if (nrMatchers >= mm) {
+      posQueue.advanceTo(doc);
       return doc;
     } else {
       return nextDoc();
diff --git a/lucene/core/src/java/org/apache/lucene/search/MultiCollector.java b/lucene/core/src/java/org/apache/lucene/search/MultiCollector.java
index b901515..fb0e69c 100644
--- a/lucene/core/src/java/org/apache/lucene/search/MultiCollector.java
+++ b/lucene/core/src/java/org/apache/lucene/search/MultiCollector.java
@@ -101,7 +101,6 @@
     return new MultiLeafCollector(leafCollectors);
   }
 
-
   private static class MultiLeafCollector implements LeafCollector {
 
     private final LeafCollector[] collectors;
diff --git a/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java b/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java
index ffaa701..821373f 100644
--- a/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java
@@ -1,6 +1,6 @@
 package org.apache.lucene.search;
 
-/*
+/**
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
  * this work for additional information regarding copyright ownership.
@@ -18,24 +18,34 @@
  */
 
 import java.io.IOException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
 
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.DocsAndPositionsEnum;
-import org.apache.lucene.index.LeafReader;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.IndexReaderContext;
+import org.apache.lucene.index.LeafReader;
+import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.index.TermContext;
 import org.apache.lucene.index.TermState;
 import org.apache.lucene.index.Terms;
 import org.apache.lucene.index.TermsEnum;
-import org.apache.lucene.search.similarities.Similarity.SimScorer;
+import org.apache.lucene.search.PhraseQuery.TermDocsEnumFactory;
 import org.apache.lucene.search.similarities.Similarity;
+import org.apache.lucene.search.similarities.Similarity.SimScorer;
 import org.apache.lucene.util.ArrayUtil;
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.IntroSorter;
 import org.apache.lucene.util.PriorityQueue;
 import org.apache.lucene.util.ToStringUtils;
 
@@ -179,10 +189,13 @@
     }
 
     @Override
-    public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
+    public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
       assert !termArrays.isEmpty();
       final LeafReader reader = context.reader();
       final Bits liveDocs = acceptDocs;
+
+      if ((flags & DocsEnum.FLAG_POSITIONS) < DocsEnum.FLAG_POSITIONS)
+        flags = DocsEnum.FLAG_POSITIONS;
       
       PhraseQuery.PostingsAndFreq[] postingsFreqs = new PhraseQuery.PostingsAndFreq[termArrays.size()];
 
@@ -197,9 +210,9 @@
       for (int pos=0; pos<postingsFreqs.length; pos++) {
         Term[] terms = termArrays.get(pos);
 
-        final DocsAndPositionsEnum postingsEnum;
+        final DocsEnum postingsEnum;
         int docFreq;
-
+        TermDocsEnumFactory factory;
         if (terms.length > 1) {
           postingsEnum = new UnionDocsAndPositionsEnum(liveDocs, context, terms, termContexts, termsEnum);
 
@@ -221,6 +234,7 @@
             // None of the terms are in this reader
             return null;
           }
+          factory = new MultiTermDocsEnumFactory(liveDocs, context, terms, termContexts, termsEnum, flags);
         } else {
           final Term term = terms[0];
           TermState termState = termContexts.get(term).get(context.ord);
@@ -229,7 +243,7 @@
             return null;
           }
           termsEnum.seekExact(term.bytes(), termState);
-          postingsEnum = termsEnum.docsAndPositions(liveDocs, null, DocsEnum.FLAG_NONE);
+          postingsEnum = termsEnum.docsAndPositions(liveDocs, null, flags);
 
           if (postingsEnum == null) {
             // term does exist, but has no positions
@@ -237,10 +251,10 @@
             throw new IllegalStateException("field \"" + term.field() + "\" was indexed without position data; cannot run PhraseQuery (term=" + term.text() + ")");
           }
 
-          docFreq = termsEnum.docFreq();
+          factory = new TermDocsEnumFactory(term.bytes(), termState, termsEnum, flags, acceptDocs);
         }
-
-        postingsFreqs[pos] = new PhraseQuery.PostingsAndFreq(postingsEnum, docFreq, positions.get(pos).intValue(), terms);
+        
+        postingsFreqs[pos] = new PhraseQuery.PostingsAndFreq(postingsEnum, factory, termsEnum.docFreq() , positions.get(pos).intValue(), terms);
       }
 
       // sort by increasing docFreq order
@@ -257,7 +271,7 @@
 
     @Override
     public Explanation explain(LeafReaderContext context, int doc) throws IOException {
-      Scorer scorer = scorer(context, context.reader().getLiveDocs());
+      Scorer scorer = scorer(context, DocsEnum.FLAG_POSITIONS, context.reader().getLiveDocs());
       if (scorer != null) {
         int newDoc = scorer.advance(doc);
         if (newDoc == doc) {
@@ -401,6 +415,27 @@
     }
     return true;
   }
+
+  private static class MultiTermDocsEnumFactory extends TermDocsEnumFactory {
+
+    LeafReaderContext context;
+    Term[] terms;
+    Map<Term, TermContext> termContexts;
+
+    MultiTermDocsEnumFactory(Bits liveDocs, LeafReaderContext context, Term[] terms,
+                             Map<Term,TermContext> termContexts, TermsEnum termsEnum, int flags) throws IOException {
+      super(termsEnum, flags, liveDocs);
+      this.context = context;
+      this.terms = terms;
+      this.termContexts = termContexts;
+    }
+
+    @Override
+    public DocsEnum docsAndPositionsEnum() throws IOException {
+      return new UnionDocsAndPositionsEnum(liveDocs, context, terms, termContexts, termsEnum, flags);
+    }
+
+  }
 }
 
 /**
@@ -408,15 +443,15 @@
  */
 
 // TODO: if ever we allow subclassing of the *PhraseScorer
-class UnionDocsAndPositionsEnum extends DocsAndPositionsEnum {
+class UnionDocsAndPositionsEnum extends DocsEnum {
 
-  private static final class DocsQueue extends PriorityQueue<DocsAndPositionsEnum> {
-    DocsQueue(List<DocsAndPositionsEnum> docsEnums) throws IOException {
+  private static final class DocsQueue extends PriorityQueue<DocsEnum> {
+    DocsQueue(List<DocsEnum> docsEnums) throws IOException {
       super(docsEnums.size());
 
-      Iterator<DocsAndPositionsEnum> i = docsEnums.iterator();
+      Iterator<DocsEnum> i = docsEnums.iterator();
       while (i.hasNext()) {
-        DocsAndPositionsEnum postings = i.next();
+        DocsEnum postings = i.next();
         if (postings.nextDoc() != DocIdSetIterator.NO_MORE_DOCS) {
           add(postings);
         }
@@ -424,30 +459,54 @@
     }
 
     @Override
-    public final boolean lessThan(DocsAndPositionsEnum a, DocsAndPositionsEnum b) {
+    public final boolean lessThan(DocsEnum a, DocsEnum b) {
       return a.docID() < b.docID();
     }
   }
 
-  private static final class IntQueue {
-    private int _arraySize = 16;
+  // TODO: Reimplement this as int[_arraySize * 3], storing position at i * 3,
+  // startOffset at i * 3 + 1 and endOffset at i * 3 + 2.  Will need to also
+  // implement a new SorterTemplate to sort the array.
+
+  private static final class PositionQueue {
+    private int _arraySize = 48;
     private int _index = 0;
     private int _lastIndex = 0;
     private int[] _array = new int[_arraySize];
     
-    final void add(int i) {
-      if (_lastIndex == _arraySize)
+    final void add(int pos, int start, int end) {
+      if (_lastIndex * 3 == _arraySize)
         growArray();
 
-      _array[_lastIndex++] = i;
+      _array[_lastIndex * 3] = pos;
+      _array[_lastIndex * 3 + 1] = start;
+      _array[_lastIndex * 3 + 2] = end;
+      _lastIndex += 1;
     }
 
     final int next() {
-      return _array[_index++];
+      return _array[_index++ * 3];
+    }
+
+    final int startPosition() {
+      return _array[(_index - 1) * 3];
+    }
+
+    final int endPosition() {
+      return _array[(_index - 1) * 3];
+    }
+
+    final int startOffset() {
+      return _array[(_index - 1) * 3 + 1];
+    }
+
+    final int endOffset() {
+      return _array[(_index - 1) * 3 + 2];
     }
 
     final void sort() {
-      Arrays.sort(_array, _index, _lastIndex);
+      //Arrays.sort(_array, _index, _lastIndex);
+      sorter.sort(_index, _lastIndex);
     }
 
     final void clear() {
@@ -465,16 +524,54 @@
       _array = newArray;
       _arraySize *= 2;
     }
+
+    private IntroSorter sorter = new IntroSorter() {
+      private int pivot;
+
+      @Override
+      protected void swap(int i, int j) {
+        int ti = _array[i * 3];
+        int ts = _array[i * 3 + 1];
+        int te = _array[i * 3 + 2];
+        _array[i * 3] = _array[j * 3];
+        _array[i * 3 + 1] = _array[j * 3 + 1];
+        _array[i * 3 + 2] = _array[j * 3 + 2];
+        _array[j * 3] = ti;
+        _array[j * 3 + 1] = ts;
+        _array[j * 3 + 2] = te;
+      }
+
+      @Override
+      protected int compare(int i, int j) {
+        return _array[i * 3] - _array[j * 3];
+      }
+
+      @Override
+      protected void setPivot(int i) {
+        pivot = i;
+      }
+
+      @Override
+      protected int comparePivot(int j) {
+        return pivot - _array[j * 3];
+      }
+    };
   }
 
   private int _doc = -1;
   private int _freq;
   private DocsQueue _queue;
-  private IntQueue _posList;
+  private PositionQueue _posList;
+  private int posPending;
   private long cost;
 
-  public UnionDocsAndPositionsEnum(Bits liveDocs, LeafReaderContext context, Term[] terms, Map<Term,TermContext> termContexts, TermsEnum termsEnum) throws IOException {
-    List<DocsAndPositionsEnum> docsEnums = new LinkedList<>();
+  public UnionDocsAndPositionsEnum(Bits liveDocs, LeafReaderContext context, Term[] terms,
+                                   Map<Term,TermContext> termContexts, TermsEnum termsEnum) throws IOException {
+    this(liveDocs, context, terms, termContexts, termsEnum, DocsEnum.FLAG_POSITIONS);
+  }
+
+  public UnionDocsAndPositionsEnum(Bits liveDocs, LeafReaderContext context, Term[] terms, Map<Term,TermContext> termContexts, TermsEnum termsEnum, int flags) throws IOException {
+    List<DocsEnum> docsEnums = new LinkedList<>();
     for (int i = 0; i < terms.length; i++) {
       final Term term = terms[i];
       TermState termState = termContexts.get(term).get(context.ord);
@@ -483,7 +580,7 @@
         continue;
       }
       termsEnum.seekExact(term.bytes(), termState);
-      DocsAndPositionsEnum postings = termsEnum.docsAndPositions(liveDocs, null, DocsEnum.FLAG_NONE);
+      DocsEnum postings = termsEnum.docsAndPositions(liveDocs, null, flags);
       if (postings == null) {
         // term does exist, but has no positions
         throw new IllegalStateException("field \"" + term.field() + "\" was indexed without position data; cannot run PhraseQuery (term=" + term.text() + ")");
@@ -493,7 +590,7 @@
     }
 
     _queue = new DocsQueue(docsEnums);
-    _posList = new IntQueue();
+    _posList = new PositionQueue();
   }
 
   @Override
@@ -509,13 +606,13 @@
     _doc = _queue.top().docID();
 
     // merge sort all positions together
-    DocsAndPositionsEnum postings;
+    DocsEnum postings;
     do {
       postings = _queue.top();
 
       final int freq = postings.freq();
       for (int i = 0; i < freq; i++) {
-        _posList.add(postings.nextPosition());
+        _posList.add(postings.nextPosition(), postings.startOffset(), postings.endOffset());
       }
 
       if (postings.nextDoc() != NO_MORE_DOCS) {
@@ -527,23 +624,37 @@
 
     _posList.sort();
     _freq = _posList.size();
+    posPending = _freq;
 
     return _doc;
   }
 
   @Override
   public int nextPosition() {
+    if (posPending == 0)
+      return NO_MORE_POSITIONS;
+    posPending--;
     return _posList.next();
   }
 
   @Override
+  public int startPosition() throws IOException {
+    return _posList.startPosition();
+  }
+
+  @Override
+  public int endPosition() throws IOException {
+    return _posList.endPosition();
+  }
+
+  @Override
   public int startOffset() {
-    return -1;
+    return _posList.startOffset();
   }
 
   @Override
   public int endOffset() {
-    return -1;
+    return _posList.endOffset();
   }
 
   @Override
@@ -554,7 +665,7 @@
   @Override
   public final int advance(int target) throws IOException {
     while (_queue.top() != null && target > _queue.top().docID()) {
-      DocsAndPositionsEnum postings = _queue.pop();
+      DocsEnum postings = _queue.pop();
       if (postings.advance(target) != NO_MORE_DOCS) {
         _queue.add(postings);
       }
@@ -563,7 +674,7 @@
   }
 
   @Override
-  public final int freq() {
+  public final int freq() throws IOException {
     return _freq;
   }
 
diff --git a/lucene/core/src/java/org/apache/lucene/search/PhrasePositions.java b/lucene/core/src/java/org/apache/lucene/search/PhrasePositions.java
index c975b01..9760d53 100644
--- a/lucene/core/src/java/org/apache/lucene/search/PhrasePositions.java
+++ b/lucene/core/src/java/org/apache/lucene/search/PhrasePositions.java
@@ -18,32 +18,35 @@
  */
 
 import java.io.IOException;
-import org.apache.lucene.index.*;
+
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.index.Term;
 
 /**
- * Position of a term in a document that takes into account the term offset within the phrase. 
+ * Position of a term in a document that takes into account the term phraseOffset within the phrase.
  */
 final class PhrasePositions {
   int doc;              // current doc
   int position;         // position in doc
-  int count;            // remaining pos in this doc
-  int offset;           // position in phrase
+  //int count;            // remaining pos in this doc
+  int phraseOffset;           // position in phrase
   final int ord;                                  // unique across all PhrasePositions instances
-  final DocsAndPositionsEnum postings;            // stream of docs & positions
+  final DocsEnum postings;            // stream of docs & positions
   PhrasePositions next;                           // used to make lists
   int rptGroup = -1; // >=0 indicates that this is a repeating PP
   int rptInd; // index in the rptGroup
   final Term[] terms; // for repetitions initialization 
 
-  PhrasePositions(DocsAndPositionsEnum postings, int o, int ord, Term[] terms) {
+  PhrasePositions(DocsEnum postings, int o, int ord, Term[] terms) {
     this.postings = postings;
-    offset = o;
+    phraseOffset = o;
     this.ord = ord;
     this.terms = terms;
   }
 
   final boolean next() throws IOException {  // increments to next doc
     doc = postings.nextDoc();
+    
     if (doc == DocIdSetIterator.NO_MORE_DOCS) {
       return false;
     }
@@ -59,31 +62,44 @@
   }
 
   final void firstPosition() throws IOException {
-    count = postings.freq();  // read first pos
     nextPosition();
   }
 
   /**
    * Go to next location of this term current document, and set 
-   * <code>position</code> as <code>location - offset</code>, so that a 
+   * <code>position</code> as <code>location - phraseOffset</code>, so that a
    * matching exact phrase is easily identified when all PhrasePositions 
    * have exactly the same <code>position</code>.
    */
   final boolean nextPosition() throws IOException {
-    if (count-- > 0) {  // read subsequent pos's
-      position = postings.nextPosition() - offset;
-      return true;
-    } else
+    int nextPos = postings.nextPosition();
+    if (nextPos == DocsEnum.NO_MORE_POSITIONS) {
+      position = nextPos;
       return false;
+    }
+    position = nextPos - phraseOffset;
+    return true;
+  }
+
+  public final int startOffset() throws IOException {
+    return postings.startOffset();
+  }
+
+  public final int endOffset() throws IOException {
+    return postings.endOffset();
   }
   
   /** for debug purposes */
   @Override
   public String toString() {
-    String s = "d:"+doc+" o:"+offset+" p:"+position+" c:"+count;
+    String s = "d:"+doc+" phraseOffset:"+ phraseOffset +" position:"+position;
     if (rptGroup >=0 ) {
       s += " rpt:"+rptGroup+",i"+rptInd;
     }
+    s += " t: [" + terms[0];
+    for (int i = 1; i < terms.length; i++)
+      s += "," + terms[1];
+    s += "]";
     return s;
   }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java b/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java
index bf5a373..90eb341 100644
--- a/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java
@@ -1,6 +1,6 @@
 package org.apache.lucene.search;
 
-/*
+/**
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
  * this work for additional information regarding copyright ownership.
@@ -22,21 +22,21 @@
 import java.util.Arrays;
 import java.util.Set;
 
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.LeafReader;
 import org.apache.lucene.index.IndexReaderContext;
+import org.apache.lucene.index.LeafReader;
+import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.index.TermContext;
 import org.apache.lucene.index.TermState;
 import org.apache.lucene.index.Terms;
 import org.apache.lucene.index.TermsEnum;
-import org.apache.lucene.search.similarities.Similarity.SimScorer;
 import org.apache.lucene.search.similarities.Similarity;
+import org.apache.lucene.search.similarities.Similarity.SimScorer;
 import org.apache.lucene.util.ArrayUtil;
 import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.ToStringUtils;
 
 /** A Query that matches documents containing a particular sequence of terms.
@@ -138,13 +138,15 @@
   }
 
   static class PostingsAndFreq implements Comparable<PostingsAndFreq> {
-    final DocsAndPositionsEnum postings;
+    final TermDocsEnumFactory factory;
+    final DocsEnum postings;
     final int docFreq;
     final int position;
     final Term[] terms;
     final int nTerms; // for faster comparisons
 
-    public PostingsAndFreq(DocsAndPositionsEnum postings, int docFreq, int position, Term... terms) {
+    public PostingsAndFreq(DocsEnum postings, TermDocsEnumFactory factory, int docFreq, int position, Term... terms) throws IOException {
+      this.factory = factory;
       this.postings = postings;
       this.docFreq = docFreq;
       this.position = position;
@@ -245,7 +247,7 @@
     }
 
     @Override
-    public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
+    public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
       assert !terms.isEmpty();
       final LeafReader reader = context.reader();
       final Bits liveDocs = acceptDocs;
@@ -267,7 +269,7 @@
           return null;
         }
         te.seekExact(t.bytes(), state);
-        DocsAndPositionsEnum postingsEnum = te.docsAndPositions(liveDocs, null, DocsEnum.FLAG_NONE);
+        DocsEnum postingsEnum = te.docs(liveDocs, null, DocsEnum.FLAG_POSITIONS);
 
         // PhraseQuery on a field that did not index
         // positions.
@@ -276,7 +278,8 @@
           // term does exist, but has no positions
           throw new IllegalStateException("field \"" + t.field() + "\" was indexed without position data; cannot run PhraseQuery (term=" + t.text() + ")");
         }
-        postingsFreqs[i] = new PostingsAndFreq(postingsEnum, te.docFreq(), positions.get(i).intValue(), t);
+        TermDocsEnumFactory factory = new TermDocsEnumFactory(t.bytes(), state, te, flags, acceptDocs);
+        postingsFreqs[i] = new PostingsAndFreq(postingsEnum, factory, te.docFreq(), positions.get(i).intValue(), t);
       }
 
       // sort by increasing docFreq order
@@ -298,11 +301,11 @@
 
     @Override
     public Explanation explain(LeafReaderContext context, int doc) throws IOException {
-      Scorer scorer = scorer(context, context.reader().getLiveDocs());
+      Scorer scorer = scorer(context, DocsEnum.FLAG_POSITIONS, context.reader().getLiveDocs());
       if (scorer != null) {
         int newDoc = scorer.advance(doc);
         if (newDoc == doc) {
-          float freq = slop == 0 ? scorer.freq() : ((SloppyPhraseScorer)scorer).sloppyFreq();
+          float freq = slop == 0 ? ((ExactPhraseScorer)scorer).phraseFreq() : ((SloppyPhraseScorer)scorer).sloppyFreq();
           SimScorer docScorer = similarity.simScorer(stats, context);
           ComplexExplanation result = new ComplexExplanation();
           result.setDescription("weight("+getQuery()+" in "+doc+") [" + similarity.getClass().getSimpleName() + "], result of:");
@@ -396,4 +399,33 @@
       ^ positions.hashCode();
   }
 
+  static class TermDocsEnumFactory {
+    protected final TermsEnum termsEnum;
+    protected final Bits liveDocs;
+    protected final int flags;
+
+    private final BytesRef term;
+    private final TermState termState;
+    
+    TermDocsEnumFactory(TermsEnum termsEnum, int flags, Bits liveDocs) {
+      this(null, null, termsEnum, flags, liveDocs);
+    }
+    
+    TermDocsEnumFactory(BytesRef term, TermState termState, TermsEnum termsEnum, int flags,  Bits liveDocs) {
+      this.termsEnum = termsEnum;
+      this.termState = termState;
+      this.liveDocs = liveDocs;
+      this.term = term;
+      this.flags = flags;
+    }
+    
+    
+    public DocsEnum docsAndPositionsEnum()
+        throws IOException {
+      assert term != null;
+      termsEnum.seekExact(term, termState);
+      return termsEnum.docsAndPositions(liveDocs, null, flags);
+    }
+
+  }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/PhraseQueue.java b/lucene/core/src/java/org/apache/lucene/search/PhraseQueue.java
index d2c8655..5d63c51 100644
--- a/lucene/core/src/java/org/apache/lucene/search/PhraseQueue.java
+++ b/lucene/core/src/java/org/apache/lucene/search/PhraseQueue.java
@@ -29,11 +29,11 @@
     if (pp1.doc == pp2.doc) 
       if (pp1.position == pp2.position)
         // same doc and pp.position, so decide by actual term positions. 
-        // rely on: pp.position == tp.position - offset. 
-        if (pp1.offset == pp2.offset) {
+        // rely on: pp.position == tp.position - phraseOffset.
+        if (pp1.phraseOffset == pp2.phraseOffset) {
           return pp1.ord < pp2.ord;
         } else {
-          return pp1.offset < pp2.offset;
+          return pp1.phraseOffset < pp2.phraseOffset;
         }
       else {
         return pp1.position < pp2.position;
@@ -42,4 +42,8 @@
       return pp1.doc < pp2.doc;
     }
   }
+
+  public Object[] getPPs() {
+    return getHeapArray();
+  }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/PositionQueue.java b/lucene/core/src/java/org/apache/lucene/search/PositionQueue.java
new file mode 100644
index 0000000..a599d39
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/search/PositionQueue.java
@@ -0,0 +1,142 @@
+package org.apache.lucene.search;
+
+import java.io.IOException;
+
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.PriorityQueue;
+
+/**
+ * Copyright (c) 2013 Lemur Consulting Ltd.
+ * <p/>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+public class PositionQueue extends PriorityQueue<PositionQueue.DocsEnumRef> {
+
+  public class DocsEnumRef implements Comparable<DocsEnumRef> {
+
+    public final DocsEnum docsEnum;
+    public final int ord;
+    public int start;
+    public int end;
+
+    public DocsEnumRef(DocsEnum docsEnum, int ord) {
+      this.docsEnum = docsEnum;
+      this.ord = ord;
+    }
+
+    public int nextPosition() throws IOException {
+      assert docsEnum.docID() != -1;
+      if (docsEnum.docID() == DocsEnum.NO_MORE_DOCS || docsEnum.docID() != docId
+          || docsEnum.nextPosition() == DocsEnum.NO_MORE_POSITIONS) {
+        start = end = DocsEnum.NO_MORE_POSITIONS;
+      }
+      else {
+        start = docsEnum.startPosition();
+        end = docsEnum.endPosition();
+      }
+      return start;
+    }
+
+    @Override
+    public int compareTo(DocsEnumRef o) {
+      if (this.docsEnum.docID() < o.docsEnum.docID())
+        return -1;
+      if (this.docsEnum.docID() > o.docsEnum.docID())
+        return 1;
+      if (this.start == DocsEnum.NO_MORE_POSITIONS)
+        return 1;
+      if (o.start == DocsEnum.NO_MORE_POSITIONS)
+        return -1;
+      return Integer.compare(this.start, o.start);
+    }
+  }
+
+  boolean positioned = false;
+  int pos = -1;
+  int docId = -1;
+  protected int queuesize;
+
+  public PositionQueue(DocsEnum... subDocsEnums) {
+    super(subDocsEnums.length);
+    for (int i = 0; i < subDocsEnums.length; i++) {
+      add(new DocsEnumRef(subDocsEnums[i], i));
+    }
+    queuesize = subDocsEnums.length;
+  }
+
+  protected void init() throws IOException {
+    queuesize = 0;
+    for (Object scorerRef : getHeapArray()) {
+      if (scorerRef != null) {
+        ((DocsEnumRef) scorerRef).nextPosition();
+        queuesize++;
+      }
+    }
+    updateTop();
+  }
+
+  public int nextPosition() throws IOException {
+    if (!positioned) {
+      init();
+      positioned = true;
+      return pos = top().start;
+    }
+    if (pos == DocsEnum.NO_MORE_POSITIONS)
+      return DocsEnum.NO_MORE_POSITIONS;
+    if (top().nextPosition() == DocsEnum.NO_MORE_POSITIONS)
+      queuesize--;
+    else
+      updateInternalIntervals();
+    updateTop();
+    return pos = top().start;
+  }
+
+  @Override
+  protected boolean lessThan(DocsEnumRef a, DocsEnumRef b) {
+    return a.compareTo(b) < 0;
+  }
+
+  protected void updateInternalIntervals() throws IOException {}
+
+  /**
+   * Must be called after the scorers have been advanced
+   */
+  public void advanceTo(int doc) {
+    positioned = false;
+    this.docId = doc;
+    this.queuesize = this.size();
+  }
+
+  public int startPosition() throws IOException {
+    return top().docsEnum.startPosition();
+  }
+
+  public int endPosition() throws IOException {
+    return top().docsEnum.endPosition();
+  }
+
+  public int startOffset() throws IOException {
+    return top().docsEnum.startOffset();
+  }
+
+  public int endOffset() throws IOException {
+    return top().docsEnum.endOffset();
+  }
+
+  public BytesRef getPayload() throws IOException {
+    return top().docsEnum.getPayload();
+  }
+
+
+}
diff --git a/lucene/core/src/java/org/apache/lucene/search/QueryRescorer.java b/lucene/core/src/java/org/apache/lucene/search/QueryRescorer.java
index 2f17145..8d0d573 100644
--- a/lucene/core/src/java/org/apache/lucene/search/QueryRescorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/QueryRescorer.java
@@ -17,13 +17,14 @@
  * limitations under the License.
  */
 
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.index.LeafReaderContext;
+
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.List;
 
-import org.apache.lucene.index.LeafReaderContext;
-
 /** A {@link Rescorer} that uses a provided Query to assign
  *  scores to the first-pass hits.
  *
@@ -82,7 +83,7 @@
       if (readerContext != null) {
         // We advanced to another segment:
         docBase = readerContext.docBase;
-        scorer = weight.scorer(readerContext, null);
+        scorer = weight.scorer(readerContext, DocsEnum.FLAG_NONE, null);
       }
 
       if(scorer != null) {
diff --git a/lucene/core/src/java/org/apache/lucene/search/QueryWrapperFilter.java b/lucene/core/src/java/org/apache/lucene/search/QueryWrapperFilter.java
index 8d8a010..270e4cc 100644
--- a/lucene/core/src/java/org/apache/lucene/search/QueryWrapperFilter.java
+++ b/lucene/core/src/java/org/apache/lucene/search/QueryWrapperFilter.java
@@ -17,11 +17,12 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.util.Bits;
 
+import java.io.IOException;
+
 /** 
  * Constrains search results to only match those which also match a provided
  * query.  
@@ -56,7 +57,7 @@
     return new DocIdSet() {
       @Override
       public DocIdSetIterator iterator() throws IOException {
-        return weight.scorer(privateContext, acceptDocs);
+        return weight.scorer(privateContext, DocsEnum.FLAG_FREQS, acceptDocs);
       }
 
       @Override
diff --git a/lucene/core/src/java/org/apache/lucene/search/ReqExclScorer.java b/lucene/core/src/java/org/apache/lucene/search/ReqExclScorer.java
index 4e2a5f1..cf6aec1 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ReqExclScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ReqExclScorer.java
@@ -27,7 +27,7 @@
  * This <code>Scorer</code> implements {@link Scorer#advance(int)},
  * and it uses the skipTo() on the given scorers.
  */
-class ReqExclScorer extends Scorer {
+class ReqExclScorer extends FilterScorer {
   private Scorer reqScorer;
   private DocIdSetIterator exclDisi;
   private int doc = -1;
@@ -37,7 +37,7 @@
    * @param exclDisi indicates exclusion.
    */
   public ReqExclScorer(Scorer reqScorer, DocIdSetIterator exclDisi) {
-    super(reqScorer.weight);
+    super(reqScorer);
     this.reqScorer = reqScorer;
     this.exclDisi = exclDisi;
   }
@@ -103,11 +103,6 @@
   public float score() throws IOException {
     return reqScorer.score(); // reqScorer may be null when next() or skipTo() already return false
   }
-  
-  @Override
-  public int freq() throws IOException {
-    return reqScorer.freq();
-  }
 
   @Override
   public Collection<ChildScorer> getChildren() {
@@ -129,8 +124,4 @@
     return doc = toNonExcluded();
   }
 
-  @Override
-  public long cost() {
-    return reqScorer.cost();
-  }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/ReqOptSumScorer.java b/lucene/core/src/java/org/apache/lucene/search/ReqOptSumScorer.java
index d7b4d86..ce6a007 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ReqOptSumScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ReqOptSumScorer.java
@@ -20,6 +20,8 @@
 import java.util.ArrayList;
 import java.util.Collection;
 
+import org.apache.lucene.util.BytesRef;
+
 /** A Scorer for queries with a required part and an optional part.
  * Delays skipTo() on the optional part until a score() is needed.
  * <br>
@@ -29,6 +31,7 @@
   /** The scorers passed from the constructor.
    * These are set to null as soon as their next() or skipTo() returns false.
    */
+  private PositionQueue posQueue;
   protected Scorer reqScorer;
   protected Scorer optScorer;
 
@@ -45,16 +48,21 @@
     assert optScorer != null;
     this.reqScorer = reqScorer;
     this.optScorer = optScorer;
+    posQueue = new PositionQueue(reqScorer, optScorer);
   }
 
   @Override
   public int nextDoc() throws IOException {
-    return reqScorer.nextDoc();
+    int doc = reqScorer.nextDoc();
+    posQueue.advanceTo(doc);
+    return doc;
   }
   
   @Override
   public int advance(int target) throws IOException {
-    return reqScorer.advance(target);
+    int doc = reqScorer.advance(target);
+    posQueue.advanceTo(doc);
+    return doc;
   }
   
   @Override
@@ -93,6 +101,39 @@
   }
 
   @Override
+  public int nextPosition() throws IOException {
+    int optDoc = optScorer.docID();
+    if (optDoc < reqScorer.docID())
+      optScorer.advance(reqScorer.docID());
+    return posQueue.nextPosition();
+  }
+
+  @Override
+  public int startPosition() throws IOException {
+    return posQueue.startPosition();
+  }
+
+  @Override
+  public int endPosition() throws IOException {
+    return posQueue.endPosition();
+  }
+
+  @Override
+  public int startOffset() throws IOException {
+    return posQueue.startOffset();
+  }
+
+  @Override
+  public int endOffset() throws IOException {
+    return posQueue.endOffset();
+  }
+
+  @Override
+  public BytesRef getPayload() throws IOException {
+    return posQueue.getPayload();
+  }
+
+  @Override
   public Collection<ChildScorer> getChildren() {
     ArrayList<ChildScorer> children = new ArrayList<>(2);
     children.add(new ChildScorer(reqScorer, "MUST"));
diff --git a/lucene/core/src/java/org/apache/lucene/search/Scorer.java b/lucene/core/src/java/org/apache/lucene/search/Scorer.java
index 929d3b9..696325b 100644
--- a/lucene/core/src/java/org/apache/lucene/search/Scorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/Scorer.java
@@ -20,6 +20,7 @@
 import java.io.IOException;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Locale;
 
 import org.apache.lucene.index.DocsEnum;
 
@@ -60,6 +61,13 @@
    * {@link LeafCollector#collect}.
    */
   public abstract float score() throws IOException;
+
+  /** Returns the score of the current interval spanned by this scorer.
+   * Initially invalid, until {@link #nextPosition()} is called
+   */
+  public float intervalScore() throws IOException {
+    return 1;
+  }
   
   /** returns parent Weight
    * @lucene.experimental
@@ -67,6 +75,15 @@
   public Weight getWeight() {
     return weight;
   }
+
+  @Override
+  public String toString() {
+    try {
+      return String.format(Locale.ROOT, "%d:%d(%d)->%d(%d)", docID(), startPosition(), startOffset(), endPosition(), endOffset());
+    } catch (IOException e) {
+      return super.toString();
+    }
+  }
   
   /** Returns child sub-scorers
    * @lucene.experimental */
diff --git a/lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java b/lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java
index 80a0270..402594a 100644
--- a/lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java
@@ -27,45 +27,53 @@
 
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.similarities.Similarity;
+import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.FixedBitSet;
 
+
 final class SloppyPhraseScorer extends Scorer {
   private PhrasePositions min, max;
 
-  private float sloppyFreq; //phrase frequency in current doc as computed by phraseFreq().
+  private float sloppyFreq;
 
   private final Similarity.SimScorer docScorer;
+  private final PhraseQuery.PostingsAndFreq[] postings;
   
   private final int slop;
   private final int numPostings;
   private final PhraseQueue pq; // for advancing min position
   
-  private int end; // current largest phrase position  
+  private int currentEnd; // current largest phrase position
+  private int currentRealEnd; // current largest phrase position, including phrase offset
+  private int currentEndOffset; // current largest phrase currentEnd offset
+
+  private int spanEnd;
+  private int spanOffsetEnd;
 
   private boolean hasRpts; // flag indicating that there are repetitions (as checked in first candidate doc)
   private boolean checkedRpts; // flag to only check for repetitions in first candidate doc
   private boolean hasMultiTermRpts; //  
-  private PhrasePositions[][] rptGroups; // in each group are PPs that repeats each other (i.e. same term), sorted by (query) offset 
+  private PhrasePositions[][] rptGroups; // in each group are PPs that repeats each other (i.e. same term), sorted by (query) phraseOffset
   private PhrasePositions[] rptStack; // temporary stack for switching colliding repeating pps 
   
-  private int numMatches;
   private final long cost;
   
   SloppyPhraseScorer(Weight weight, PhraseQuery.PostingsAndFreq[] postings,
       int slop, Similarity.SimScorer docScorer) {
     super(weight);
     this.docScorer = docScorer;
+    this.postings = postings;
     this.slop = slop;
     this.numPostings = postings==null ? 0 : postings.length;
-    pq = new PhraseQueue(postings.length);
-    // min(cost)
-    cost = postings[0].postings.cost();
+    pq = new PhraseQueue(this.numPostings);
     // convert tps to a list of phrase positions.
     // note: phrase-position differs from term-position in that its position
-    // reflects the phrase offset: pp.pos = tp.pos - offset.
+    // reflects the phrase phraseOffset: pp.pos = tp.pos - phraseOffset.
     // this allows to easily identify a matching (exact) phrase 
     // when all PhrasePositions have exactly the same position.
-    if (postings.length > 0) {
+    if (postings != null && postings.length > 0) {
+      // min(cost)
+      cost = postings[0].postings.cost();
       min = new PhrasePositions(postings[0].postings, postings[0].position, 0, postings[0].terms);
       max = min;
       max.doc = -1;
@@ -77,69 +85,113 @@
       }
       max.next = min; // make it cyclic for easier manipulation
     }
+    else {
+      cost = 0;
+    }
   }
 
-  /**
-   * Score a candidate doc for all slop-valid position-combinations (matches) 
-   * encountered while traversing/hopping the PhrasePositions.
-   * <br> The score contribution of a match depends on the distance: 
-   * <br> - highest score for distance=0 (exact match).
-   * <br> - score gets lower as distance gets higher.
-   * <br>Example: for query "a b"~2, a document "x a b a y" can be scored twice: 
-   * once for "a b" (distance=0), and once for "b a" (distance=2).
-   * <br>Possibly not all valid combinations are encountered, because for efficiency  
-   * we always propagate the least PhrasePosition. This allows to base on 
-   * PriorityQueue and move forward faster. 
-   * As result, for example, document "a b c b a"
-   * would score differently for queries "a b c"~4 and "c b a"~4, although 
-   * they really are equivalent. 
-   * Similarly, for doc "a b c b a f g", query "c b"~2 
-   * would get same score as "g f"~2, although "c b"~2 could be matched twice.
-   * We may want to fix this in the future (currently not, for performance reasons).
-   */
-  private float phraseFreq() throws IOException {
-    if (!initPhrasePositions()) {
-      return 0.0f;
+  private int matchLength;
+  private int startpos = -1;
+  private int startoffset = -1;
+
+  @Override
+  public int freq() throws IOException {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int nextPosition() throws IOException {
+    if (cached) {
+      cached = false;
+      return this.startPosition();
     }
-    float freq = 0.0f;
-    numMatches = 0;
-    PhrasePositions pp = pq.pop();
-    int matchLength = end - pp.position;
-    int next = pq.top().position; 
-    while (advancePP(pp)) {
-      if (hasRpts && !advanceRpts(pp)) {
+
+    if (pq.size() < postings.length)
+      return NO_MORE_POSITIONS;
+
+    PhrasePositions top = pq.pop();
+    matchLength = currentEnd - top.position;
+    int next = pq.top().position;
+    int pos = top.position + top.phraseOffset;
+    int startoffset = top.startOffset();
+    spanEnd = currentRealEnd;
+    spanOffsetEnd = currentEndOffset;
+    while (advancePP(top)) {
+      if (hasRpts && !advanceRpts(top))
         break; // pps exhausted
-      }
-      if (pp.position > next) { // done minimizing current match-length 
+      if (top.position > next) { // done minimizing current match-length
         if (matchLength <= slop) {
-          freq += docScorer.computeSlopFactor(matchLength); // score match
-          numMatches++;
-        }      
-        pq.add(pp);
-        pp = pq.pop();
-        next = pq.top().position;
-        matchLength = end - pp.position;
-      } else {
-        int matchLength2 = end - pp.position;
-        if (matchLength2 < matchLength) {
-          matchLength = matchLength2;
+          setSpanStart(pos, startoffset);
+          pq.add(top);
+          return startpos;
         }
+        pq.add(top);
+        top = pq.pop();
+        next = pq.top().position;
+        matchLength = currentEnd - top.position;
+        pos = top.position + top.phraseOffset;
+        startoffset = top.startOffset();
+        spanEnd = currentRealEnd;
+        spanOffsetEnd = currentEndOffset;
+      }
+      else {
+        int matchLength2 = currentEnd - top.position;
+        pos = top.position + top.phraseOffset;
+        startoffset = top.startOffset();
+        spanEnd = currentRealEnd;
+        spanOffsetEnd = currentEndOffset;
+        if (matchLength2 < matchLength)
+          matchLength = matchLength2;
       }
     }
+
     if (matchLength <= slop) {
-      freq += docScorer.computeSlopFactor(matchLength); // score match
-      numMatches++;
-    }    
-    return freq;
+      setSpanStart(pos, startoffset);
+      return startpos;
+    }
+
+    return NO_MORE_POSITIONS;
+
   }
 
-  /** advance a PhrasePosition and update 'end', return false if exhausted */
+  private void setSpanStart(int topPos, int topStartOffset) throws IOException {
+    startpos = topPos;
+    startoffset = topStartOffset;
+    for (Object o : pq.getPPs()) {
+      if (o == null)
+        continue;
+      PhrasePositions pp = (PhrasePositions) o;
+      if (pp.position != NO_MORE_POSITIONS) {
+        startpos = Math.min(startpos, pp.position + pp.phraseOffset);
+        startoffset = Math.min(startoffset, pp.startOffset());
+      }
+    }
+  }
+
+  boolean cached = false;
+
+  private int firstPosition() throws IOException {
+    if (!initPhrasePositions())
+      return NO_MORE_POSITIONS;
+
+    sloppyFreq = -1;
+    cached = false;
+    int pos = nextPosition();
+    cached = true;
+    return pos;
+  }
+
+  /** advance a PhrasePosition and update 'currentEnd', return false if exhausted */
   private boolean advancePP(PhrasePositions pp) throws IOException {
     if (!pp.nextPosition()) {
       return false;
     }
-    if (pp.position > end) {
-      end = pp.position;
+    if (pp.position > currentEnd) {
+      currentEnd = pp.position;
+    }
+    if (pp.position + pp.phraseOffset > currentRealEnd) {
+      currentRealEnd = pp.position + pp.phraseOffset;
+      currentEndOffset = pp.endOffset();
     }
     return true;
   }
@@ -186,10 +238,10 @@
     return true;
   }
 
-  /** compare two pps, but only by position and offset */
+  /** compare two pps, but only by position and phraseOffset */
   private PhrasePositions lesser(PhrasePositions pp, PhrasePositions pp2) {
     if (pp.position < pp2.position ||
-        (pp.position == pp2.position && pp.offset < pp2.offset)) {
+        (pp.position == pp2.position && pp.phraseOffset < pp2.phraseOffset)) {
       return pp;
     }
     return pp2;
@@ -224,7 +276,8 @@
    * @return false if PPs are exhausted (and so current doc will not be a match) 
    */
   private boolean initPhrasePositions() throws IOException {
-    end = Integer.MIN_VALUE;
+    currentEnd = currentRealEnd = Integer.MIN_VALUE;
+    currentEndOffset = -1;
     if (!checkedRpts) {
       return initFirstTime();
     }
@@ -242,8 +295,12 @@
     // position pps and build queue from list
     for (PhrasePositions pp=min,prev=null; prev!=max; pp=(prev=pp).next) {  // iterate cyclic list: done once handled max
       pp.firstPosition();
-      if (pp.position > end) {
-        end = pp.position;
+      if (pp.position > currentEnd) {
+        currentEnd = pp.position;
+      }
+      if (pp.position + pp.phraseOffset > currentRealEnd) {
+        currentRealEnd = pp.position + pp.phraseOffset;
+        currentEndOffset = pp.endOffset();
       }
       pq.add(pp);
     }
@@ -268,17 +325,21 @@
   }
 
   /** Fill the queue (all pps are already placed */
-  private void fillQueue() {
+  private void fillQueue() throws IOException {
     pq.clear();
     for (PhrasePositions pp=min,prev=null; prev!=max; pp=(prev=pp).next) {  // iterate cyclic list: done once handled max
-      if (pp.position > end) {
-        end = pp.position;
+      if (pp.position > currentEnd) {
+        currentEnd = pp.position;
+      }
+      if (pp.position + pp.phraseOffset > currentRealEnd) {
+        currentRealEnd = pp.position + pp.phraseOffset;
+        currentEndOffset = pp.endOffset();
       }
       pq.add(pp);
     }
   }
 
-  /** At initialization (each doc), each repetition group is sorted by (query) offset.
+  /** At initialization (each doc), each repetition group is sorted by (query) phraseOffset.
    * This provides the start condition: no collisions.
    * <p>Case 1: no multi-term repeats<br>
    * It is sufficient to advance each pp in the group by one less than its group index.
@@ -298,7 +359,7 @@
           int k;
           while((k=collide(pp)) >= 0) {
             PhrasePositions pp2 = lesser(pp, rg[k]);
-            if (!advancePP(pp2)) {  // at initialization always advance pp with higher offset
+            if (!advancePP(pp2)) {  // at initialization always advance pp with higher phraseOffset
               return false; // exhausted
             }
             if (pp2.rptInd < i) { // should not happen?
@@ -356,14 +417,14 @@
     return true; // PPs available
   }
 
-  /** sort each repetition group by (query) offset. 
+  /** sort each repetition group by (query) phraseOffset.
    * Done only once (at first doc) and allows to initialize faster for each doc. */
   private void sortRptGroups(ArrayList<ArrayList<PhrasePositions>> rgs) {
     rptGroups = new PhrasePositions[rgs.size()][];
     Comparator<PhrasePositions> cmprtr = new Comparator<PhrasePositions>() {
       @Override
       public int compare(PhrasePositions pp1, PhrasePositions pp2) {
-        return pp1.offset - pp2.offset;
+        return pp1.phraseOffset - pp2.phraseOffset;
       }
     };
     for (int i=0; i<rptGroups.length; i++) {
@@ -390,7 +451,7 @@
           PhrasePositions pp2 = rpp[j];
           if (
               pp2.rptGroup >=0        // already marked as a repetition
-              || pp2.offset == pp.offset // not a repetition: two PPs are originally in same offset in the query! 
+              || pp2.phraseOffset == pp.phraseOffset // not a repetition: two PPs are originally in same phraseOffset in the query!
               || tpPos(pp2) != tpPos) {  // not a repetition
             continue; 
           }
@@ -434,9 +495,9 @@
     return res;
   }
 
-  /** Actual position in doc of a PhrasePosition, relies on that position = tpPos - offset) */
-  private final int tpPos(PhrasePositions pp) {
-    return pp.position + pp.offset;
+  /** Actual position in doc of a PhrasePosition, relies on that position = tpPos - phraseOffset) */
+  private int tpPos(PhrasePositions pp) {
+    return pp.position + pp.phraseOffset;
   }
 
   /** find repeating terms and assign them ordinal values */
@@ -446,7 +507,7 @@
     for (PhrasePositions pp=min,prev=null; prev!=max; pp=(prev=pp).next) { // iterate cyclic list: done once handled max
       for (Term t : pp.terms) {
         Integer cnt0 = tcnt.get(t);
-        Integer cnt = cnt0==null ? new Integer(1) : new Integer(1+cnt0.intValue());
+        Integer cnt = cnt0==null ? new Integer(1) : new Integer(1 + cnt0);
         tcnt.put(t, cnt);
         if (cnt==2) {
           tord.put(t,tord.size());
@@ -468,7 +529,7 @@
         }
       }
     }
-    return rp.toArray(new PhrasePositions[0]);
+    return rp.toArray(new PhrasePositions[rp.size()]);
   }
   
   /** bit-sets - for each repeating pp, for each of its repeating terms, the term ordinal values is set */
@@ -508,7 +569,7 @@
   /** map each term to the single group that contains it */ 
   private HashMap<Term,Integer> termGroups(LinkedHashMap<Term,Integer> tord, ArrayList<FixedBitSet> bb) throws IOException {
     HashMap<Term,Integer> tg = new HashMap<>();
-    Term[] t = tord.keySet().toArray(new Term[0]);
+    Term[] t = tord.keySet().toArray(new Term[tord.size()]);
     for (int i=0; i<bb.size(); i++) { // i is the group no.
       FixedBitSet bits = bb.get(i);
       for (int ord = bits.nextSetBit(0); ord != DocIdSetIterator.NO_MORE_DOCS; ord = ord + 1 >= bits.length() ? DocIdSetIterator.NO_MORE_DOCS : bits.nextSetBit(ord + 1)) {
@@ -518,16 +579,60 @@
     return tg;
   }
 
-  @Override
-  public int freq() {
-    return numMatches;
-  }
-  
-  float sloppyFreq() {
+  /**
+   * Score a candidate doc for all slop-valid position-combinations (matches)
+   * encountered while traversing/hopping the PhrasePositions.
+   * <br> The score contribution of a match depends on the distance:
+   * <br> - highest score for distance=0 (exact match).
+   * <br> - score gets lower as distance gets higher.
+   * <br>Example: for query "a b"~2, a document "x a b a y" can be scored twice:
+   * once for "a b" (distance=0), and once for "b a" (distance=2).
+   * <br>Possibly not all valid combinations are encountered, because for efficiency
+   * we always propagate the least PhrasePosition. This allows to base on
+   * PriorityQueue and move forward faster.
+   * As result, for example, document "a b c b a"
+   * would score differently for queries "a b c"~4 and "c b a"~4, although
+   * they really are equivalent.
+   * Similarly, for doc "a b c b a f g", query "c b"~2
+   * would get same score as "g f"~2, although "c b"~2 could be matched twice.
+   * We may want to fix this in the future (currently not, for performance reasons).
+   */
+  float sloppyFreq() throws IOException {
+    if (sloppyFreq == -1) {
+      sloppyFreq = 0.0f;
+      while (nextPosition() != NO_MORE_POSITIONS) {
+        sloppyFreq += docScorer.computeSlopFactor(matchLength);
+      }
+    }
     return sloppyFreq;
   }
-  
-//  private void printQueue(PrintStream ps, PhrasePositions ext, String title) {
+
+  @Override
+  public int startOffset() throws IOException {
+    return startoffset;
+  }
+
+  @Override
+  public int endOffset() throws IOException {
+    return spanOffsetEnd;
+  }
+
+  @Override
+  public BytesRef getPayload() throws IOException {
+    return null;    // TODO : getPayload on intervals?
+  }
+
+  @Override
+  public int startPosition() throws IOException {
+    return startpos;
+  }
+
+  @Override
+  public int endPosition() throws IOException {
+    return spanEnd;
+  }
+
+  //  private void printQueue(PrintStream ps, PhrasePositions ext, String title) {
 //    //if (min.doc != ?) return;
 //    ps.println();
 //    ps.println("---- "+title);
@@ -569,8 +674,8 @@
   }
   
   @Override
-  public float score() {
-    return docScorer.score(max.doc, sloppyFreq);
+  public float score() throws IOException {
+    return docScorer.score(max.doc, sloppyFreq());
   }
 
   @Override
@@ -586,9 +691,9 @@
         }
       }
       // found a doc with all of the terms
-      sloppyFreq = phraseFreq(); // check for phrase
+      //sloppyFreq = phraseFreq(); // check for phrase
       target = min.doc + 1; // next target in case sloppyFreq is still 0
-    } while (sloppyFreq == 0f);
+    } while (firstPosition() == NO_MORE_POSITIONS);
 
     // found a match
     return max.doc;
@@ -601,4 +706,5 @@
 
   @Override
   public String toString() { return "scorer(" + weight + ")"; }
+
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/TermQuery.java b/lucene/core/src/java/org/apache/lucene/search/TermQuery.java
index 1bc2978..e4e46f5 100644
--- a/lucene/core/src/java/org/apache/lucene/search/TermQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/TermQuery.java
@@ -17,71 +17,75 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.util.Set;
-
-import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.DocsEnum;
-import org.apache.lucene.index.LeafReader;
 import org.apache.lucene.index.IndexReaderContext;
+import org.apache.lucene.index.LeafReader;
+import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.ReaderUtil;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.index.TermContext;
 import org.apache.lucene.index.TermState;
 import org.apache.lucene.index.TermsEnum;
-import org.apache.lucene.search.similarities.Similarity.SimScorer;
 import org.apache.lucene.search.similarities.Similarity;
+import org.apache.lucene.search.similarities.Similarity.SimScorer;
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.ToStringUtils;
 
-/** A Query that matches documents containing a term.
-  This may be combined with other terms with a {@link BooleanQuery}.
-  */
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * A Query that matches documents containing a term. This may be combined with
+ * other terms with a {@link BooleanQuery}.
+ */
 public class TermQuery extends Query {
   private final Term term;
   private final int docFreq;
   private final TermContext perReaderTermState;
-
+  
   final class TermWeight extends Weight {
     private final Similarity similarity;
     private final Similarity.SimWeight stats;
     private final TermContext termStates;
-
+    
     public TermWeight(IndexSearcher searcher, TermContext termStates)
-      throws IOException {
+        throws IOException {
       assert termStates != null : "TermContext must not be null";
       this.termStates = termStates;
       this.similarity = searcher.getSimilarity();
-      this.stats = similarity.computeWeight(
-          getBoost(), 
-          searcher.collectionStatistics(term.field()), 
+      this.stats = similarity.computeWeight(getBoost(),
+          searcher.collectionStatistics(term.field()),
           searcher.termStatistics(term, termStates));
     }
-
+    
     @Override
-    public String toString() { return "weight(" + TermQuery.this + ")"; }
-
+    public String toString() {
+      return "weight(" + TermQuery.this + ")";
+    }
+    
     @Override
-    public Query getQuery() { return TermQuery.this; }
-
+    public Query getQuery() {
+      return TermQuery.this;
+    }
+    
     @Override
     public float getValueForNormalization() {
       return stats.getValueForNormalization();
     }
-
+    
     @Override
     public void normalize(float queryNorm, float topLevelBoost) {
       stats.normalize(queryNorm, topLevelBoost);
     }
-
+    
     @Override
-    public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
+    public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
       assert termStates.topReaderContext == ReaderUtil.getTopLevelContext(context) : "The top-reader used to create Weight (" + termStates.topReaderContext + ") is not the same as the current reader's top-reader (" + ReaderUtil.getTopLevelContext(context);
       final TermsEnum termsEnum = getTermsEnum(context);
       if (termsEnum == null) {
         return null;
       }
-      DocsEnum docs = termsEnum.docs(acceptDocs, null);
+      DocsEnum docs = termsEnum.docs(acceptDocs, null, flags);
       assert docs != null;
       return new TermScorer(this, docs, similarity.simScorer(stats, context));
     }
@@ -96,90 +100,100 @@
         assert termNotInReader(context.reader(), term) : "no termstate found but term exists in reader term=" + term;
         return null;
       }
-      //System.out.println("LD=" + reader.getLiveDocs() + " set?=" + (reader.getLiveDocs() != null ? reader.getLiveDocs().get(0) : "null"));
-      final TermsEnum termsEnum = context.reader().terms(term.field()).iterator(null);
+      // System.out.println("LD=" + reader.getLiveDocs() + " set?=" +
+      // (reader.getLiveDocs() != null ? reader.getLiveDocs().get(0) : "null"));
+      final TermsEnum termsEnum = context.reader().terms(term.field())
+          .iterator(null);
       termsEnum.seekExact(term.bytes(), state);
       return termsEnum;
     }
     
     private boolean termNotInReader(LeafReader reader, Term term) throws IOException {
       // only called from assert
-      //System.out.println("TQ.termNotInReader reader=" + reader + " term=" + field + ":" + bytes.utf8ToString());
+      // System.out.println("TQ.termNotInReader reader=" + reader + " term=" +
+      // field + ":" + bytes.utf8ToString());
       return reader.docFreq(term) == 0;
     }
     
     @Override
     public Explanation explain(LeafReaderContext context, int doc) throws IOException {
-      Scorer scorer = scorer(context, context.reader().getLiveDocs());
+      Scorer scorer = scorer(context, DocsEnum.FLAG_FREQS, context.reader().getLiveDocs());
       if (scorer != null) {
         int newDoc = scorer.advance(doc);
         if (newDoc == doc) {
           float freq = scorer.freq();
           SimScorer docScorer = similarity.simScorer(stats, context);
           ComplexExplanation result = new ComplexExplanation();
-          result.setDescription("weight("+getQuery()+" in "+doc+") [" + similarity.getClass().getSimpleName() + "], result of:");
-          Explanation scoreExplanation = docScorer.explain(doc, new Explanation(freq, "termFreq=" + freq));
+          result.setDescription("weight(" + getQuery() + " in " + doc + ") ["
+              + similarity.getClass().getSimpleName() + "], result of:");
+          Explanation scoreExplanation = docScorer.explain(doc,
+              new Explanation(freq, "termFreq=" + freq));
           result.addDetail(scoreExplanation);
           result.setValue(scoreExplanation.getValue());
           result.setMatch(true);
           return result;
         }
       }
-      return new ComplexExplanation(false, 0.0f, "no matching term");      
+      return new ComplexExplanation(false, 0.0f, "no matching term");
     }
   }
-
+  
   /** Constructs a query for the term <code>t</code>. */
   public TermQuery(Term t) {
     this(t, -1);
   }
-
-  /** Expert: constructs a TermQuery that will use the
-   *  provided docFreq instead of looking up the docFreq
-   *  against the searcher. */
+  
+  /**
+   * Expert: constructs a TermQuery that will use the provided docFreq instead
+   * of looking up the docFreq against the searcher.
+   */
   public TermQuery(Term t, int docFreq) {
     term = t;
     this.docFreq = docFreq;
     perReaderTermState = null;
   }
   
-  /** Expert: constructs a TermQuery that will use the
-   *  provided docFreq instead of looking up the docFreq
-   *  against the searcher. */
+  /**
+   * Expert: constructs a TermQuery that will use the provided docFreq instead
+   * of looking up the docFreq against the searcher.
+   */
   public TermQuery(Term t, TermContext states) {
     assert states != null;
     term = t;
     docFreq = states.docFreq();
     perReaderTermState = states;
   }
-
+  
   /** Returns the term of this query. */
-  public Term getTerm() { return term; }
-
+  public Term getTerm() {
+    return term;
+  }
+  
   @Override
   public Weight createWeight(IndexSearcher searcher) throws IOException {
     final IndexReaderContext context = searcher.getTopReaderContext();
     final TermContext termState;
-    if (perReaderTermState == null || perReaderTermState.topReaderContext != context) {
-      // make TermQuery single-pass if we don't have a PRTS or if the context differs!
+    if (perReaderTermState == null
+        || perReaderTermState.topReaderContext != context) {
+      // make TermQuery single-pass if we don't have a PRTS or if the context
+      // differs!
       termState = TermContext.build(context, term);
     } else {
-     // PRTS was pre-build for this IS
-     termState = this.perReaderTermState;
+      // PRTS was pre-build for this IS
+      termState = this.perReaderTermState;
     }
-
+    
     // we must not ignore the given docFreq - if set use the given value (lie)
-    if (docFreq != -1)
-      termState.setDocFreq(docFreq);
+    if (docFreq != -1) termState.setDocFreq(docFreq);
     
     return new TermWeight(searcher, termState);
   }
-
+  
   @Override
   public void extractTerms(Set<Term> terms) {
     terms.add(getTerm());
   }
-
+  
   /** Prints a user-readable version of this query. */
   @Override
   public String toString(String field) {
@@ -192,21 +206,20 @@
     buffer.append(ToStringUtils.boost(getBoost()));
     return buffer.toString();
   }
-
+  
   /** Returns true iff <code>o</code> is equal to this. */
   @Override
   public boolean equals(Object o) {
-    if (!(o instanceof TermQuery))
-      return false;
-    TermQuery other = (TermQuery)o;
+    if (!(o instanceof TermQuery)) return false;
+    TermQuery other = (TermQuery) o;
     return (this.getBoost() == other.getBoost())
-      && this.term.equals(other.term);
+        && this.term.equals(other.term);
   }
-
-  /** Returns a hash code value for this object.*/
+  
+  /** Returns a hash code value for this object. */
   @Override
   public int hashCode() {
     return Float.floatToIntBits(getBoost()) ^ term.hashCode();
   }
-
+  
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/TermScorer.java b/lucene/core/src/java/org/apache/lucene/search/TermScorer.java
index 6697524..71f15ee 100644
--- a/lucene/core/src/java/org/apache/lucene/search/TermScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/TermScorer.java
@@ -17,10 +17,11 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.search.similarities.Similarity;
+import org.apache.lucene.util.BytesRef;
+
+import java.io.IOException;
 
 /** Expert: A <code>Scorer</code> for documents matching a <code>Term</code>.
  */
@@ -64,6 +65,36 @@
   public int nextDoc() throws IOException {
     return docsEnum.nextDoc();
   }
+
+  @Override
+  public int nextPosition() throws IOException {
+    return docsEnum.nextPosition();
+  }
+
+  @Override
+  public int startPosition() throws IOException {
+    return docsEnum.startPosition();
+  }
+
+  @Override
+  public int endPosition() throws IOException {
+    return docsEnum.endPosition();
+  }
+
+  @Override
+  public int startOffset() throws IOException {
+    return docsEnum.startOffset();
+  }
+
+  @Override
+  public int endOffset() throws IOException {
+    return docsEnum.endOffset();
+  }
+
+  @Override
+  public BytesRef getPayload() throws IOException {
+    return docsEnum.getPayload();
+  }
   
   @Override
   public float score() throws IOException {
@@ -92,5 +123,16 @@
 
   /** Returns a string representation of this <code>TermScorer</code>. */
   @Override
-  public String toString() { return "scorer(" + weight + ")"; }
+  public String toString() {
+    return "scorer(" + weight + ")[" + super.toString() + "]";
+  }
+  
+  // TODO: benchmark if the specialized conjunction really benefits
+  // from this, or if instead its from sorting by docFreq, or both
+
+  DocsEnum getDocsEnum() {
+    return docsEnum;
+  }
+
+
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/TimeLimitingCollector.java b/lucene/core/src/java/org/apache/lucene/search/TimeLimitingCollector.java
index e179089..9c72f2c 100644
--- a/lucene/core/src/java/org/apache/lucene/search/TimeLimitingCollector.java
+++ b/lucene/core/src/java/org/apache/lucene/search/TimeLimitingCollector.java
@@ -17,12 +17,12 @@
  * limitations under the License.
  */
 
+import java.io.IOException;
+
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.util.Counter;
 import org.apache.lucene.util.ThreadInterruptedException;
 
-import java.io.IOException;
-
 /**
  * The {@link TimeLimitingCollector} is used to timeout search requests that
  * take longer than the maximum allowed search time limit. After this time is
@@ -156,7 +156,7 @@
       
     };
   }
-  
+
   /**
    * This is so the same timer can be used with a multi-phase search process such as grouping. 
    * We don't want to create a new TimeLimitingCollector for each phase because that would 
diff --git a/lucene/core/src/java/org/apache/lucene/search/TotalHitCountCollector.java b/lucene/core/src/java/org/apache/lucene/search/TotalHitCountCollector.java
index 4fc5be6..72a527f 100644
--- a/lucene/core/src/java/org/apache/lucene/search/TotalHitCountCollector.java
+++ b/lucene/core/src/java/org/apache/lucene/search/TotalHitCountCollector.java
@@ -17,6 +17,7 @@
  * limitations under the License.
  */
 
+import org.apache.lucene.index.DocsEnum;
 
 /**
  * Just counts the total number of hits.
diff --git a/lucene/core/src/java/org/apache/lucene/search/Weight.java b/lucene/core/src/java/org/apache/lucene/search/Weight.java
index 422356c..b2d7320 100644
--- a/lucene/core/src/java/org/apache/lucene/search/Weight.java
+++ b/lucene/core/src/java/org/apache/lucene/search/Weight.java
@@ -17,13 +17,13 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-
+import org.apache.lucene.index.IndexReaderContext;
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.IndexReaderContext; // javadocs
 import org.apache.lucene.search.similarities.Similarity;
 import org.apache.lucene.util.Bits;
 
+import java.io.IOException;
+
 /**
  * Expert: Calculate query weights and build query scorers.
  * <p>
@@ -34,7 +34,7 @@
  * {@link org.apache.lucene.index.LeafReader} dependent state should reside in the {@link Scorer}.
  * <p>
  * Since {@link Weight} creates {@link Scorer} instances for a given
- * {@link org.apache.lucene.index.LeafReaderContext} ({@link #scorer(org.apache.lucene.index.LeafReaderContext, Bits)})
+ * {@link org.apache.lucene.index.LeafReaderContext} ({@link #scorer(org.apache.lucene.index.LeafReaderContext, int, Bits)})
  * callers must maintain the relationship between the searcher's top-level
  * {@link IndexReaderContext} and the context used to create a {@link Scorer}. 
  * <p>
@@ -49,7 +49,7 @@
  * <li>The query normalization factor is passed to {@link #normalize(float, float)}. At
  * this point the weighting is complete.
  * <li>A <code>Scorer</code> is constructed by
- * {@link #scorer(org.apache.lucene.index.LeafReaderContext, Bits)}.
+ * {@link #scorer(org.apache.lucene.index.LeafReaderContext, int, Bits)}.
  * </ol>
  * 
  * @since 2.9
@@ -96,7 +96,7 @@
    * @return a {@link Scorer} which scores documents in/out-of order.
    * @throws IOException if there is a low-level I/O error
    */
-  public abstract Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException;
+  public abstract Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException;
 
   /**
    * Optional method, to return a {@link BulkScorer} to
@@ -125,9 +125,9 @@
    * passes them to a collector.
    * @throws IOException if there is a low-level I/O error
    */
-  public BulkScorer bulkScorer(LeafReaderContext context, boolean scoreDocsInOrder, Bits acceptDocs) throws IOException {
+  public BulkScorer bulkScorer(LeafReaderContext context, boolean scoreDocsInOrder, int flags, Bits acceptDocs) throws IOException {
 
-    Scorer scorer = scorer(context, acceptDocs);
+    Scorer scorer = scorer(context, flags, acceptDocs);
     if (scorer == null) {
       // No docs match
       return null;
@@ -198,14 +198,13 @@
    * Returns true iff this implementation scores docs only out of order. This
    * method is used in conjunction with {@link Collector}'s
    * {@link LeafCollector#acceptsDocsOutOfOrder() acceptsDocsOutOfOrder} and
-   * {@link #bulkScorer(org.apache.lucene.index.LeafReaderContext, boolean, Bits)} to
+   * {@link #bulkScorer(org.apache.lucene.index.LeafReaderContext, boolean, int, Bits)} to
    * create a matching {@link Scorer} instance for a given {@link Collector}, or
    * vice versa.
    * <p>
    * <b>NOTE:</b> the default implementation returns <code>false</code>, i.e.
    * the <code>Scorer</code> scores documents in-order.
    */
-  public boolean scoresDocsOutOfOrder() {
-    return false;
-  }
+  public boolean scoresDocsOutOfOrder() { return false; }
+
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/package.html b/lucene/core/src/java/org/apache/lucene/search/package.html
index 68595b7..3c1a161 100644
--- a/lucene/core/src/java/org/apache/lucene/search/package.html
+++ b/lucene/core/src/java/org/apache/lucene/search/package.html
@@ -436,15 +436,15 @@
                 that scores via a {@link org.apache.lucene.search.similarities.Similarity Similarity} will just defer to the Similarity's implementation:
                 {@link org.apache.lucene.search.similarities.Similarity.SimWeight#normalize SimWeight#normalize(float,float)}.</li>
             <li>
-                {@link org.apache.lucene.search.Weight#scorer(org.apache.lucene.index.LeafReaderContext, org.apache.lucene.util.Bits)
-                  scorer(LeafReaderContext context, Bits acceptDocs)} &mdash;
+                {@link org.apache.lucene.search.Weight#scorer(org.apache.lucene.index.LeafReaderContext, int, org.apache.lucene.util.Bits)
+                  scorer(LeafReaderContext context, int flags, Bits acceptDocs)} &mdash;
                 Construct a new {@link org.apache.lucene.search.Scorer Scorer} for this Weight. See <a href="#scorerClass">The Scorer Class</a>
                 below for help defining a Scorer. As the name implies, the Scorer is responsible for doing the actual scoring of documents 
                 given the Query.
             </li>
             <li>
-                {@link org.apache.lucene.search.Weight#bulkScorer(org.apache.lucene.index.LeafReaderContext, boolean, org.apache.lucene.util.Bits)
-                  scorer(LeafReaderContext context, boolean scoreDocsInOrder, Bits acceptDocs)} &mdash;
+                {@link org.apache.lucene.search.Weight#bulkScorer(org.apache.lucene.index.LeafReaderContext, boolean, int, org.apache.lucene.util.Bits)
+                  scorer(LeafReaderContext context, boolean scoreDocsInOrder, int flags, Bits acceptDocs)} &mdash;
                 Construct a new {@link org.apache.lucene.search.BulkScorer BulkScorer} for this Weight. See <a href="#bulkScorerClass">The BulkScorer Class</a>
                 below for help defining a BulkScorer. This is an optional method, and most queries do not implement it.
             </li>
diff --git a/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java b/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java
index 1be23e6..3f54a54 100644
--- a/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java
@@ -17,11 +17,12 @@
  * limitations under the License.
  */
 
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.search.ComplexExplanation;
 import org.apache.lucene.search.Explanation;
-import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.Weight;
 import org.apache.lucene.search.similarities.DefaultSimilarity;
 import org.apache.lucene.search.similarities.Similarity;
@@ -148,14 +149,14 @@
     }
 
     @Override
-    public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
+    public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
       return new PayloadNearSpanScorer(query.getSpans(context, acceptDocs, termContexts), this,
           similarity, similarity.simScorer(stats, context));
     }
     
     @Override
     public Explanation explain(LeafReaderContext context, int doc) throws IOException {
-      PayloadNearSpanScorer scorer = (PayloadNearSpanScorer) scorer(context, context.reader().getLiveDocs());
+      PayloadNearSpanScorer scorer = (PayloadNearSpanScorer) scorer(context, DocsEnum.FLAG_PAYLOADS, context.reader().getLiveDocs());
       if (scorer != null) {
         int newDoc = scorer.advance(doc);
         if (newDoc == doc) {
diff --git a/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadTermQuery.java b/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadTermQuery.java
index 6afbdf2..8d3168f 100644
--- a/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadTermQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadTermQuery.java
@@ -19,7 +19,7 @@
 
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.Term;
-import org.apache.lucene.index.DocsAndPositionsEnum;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.Weight;
@@ -79,7 +79,7 @@
     }
 
     @Override
-    public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
+    public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
       return new PayloadTermSpanScorer((TermSpans) query.getSpans(context, acceptDocs, termContexts),
           this, similarity.simScorer(stats, context));
     }
@@ -120,7 +120,7 @@
 
       protected void processPayload(Similarity similarity) throws IOException {
         if (termSpans.isPayloadAvailable()) {
-          final DocsAndPositionsEnum postings = termSpans.getPostings();
+          final DocsEnum postings = termSpans.getPostings();
           payload = postings.getPayload();
           if (payload != null) {
             payloadScore = function.currentScore(doc, term.field(),
@@ -176,7 +176,7 @@
     
     @Override
     public Explanation explain(LeafReaderContext context, int doc) throws IOException {
-      PayloadTermSpanScorer scorer = (PayloadTermSpanScorer) scorer(context, context.reader().getLiveDocs());
+      PayloadTermSpanScorer scorer = (PayloadTermSpanScorer) scorer(context, DocsEnum.FLAG_POSITIONS, context.reader().getLiveDocs());
       if (scorer != null) {
         int newDoc = scorer.advance(doc);
         if (newDoc == doc) {
diff --git a/lucene/core/src/java/org/apache/lucene/search/posfilter/BlockPhraseScorer.java b/lucene/core/src/java/org/apache/lucene/search/posfilter/BlockPhraseScorer.java
new file mode 100644
index 0000000..8a48e55
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/search/posfilter/BlockPhraseScorer.java
@@ -0,0 +1,70 @@
+package org.apache.lucene.search.posfilter;
+
+import java.io.IOException;
+
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.similarities.Similarity;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class BlockPhraseScorer extends PositionFilteredScorer {
+
+  private final Interval[] subIntervals;
+
+  public BlockPhraseScorer(Scorer filteredScorer, Similarity.SimScorer simScorer) {
+    super(filteredScorer, simScorer);
+    subIntervals = new Interval[subScorers.length];
+    for (int i = 0; i < subScorers.length; i++) {
+      subIntervals[i] = new Interval();
+    }
+  }
+
+  @Override
+  public void reset(int doc) throws IOException {
+    super.reset(doc);
+    for (int i = 0; i < subScorers.length; i++) {
+      subIntervals[i].reset();
+    }
+  }
+
+  @Override
+  protected int doNextPosition() throws IOException {
+    if (subScorers[0].nextPosition() == NO_MORE_POSITIONS)
+      return NO_MORE_POSITIONS;
+    subIntervals[0].update(subScorers[0]);
+    int i = 1;
+    while (i < subScorers.length) {
+      while (subIntervals[i].begin <= subIntervals[i - 1].end) {
+        if (subScorers[i].nextPosition() == NO_MORE_POSITIONS)
+          return NO_MORE_POSITIONS;
+        subIntervals[i].update(subScorers[i]);
+      }
+      if (subIntervals[i].begin == subIntervals[i - 1].end + 1) {
+        i++;
+      }
+      else {
+        if (subScorers[0].nextPosition() == NO_MORE_POSITIONS)
+          return NO_MORE_POSITIONS;
+        subIntervals[0].update(subScorers[0]);
+        i = 1;
+      }
+    }
+    current.update(subIntervals[0], subIntervals[subScorers.length - 1]);
+    return subScorers[0].startPosition();
+  }
+}
diff --git a/lucene/core/src/java/org/apache/lucene/search/posfilter/Interval.java b/lucene/core/src/java/org/apache/lucene/search/posfilter/Interval.java
new file mode 100644
index 0000000..7bfe563
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/search/posfilter/Interval.java
@@ -0,0 +1,166 @@
+package org.apache.lucene.search.posfilter;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.index.DocsEnum;
+
+/**
+ * Represents a section of a document that matches a query
+ */
+class Interval implements Cloneable {
+
+  /** The position of the start of this Interval */
+  public int begin;
+
+  /** The position of the end of this Interval */
+  public int end;
+
+  /** The offset of the start of this Interval */
+  public int offsetBegin;
+
+  /** The offset of the end of this Interval */
+  public int offsetEnd;
+
+  /** An interval that will always compare as less than any other interval */
+  public static final Interval INFINITE_INTERVAL = new Interval();
+
+   /**
+   * Constructs a new Interval
+   * @param begin the start position
+   * @param end the end position
+   * @param offsetBegin the start offset
+   * @param offsetEnd the end offset
+   */
+  public Interval(int begin, int end, int offsetBegin, int offsetEnd) {
+    this.begin = begin;
+    this.end = end;
+    this.offsetBegin = offsetBegin;
+    this.offsetEnd = offsetEnd;
+  }
+
+  /**
+   * Constructs a new Interval with no initial values.  This
+   * will always compare as less than any other Interval.
+   */
+  public Interval() {
+    this(Integer.MIN_VALUE, Integer.MIN_VALUE, -1, -1);
+  }
+
+  /**
+   * Update to span the range defined by two other Intervals.
+   * @param start the first Interval
+   * @param end the second Interval
+   */
+  public void update(Interval start, Interval end) {
+    this.begin = start.begin;
+    this.offsetBegin = start.offsetBegin;
+    this.end = end.end;
+    this.offsetEnd = end.offsetEnd;
+  }
+
+  public void update(DocsEnum start, Interval end) throws IOException {
+    this.begin = start.startPosition();
+    this.offsetBegin = start.startOffset();
+    this.end = end.end;
+    this.offsetEnd = end.offsetEnd;
+  }
+
+  public void update(Interval start, DocsEnum end) throws IOException {
+    this.begin = start.begin;
+    this.offsetBegin = start.offsetBegin;
+    this.end = end.endPosition();
+    this.offsetEnd = end.endOffset();
+  }
+
+  /**
+   * Compare with another Interval.
+   * @param other the comparator
+   * @return true if both start and end positions are less than
+   *              the comparator.
+   */
+  public boolean lessThanExclusive(Interval other) {
+    return begin < other.begin && end < other.end;
+  }
+
+  /**
+   * Compare with another Interval
+   * @param other the comparator
+   * @return true if this Interval contains the comparator
+   */
+  public boolean contains(Interval other) {
+    return begin <= other.begin && other.end <= end;
+  }
+
+  /**
+   * Compare with another Interval to find overlaps
+   * @param other the comparator
+   * @return true if the two intervals overlap
+   */
+  public boolean overlaps(Interval other) {
+    return this.contains(other) || other.contains(this);
+  }
+
+  /**
+   * Set to a state that will always compare as less than any
+   * other Interval.
+   */
+  public void reset() {
+    offsetBegin = offsetEnd = -1;
+    begin = end = Integer.MIN_VALUE;
+  }
+
+  /**
+   * Set to a state that will always compare as more than any
+   * other Interval.
+   */
+  public void setMaximum() {
+    offsetBegin = offsetEnd = -1;
+    begin = end = DocsEnum.NO_MORE_POSITIONS;
+  }
+  
+  @Override
+  public Object clone() {
+    try {
+      return super.clone();
+    } catch (CloneNotSupportedException e) {
+      throw new RuntimeException(); // should not happen
+    }
+  }
+  
+  @Override
+  public String toString() {
+    return "Interval [begin=" + begin + "(" + offsetBegin + "), end="
+        + end + "(" + offsetEnd + ")]";
+  }
+
+  public void update(DocsEnum docsEnum) throws IOException {
+    offsetBegin = docsEnum.startOffset();
+    offsetEnd = docsEnum.endOffset();
+    begin = docsEnum.startPosition();
+    end = docsEnum.endPosition();
+  }
+
+  public void update(Interval interval) {
+    this.begin = interval.begin;
+    this.end = interval.end;
+    this.offsetBegin = interval.offsetBegin;
+    this.offsetEnd = interval.offsetEnd;
+  }
+
+}
\ No newline at end of file
diff --git a/lucene/core/src/java/org/apache/lucene/search/posfilter/NonOverlappingQuery.java b/lucene/core/src/java/org/apache/lucene/search/posfilter/NonOverlappingQuery.java
new file mode 100644
index 0000000..5fc85bb
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/search/posfilter/NonOverlappingQuery.java
@@ -0,0 +1,211 @@
+package org.apache.lucene.search.posfilter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.util.Set;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Weight;
+import org.apache.lucene.search.similarities.Similarity;
+import org.apache.lucene.util.Bits;
+
+/**
+ * A Query that matches documents containing an interval (the minuend) that
+ * does not contain another interval (the subtrahend).
+ *
+ * As an example, given the following {@link org.apache.lucene.search.BooleanQuery}:
+ * <pre>
+ *   BooleanQuery bq = new BooleanQuery();
+ *   bq.add(new TermQuery(new Term(field, "quick")), BooleanQuery.Occur.MUST);
+ *   bq.add(new TermQuery(new Term(field, "fox")), BooleanQuery.Occur.MUST);
+ * </pre>
+ *
+ * The document "the quick brown fox" will be matched by this query.  But
+ * create a NonOverlappingQuery using this query as a minuend:
+ * <pre>
+ *   NonOverlappingQuery brq = new NonOverlappingQuery(bq, new TermQuery(new Term(field, "brown")));
+ * </pre>
+ *
+ * This query will not match "the quick brown fox", because "brown" is found
+ * within the interval of the boolean query for "quick" and "fox.  The query
+ * will match "the quick fox is brown", because here "brown" is outside
+ * the minuend's interval.
+ *
+ * N.B. Positions must be included in the index for this query to work
+ *
+ * Implements the Brouwerian operator as defined in <a href=
+ * "http://vigna.dsi.unimi.it/ftp/papers/EfficientAlgorithmsMinimalIntervalSemantics"
+ * >"Efficient Optimally Lazy Algorithms for Minimal-Interval Semantics"</a>
+ *
+ * @lucene.experimental
+ */
+public final class NonOverlappingQuery extends PositionFilterQuery {
+
+  private Query subtrahend;
+
+  /**
+   * Constructs a Query that matches documents containing intervals of the minuend
+   * that are not subtended by the subtrahend
+   * @param minuend the minuend Query
+   * @param subtrahend the subtrahend Query
+   */
+  public NonOverlappingQuery(Query minuend, Query subtrahend) {
+    super(minuend, new BrouwerianScorerFactory(subtrahend));
+    this.subtrahend = subtrahend;
+  }
+
+  @Override
+  public void extractTerms(Set<Term> terms) {
+    super.extractTerms(terms);
+    subtrahend.extractTerms(terms);
+  }
+
+  @Override
+  public Query rewrite(IndexReader reader) throws IOException {
+    Query rewrittenMinuend = innerQuery.rewrite(reader);
+    Query rewrittenSubtrahend = subtrahend.rewrite(reader);
+    if (rewrittenMinuend != innerQuery || rewrittenSubtrahend != subtrahend) {
+      return new NonOverlappingQuery(rewrittenMinuend, rewrittenSubtrahend);
+    }
+    return this;
+  }
+
+  private static class BrouwerianScorerFactory implements ScorerFilterFactory {
+
+    private final Query subtrahend;
+
+    BrouwerianScorerFactory(Query subtrahend) {
+      this.subtrahend = subtrahend;
+    }
+
+    @Override
+    public Scorer scorer(Scorer filteredScorer, Similarity.SimScorer simScorer) {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getName() {
+      return "NonOverlapping[" + subtrahend.toString() + "]/";
+    }
+  }
+
+  @Override
+  public Weight createWeight(IndexSearcher searcher) throws IOException {
+    return new BrouwerianWeight(innerQuery.createWeight(searcher),
+                                subtrahend.createWeight(searcher), searcher);
+  }
+
+  class BrouwerianWeight extends ScorerFilterWeight {
+
+    private final Weight subtrahendWeight;
+
+    public BrouwerianWeight(Weight minuendWeight, Weight subtrahendWeight, IndexSearcher searcher)
+        throws IOException {
+      super(minuendWeight, searcher);
+      this.subtrahendWeight = subtrahendWeight;
+    }
+
+    @Override
+    public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
+      return new BrouwerianScorer(innerWeight.scorer(context, flags, acceptDocs),
+                                  subtrahendWeight.scorer(context, flags, acceptDocs),
+                                  similarity.simScorer(stats, context));
+    }
+  }
+
+  static class BrouwerianScorer extends PositionFilteredScorer {
+
+    private static final int UNPOSITIONED = -2;
+
+    private final Scorer subtrahend;
+    private Interval subtInterval = new Interval();
+    private int subtPosition = -1;
+
+    BrouwerianScorer(Scorer minuend, Scorer subtrahend, Similarity.SimScorer simScorer) {
+      super(minuend, simScorer);
+      this.subtrahend = subtrahend;
+    }
+
+    @Override
+    protected void reset(int doc) throws IOException {
+      super.reset(doc);
+      if (this.subtrahend == null || this.subtrahend.docID() == NO_MORE_DOCS || this.subtrahend.advance(doc) != doc)
+        subtPosition = NO_MORE_POSITIONS;
+      else
+        subtPosition = UNPOSITIONED;
+      this.subtInterval.reset();
+    }
+
+    @Override
+    public float intervalScore() throws IOException {
+      return child.intervalScore();
+    }
+
+    @Override
+    protected int doNextPosition() throws IOException {
+      if (subtPosition == NO_MORE_POSITIONS) {
+        int pos = child.nextPosition();
+        if (pos != NO_MORE_POSITIONS)
+          current.update(child);
+        return pos;
+      }
+      while (child.nextPosition() != NO_MORE_POSITIONS) {
+        current.update(child);
+        while (subtInterval.lessThanExclusive(current) &&
+                  (subtPosition = subtrahend.nextPosition()) != NO_MORE_POSITIONS) {
+          subtInterval.update(subtrahend);
+        }
+        if (subtPosition == NO_MORE_POSITIONS || !current.overlaps(subtInterval))
+          return current.begin;
+      }
+      return NO_MORE_POSITIONS;
+    }
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = super.hashCode();
+    result = prime * result + ((innerQuery == null) ? 0 : innerQuery.hashCode());
+    result = prime * result
+        + ((subtrahend == null) ? 0 : subtrahend.hashCode());
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) return true;
+    if (!super.equals(obj)) return false;
+    if (getClass() != obj.getClass()) return false;
+    NonOverlappingQuery other = (NonOverlappingQuery) obj;
+    if (innerQuery == null) {
+      if (other.innerQuery != null) return false;
+    } else if (!innerQuery.equals(other.innerQuery)) return false;
+    if (subtrahend == null) {
+      if (other.subtrahend != null) return false;
+    } else if (!subtrahend.equals(other.subtrahend)) return false;
+    return true;
+  }
+
+}
\ No newline at end of file
diff --git a/lucene/core/src/java/org/apache/lucene/search/posfilter/OrderedNearQuery.java b/lucene/core/src/java/org/apache/lucene/search/posfilter/OrderedNearQuery.java
new file mode 100644
index 0000000..692db55
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/search/posfilter/OrderedNearQuery.java
@@ -0,0 +1,139 @@
+package org.apache.lucene.search.posfilter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.similarities.Similarity;
+
+/**
+ * A query that matches if a set of subqueries also match, and are within
+ * a given distance of each other within the document.  The subqueries
+ * must appear in the document in order.
+ *
+ * N.B. Positions must be included in the index for this query to work
+ *
+ * Implements the AND&lt; operator as defined in <a href=
+ * "http://vigna.dsi.unimi.it/ftp/papers/EfficientAlgorithmsMinimalIntervalSemantics"
+ * >"Efficient Optimally Lazy Algorithms for Minimal-Interval Semantics"</a>
+ *
+ * @lucene.experimental
+ */
+
+public class OrderedNearQuery extends PositionFilterQuery {
+
+  /**
+   * Constructs an OrderedNearQuery
+   * @param slop the maximum distance between the subquery matches
+   * @param subqueries the subqueries to match.
+   */
+  public OrderedNearQuery(int slop, Query... subqueries) {
+    super(buildBooleanQuery(subqueries), new OrderedNearScorerFactory(slop));
+  }
+
+  private static class OrderedNearScorerFactory implements ScorerFilterFactory {
+
+    private final int slop;
+
+    public OrderedNearScorerFactory(int slop) {
+      this.slop = slop;
+    }
+
+    @Override
+    public Scorer scorer(Scorer filteredScorer, Similarity.SimScorer simScorer) {
+      return new WithinFilteredScorer(new OrderedNearScorer(filteredScorer, simScorer), slop, simScorer);
+    }
+
+    @Override
+    public String getName() {
+      return "OrderedNear/" + slop;
+    }
+  }
+
+  private static class OrderedNearScorer extends PositionFilteredScorer {
+
+    private final int lastiter;
+
+    private int index = 1;
+    private Interval[] intervals;
+
+    public OrderedNearScorer(Scorer filteredScorer, Similarity.SimScorer simScorer) {
+      super(filteredScorer, simScorer);
+      intervals = new Interval[subScorers.length];
+      for (int i = 0; i < subScorers.length; i++) {
+        intervals[i] = new Interval();
+      }
+      lastiter = intervals.length - 1;
+    }
+
+    @Override
+    public int freq() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected void reset(int doc) throws IOException {
+      for (int i = 0; i < subScorers.length; i++) {
+        assert subScorers[i].docID() == doc;
+        intervals[i].update(Interval.INFINITE_INTERVAL);
+      }
+      if (subScorers[0].nextPosition() == NO_MORE_POSITIONS)
+        intervals[0].setMaximum();
+      else
+        intervals[0].update(subScorers[0]);
+      index = 1;
+    }
+
+    @Override
+    protected int doNextPosition() throws IOException {
+      if (intervals[0].begin == NO_MORE_POSITIONS)
+        return NO_MORE_POSITIONS;
+      current.setMaximum();
+      int b = Integer.MAX_VALUE;
+      while (true) {
+        while (true) {
+          final Interval previous = intervals[index - 1];
+          if (previous.end >= b) {
+            return current.begin;
+          }
+          if (index == intervals.length || intervals[index].begin > previous.end)
+            break;
+          Interval scratch = intervals[index];
+          do {
+            if (scratch.end >= b || subScorers[index].nextPosition() == NO_MORE_POSITIONS)
+              return current.begin;
+            intervals[index].update(subScorers[index]);
+            scratch = intervals[index];
+          } while (scratch.begin <= previous.end);
+          index++;
+        }
+        current.update(intervals[0], intervals[lastiter]);
+        matchDistance = (intervals[lastiter].begin - lastiter) - intervals[0].end;
+        b = intervals[lastiter].begin;
+        index = 1;
+        if (subScorers[0].nextPosition() == NO_MORE_POSITIONS) {
+          intervals[0].setMaximum();
+          return current.begin;
+        }
+        intervals[0].update(subScorers[0]);
+      }
+    }
+  }
+}
diff --git a/lucene/core/src/java/org/apache/lucene/search/posfilter/PositionFilterQuery.java b/lucene/core/src/java/org/apache/lucene/search/posfilter/PositionFilterQuery.java
new file mode 100644
index 0000000..dee272e
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/search/posfilter/PositionFilterQuery.java
@@ -0,0 +1,166 @@
+package org.apache.lucene.search.posfilter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermContext;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.ComplexExplanation;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.TermStatistics;
+import org.apache.lucene.search.Weight;
+import org.apache.lucene.search.similarities.Similarity;
+import org.apache.lucene.util.Bits;
+
+public class PositionFilterQuery extends Query {
+
+  protected final Query innerQuery;
+  protected final ScorerFilterFactory scorerFilterFactory;
+
+  public PositionFilterQuery(Query innerQuery, ScorerFilterFactory scorerFilterFactory) {
+    this.innerQuery = innerQuery;
+    this.scorerFilterFactory = scorerFilterFactory;
+  }
+
+  protected static BooleanQuery buildBooleanQuery(Query... queries) {
+    BooleanQuery bq = new BooleanQuery();
+    for (Query q : queries) {
+      bq.add(q, BooleanClause.Occur.MUST);
+    }
+    return bq;
+  }
+
+  @Override
+  public void extractTerms(Set<Term> terms) {
+    innerQuery.extractTerms(terms);
+  }
+
+  @Override
+  public Query rewrite(IndexReader reader) throws IOException {
+    Query rewritten =  innerQuery.rewrite(reader);
+    if (rewritten != innerQuery) {
+      return new PositionFilterQuery(rewritten, scorerFilterFactory);
+    }
+    return this;
+  }
+
+  @Override
+  public Weight createWeight(IndexSearcher searcher) throws IOException {
+    return new ScorerFilterWeight(innerQuery.createWeight(searcher), searcher);
+  }
+
+  @Override
+  public String toString(String field) {
+    return scorerFilterFactory.getName() + "[" + innerQuery.toString() + "]";
+  }
+
+  public class ScorerFilterWeight extends Weight {
+
+    protected final Weight innerWeight;
+    protected final Similarity similarity;
+    protected final Similarity.SimWeight stats;
+
+    public ScorerFilterWeight(Weight innerWeight, IndexSearcher searcher) throws IOException {
+      this.innerWeight = innerWeight;
+      this.similarity = searcher.getSimilarity();
+      this.stats = getSimWeight(innerWeight.getQuery(), searcher);
+    }
+
+    private Similarity.SimWeight getSimWeight(Query query, IndexSearcher searcher)  throws IOException {
+      TreeSet<Term> terms = new TreeSet<Term>();
+      query.extractTerms(terms);
+      if (terms.size() == 0)
+        return null;
+      int i = 0;
+      TermStatistics[] termStats = new TermStatistics[terms.size()];
+      for (Term term : terms) {
+        TermContext state = TermContext.build(searcher.getTopReaderContext(), term);
+        termStats[i] = searcher.termStatistics(term, state);
+        i++;
+      }
+      // In most cases subqueries will all be on the same field, so use term statistics from
+      // the first term in the collected set.  In the cases where something is funky is happening
+      // with cross-field positional queries, phrase-type scoring isn't going to be useful anyway.
+      final String field = terms.first().field();
+      return similarity.computeWeight(query.getBoost(), searcher.collectionStatistics(field), termStats);
+    }
+
+    @Override
+    public Explanation explain(LeafReaderContext context, int doc) throws IOException {
+      Scorer scorer = scorer(context, DocsEnum.FLAG_POSITIONS, context.reader().getLiveDocs());
+      if (scorer != null) {
+        int newDoc = scorer.advance(doc);
+        if (newDoc == doc) {
+          float freq = ((PositionFilteredScorer)scorer).intervalFreq();
+          Similarity.SimScorer docScorer = similarity.simScorer(stats, context);
+          ComplexExplanation result = new ComplexExplanation();
+          result.setDescription("weight("+getQuery()+" in "+doc+") [" + similarity.getClass().getSimpleName() + "], result of:");
+          Explanation scoreExplanation = docScorer.explain(doc, new Explanation(freq, "phraseFreq=" + freq));
+          result.addDetail(scoreExplanation);
+          result.setValue(scoreExplanation.getValue());
+          result.setMatch(true);
+          return result;
+        }
+      }
+      return new ComplexExplanation(false, 0.0f,
+          "No matching term within position filter");
+    }
+
+    @Override
+    public Query getQuery() {
+      return PositionFilterQuery.this;
+    }
+
+    @Override
+    public float getValueForNormalization() throws IOException {
+      return stats == null ? 1.0f : stats.getValueForNormalization();
+    }
+
+    @Override
+    public void normalize(float norm, float topLevelBoost) {
+      if (stats != null)
+        stats.normalize(norm, topLevelBoost);
+    }
+
+    @Override
+    public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
+      Scorer filteredScorer = innerWeight.scorer(context, flags | DocsEnum.FLAG_POSITIONS, acceptDocs);
+      return filteredScorer == null ? null
+                : scorerFilterFactory.scorer(filteredScorer, similarity.simScorer(stats, context));
+    }
+  }
+
+  public static interface ScorerFilterFactory {
+
+    public Scorer scorer(Scorer filteredScorer, Similarity.SimScorer simScorer);
+
+    public String getName();
+
+  }
+}
diff --git a/lucene/core/src/java/org/apache/lucene/search/posfilter/PositionFilteredScorer.java b/lucene/core/src/java/org/apache/lucene/search/posfilter/PositionFilteredScorer.java
new file mode 100644
index 0000000..42469fb
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/search/posfilter/PositionFilteredScorer.java
@@ -0,0 +1,151 @@
+package org.apache.lucene.search.posfilter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.similarities.Similarity;
+import org.apache.lucene.util.BytesRef;
+
+public abstract class PositionFilteredScorer extends Scorer {
+
+  protected final Scorer[] subScorers;
+  protected final Scorer child;
+  protected final Interval current = new Interval();
+  protected final Similarity.SimScorer simScorer;
+  protected int matchDistance;
+
+  private boolean buffered;
+
+  public PositionFilteredScorer(Scorer filteredScorer, Similarity.SimScorer simScorer) {
+    super(filteredScorer.getWeight());
+    this.simScorer = simScorer;
+    child = filteredScorer;
+    subScorers = new Scorer[filteredScorer.getChildren().size()];
+    int i = 0;
+    for (ChildScorer subScorer : filteredScorer.getChildren()) {
+      subScorers[i++] = subScorer.child;
+    }
+  }
+
+  @Override
+  public float score() throws IOException {
+    return this.simScorer.score(docID(), intervalFreq());
+  }
+
+  private float freq = -1;
+
+  protected final float intervalFreq() throws IOException {
+    if (freq != -1)
+      return freq;
+    freq = 0;
+    while (nextPosition() != NO_MORE_POSITIONS)
+      freq += intervalScore();
+    return freq;
+  }
+
+  @Override
+  public int docID() {
+    return child.docID();
+  }
+
+  @Override
+  public int freq() throws IOException {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int nextDoc() throws IOException {
+    while (child.nextDoc() != NO_MORE_DOCS) {
+      reset(child.docID());
+      if (nextPosition() != NO_MORE_POSITIONS) {
+        buffered = true;
+        return child.docID();
+      }
+    }
+    return NO_MORE_DOCS;
+  }
+
+  @Override
+  public int advance(int target) throws IOException {
+    if (child.advance(target) == NO_MORE_DOCS)
+      return NO_MORE_DOCS;
+    do {
+      reset(child.docID());
+      if (nextPosition() != NO_MORE_POSITIONS) {
+        buffered = true;
+        return child.docID();
+      }
+    } while (child.nextDoc() != NO_MORE_DOCS);
+    return NO_MORE_DOCS;
+  }
+
+  @Override
+  public int nextPosition() throws IOException {
+    if (buffered) {
+      //System.out.println(this.hashCode() + ": returning buffered nextPos");
+      buffered = false;
+      return current.begin;
+    }
+    //System.out.println(this.hashCode() + ": returning unbuffered nextPos");
+    return doNextPosition();
+  }
+
+  protected abstract int doNextPosition() throws IOException;
+
+  protected void reset(int doc) throws IOException {
+    buffered = false;
+    freq = -1;
+  };
+
+  public int getMatchDistance() {
+    return matchDistance;
+  }
+
+  @Override
+  public int startPosition() throws IOException {
+    return current.begin;
+  }
+
+  @Override
+  public int endPosition() throws IOException {
+    return current.end;
+  }
+
+  @Override
+  public int startOffset() throws IOException {
+    return current.offsetBegin;
+  }
+
+  @Override
+  public int endOffset() throws IOException {
+    return current.offsetEnd;
+  }
+
+  @Override
+  public BytesRef getPayload() throws IOException {
+    return null; // TODO - payloads on intervals
+  }
+
+  @Override
+  public long cost() {
+    return child.cost();
+  }
+
+}
diff --git a/lucene/core/src/java/org/apache/lucene/search/posfilter/RangeFilterQuery.java b/lucene/core/src/java/org/apache/lucene/search/posfilter/RangeFilterQuery.java
new file mode 100644
index 0000000..261828e
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/search/posfilter/RangeFilterQuery.java
@@ -0,0 +1,88 @@
+package org.apache.lucene.search.posfilter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.similarities.Similarity;
+
+public class RangeFilterQuery extends PositionFilterQuery {
+
+  public RangeFilterQuery(int start, int end, Query innerQuery) {
+    super(innerQuery, new RangeFilterScorerFactory(start, end));
+  }
+
+  public RangeFilterQuery(int end, Query innerQuery) {
+    this(0, end, innerQuery);
+  }
+
+  private static class RangeFilterScorerFactory implements ScorerFilterFactory {
+
+    private final int start;
+    private final int end;
+
+    public RangeFilterScorerFactory(int start, int end) {
+      this.start = start;
+      this.end = end;
+    }
+
+    @Override
+    public Scorer scorer(Scorer filteredScorer, Similarity.SimScorer simScorer) {
+      return new RangeFilterScorer(start, end, filteredScorer, simScorer);
+    }
+
+    @Override
+    public String getName() {
+      return "RangeFilter(" + start + "," + end + ")";
+    }
+  }
+
+  private static class RangeFilterScorer extends PositionFilteredScorer {
+
+    private final int start;
+    private final int end;
+
+    public RangeFilterScorer(int start, int end, Scorer filteredScorer, Similarity.SimScorer simScorer) {
+      super(filteredScorer, simScorer);
+      this.start = start;
+      this.end = end;
+    }
+
+    @Override
+    public float intervalScore() throws IOException {
+      return child.intervalScore();
+    }
+
+    @Override
+    protected int doNextPosition() throws IOException {
+      int position;
+      while ((position = child.nextPosition()) != NO_MORE_POSITIONS) {
+        if (position > end)
+          return NO_MORE_POSITIONS;
+        if (position >= start) {
+          current.update(child);
+          return position;
+        }
+      }
+      return NO_MORE_POSITIONS;
+    }
+  }
+
+}
diff --git a/lucene/core/src/java/org/apache/lucene/search/posfilter/UnorderedNearQuery.java b/lucene/core/src/java/org/apache/lucene/search/posfilter/UnorderedNearQuery.java
new file mode 100644
index 0000000..c132dc0
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/search/posfilter/UnorderedNearQuery.java
@@ -0,0 +1,186 @@
+package org.apache.lucene.search.posfilter;
+
+/**
+ * Copyright (c) 2012 Lemur Consulting Ltd.
+ * <p/>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.search.PositionQueue;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.similarities.Similarity;
+
+/**
+ * A query that matches if a set of subqueries also match, and are within
+ * a given distance of each other within the document.  The subqueries
+ * may appear in the document in any order.
+ *
+ * N.B. Positions must be included in the index for this query to work
+ *
+ * Implements the AND operator as defined in <a href=
+ * "http://vigna.dsi.unimi.it/ftp/papers/EfficientAlgorithmsMinimalIntervalSemantics"
+ * >"Efficient Optimally Lazy Algorithms for Minimal-Interval Semantics"</a>
+ *
+ * @lucene.experimental
+ */
+
+public class UnorderedNearQuery extends PositionFilterQuery {
+
+  /**
+   * Constructs an OrderedNearQuery
+   * @param slop the maximum distance between the subquery matches
+   * @param subqueries the subqueries to match.
+   */
+  public UnorderedNearQuery(int slop, Query... subqueries) {
+    super(buildBooleanQuery(subqueries), new UnorderedNearScorerFactory(slop));
+  }
+
+  private static class UnorderedNearScorerFactory implements ScorerFilterFactory {
+
+    private final int slop;
+
+    UnorderedNearScorerFactory(int slop) {
+      this.slop = slop;
+    }
+
+    @Override
+    public Scorer scorer(Scorer filteredScorer, Similarity.SimScorer simScorer) {
+      return new WithinFilteredScorer(new UnorderedNearScorer(filteredScorer, simScorer), slop, simScorer);
+    }
+
+    @Override
+    public String getName() {
+      return "UnorderedNear/" + slop;
+    }
+  }
+
+  private static class UnorderedNearScorer extends PositionFilteredScorer {
+
+    SpanningPositionQueue posQueue;
+
+    public UnorderedNearScorer(Scorer filteredScorer, Similarity.SimScorer simScorer) {
+      super(filteredScorer, simScorer);
+      posQueue = new SpanningPositionQueue(subScorers);
+    }
+
+    @Override
+    protected int doNextPosition() throws IOException {
+      while (posQueue.isFull() && posQueue.span.begin == current.begin) {
+        posQueue.nextPosition();
+      }
+      if (!posQueue.isFull())
+        return NO_MORE_POSITIONS;
+      do {
+        //current.update(posQueue.top().interval, posQueue.span);
+        posQueue.updateCurrent();
+        if (current.equals(posQueue.top().docsEnum))
+          return current.begin;
+        matchDistance = posQueue.getMatchDistance();
+        posQueue.nextPosition();
+      } while (posQueue.isFull() && current.end == posQueue.span.end);
+      return current.begin;
+    }
+
+    @Override
+    protected void reset(int doc) throws IOException {
+      super.reset(doc);
+      current.reset();
+      posQueue.advanceTo(doc);
+    }
+
+    @Override
+    public float intervalScore() throws IOException {
+      return simScorer.computeSlopFactor(matchDistance);
+    }
+
+    private class SpanningPositionQueue extends PositionQueue {
+
+      Interval span = new Interval();
+      int scorerCount;
+      int firstIntervalEnd;
+      int lastIntervalBegin;
+
+      public SpanningPositionQueue(Scorer[] subScorers) {
+        super(subScorers);
+        scorerCount = subScorers.length;
+      }
+
+      public int getMatchDistance() {
+        return lastIntervalBegin - firstIntervalEnd - scorerCount + 1;
+      }
+
+      public boolean isFull() {
+        return queuesize == scorerCount;
+      }
+
+      public void updateCurrent() throws IOException {
+        current.update(this.top().docsEnum, span);
+        this.firstIntervalEnd = this.top().end;
+      }
+
+      private void updateRightExtreme(DocsEnum newRight) throws IOException {
+        if (span.end <= newRight.endPosition()) {
+          span.update(span, newRight);
+          this.lastIntervalBegin = newRight.startPosition();
+        }
+      }
+
+      protected void updateInternalIntervals() throws IOException {
+        updateRightExtreme(top().docsEnum);
+      }
+
+      @Override
+      public int nextPosition() throws IOException {
+        int position;
+        if ((position = super.nextPosition()) == DocsEnum.NO_MORE_POSITIONS) {
+          return DocsEnum.NO_MORE_POSITIONS;
+        }
+        span.update(top().docsEnum, span);
+        return position;
+      }
+
+      @Override
+      protected void init() throws IOException {
+        super.init();
+        for (Object docsEnumRef : getHeapArray()) {
+          if (docsEnumRef != null) {
+            updateRightExtreme(((DocsEnumRef) docsEnumRef).docsEnum);
+          }
+        }
+      }
+
+      @Override
+      public void advanceTo(int doc) {
+        super.advanceTo(doc);
+        span.reset();
+        firstIntervalEnd = lastIntervalBegin = span.begin;
+      }
+
+      @Override
+      protected boolean lessThan(DocsEnumRef left, DocsEnumRef right) {
+        int c = left.compareTo(right);
+        if (c != 0)
+          return c < 0;
+        return left.end > right.end;
+      }
+
+    }
+
+  }
+
+}
+
diff --git a/lucene/core/src/java/org/apache/lucene/search/posfilter/WithinFilteredScorer.java b/lucene/core/src/java/org/apache/lucene/search/posfilter/WithinFilteredScorer.java
new file mode 100644
index 0000000..5887644
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/search/posfilter/WithinFilteredScorer.java
@@ -0,0 +1,52 @@
+package org.apache.lucene.search.posfilter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.search.similarities.Similarity;
+
+public class WithinFilteredScorer extends PositionFilteredScorer {
+
+  private final int slop;
+  private final PositionFilteredScorer wrappedScorer;
+
+  public WithinFilteredScorer(PositionFilteredScorer wrappedScorer, int slop, Similarity.SimScorer simScorer) {
+    super(wrappedScorer, simScorer);
+    this.slop = slop;
+    this.wrappedScorer = wrappedScorer;
+  }
+
+  @Override
+  public float intervalScore() throws IOException {
+    return wrappedScorer.intervalScore();
+  }
+
+  @Override
+  protected int doNextPosition() throws IOException {
+    int position;
+    while ((position = wrappedScorer.nextPosition()) != NO_MORE_POSITIONS) {
+      if (wrappedScorer.getMatchDistance() <= slop) {
+        current.update(wrappedScorer);
+        return position;
+      }
+    }
+    return NO_MORE_POSITIONS;
+  }
+
+}
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/Similarity.java b/lucene/core/src/java/org/apache/lucene/search/similarities/Similarity.java
index cc7da6f..c8210c3 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/Similarity.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/Similarity.java
@@ -17,10 +17,8 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-
-import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.FieldInvertState;
+import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.search.BooleanQuery;
 import org.apache.lucene.search.CollectionStatistics;
 import org.apache.lucene.search.Explanation;
@@ -28,9 +26,11 @@
 import org.apache.lucene.search.PhraseQuery;
 import org.apache.lucene.search.TermQuery;
 import org.apache.lucene.search.TermStatistics;
-import org.apache.lucene.search.spans.SpanQuery; // javadoc
+import org.apache.lucene.search.spans.SpanQuery;
 import org.apache.lucene.util.BytesRef;
-import org.apache.lucene.util.SmallFloat; // javadoc
+import org.apache.lucene.util.SmallFloat;
+
+import java.io.IOException;
 
 /** 
  * Similarity defines the components of Lucene scoring.
diff --git a/lucene/core/src/java/org/apache/lucene/search/spans/SpanScorer.java b/lucene/core/src/java/org/apache/lucene/search/spans/SpanScorer.java
index 74a098d..416ba5c 100644
--- a/lucene/core/src/java/org/apache/lucene/search/spans/SpanScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/spans/SpanScorer.java
@@ -19,9 +19,10 @@
 
 import java.io.IOException;
 
-import org.apache.lucene.search.Weight;
 import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Weight;
 import org.apache.lucene.search.similarities.Similarity;
+import org.apache.lucene.util.BytesRef;
 
 /**
  * Public for extension only.
@@ -96,16 +97,47 @@
   public int freq() throws IOException {
     return numMatches;
   }
-  
+
+  @Override
+  public int nextPosition() throws IOException {
+    throw new UnsupportedOperationException("SpanQueries do not support nextPosition() iteration");
+  }
+
+  @Override
+  public int startPosition() throws IOException {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int endPosition() throws IOException {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int startOffset() throws IOException {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int endOffset() throws IOException {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public BytesRef getPayload() throws IOException {
+    throw new UnsupportedOperationException();
+  }
+
   /** Returns the intermediate "sloppy freq" adjusted for edit distance 
    *  @lucene.internal */
   // only public so .payloads can see it.
   public float sloppyFreq() throws IOException {
     return freq;
   }
-  
+
   @Override
   public long cost() {
     return spans.cost();
   }
+
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/spans/SpanTermQuery.java b/lucene/core/src/java/org/apache/lucene/search/spans/SpanTermQuery.java
index bb36b3f..465479e 100644
--- a/lucene/core/src/java/org/apache/lucene/search/spans/SpanTermQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/spans/SpanTermQuery.java
@@ -20,7 +20,7 @@
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.Fields;
 import org.apache.lucene.index.Term;
-import org.apache.lucene.index.DocsAndPositionsEnum;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.TermContext;
 import org.apache.lucene.index.TermState;
 import org.apache.lucene.index.Terms;
@@ -115,7 +115,7 @@
     final TermsEnum termsEnum = context.reader().terms(term.field()).iterator(null);
     termsEnum.seekExact(term.bytes(), state);
     
-    final DocsAndPositionsEnum postings = termsEnum.docsAndPositions(acceptDocs, null, DocsAndPositionsEnum.FLAG_PAYLOADS);
+    final DocsEnum postings = termsEnum.docsAndPositions(acceptDocs, null, DocsEnum.FLAG_PAYLOADS);
 
     if (postings != null) {
       return new TermSpans(postings, term);
diff --git a/lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java b/lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java
index 0e06343..6fb80ab 100644
--- a/lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java
+++ b/lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java
@@ -17,11 +17,18 @@
  * limitations under the License.
  */
 
-import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexReaderContext;
+import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.index.TermContext;
-import org.apache.lucene.search.*;
+import org.apache.lucene.search.ComplexExplanation;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.TermStatistics;
+import org.apache.lucene.search.Weight;
 import org.apache.lucene.search.similarities.Similarity;
 import org.apache.lucene.search.similarities.Similarity.SimScorer;
 import org.apache.lucene.util.Bits;
@@ -81,7 +88,7 @@
   }
 
   @Override
-  public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
+  public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
     if (stats == null) {
       return null;
     } else {
@@ -91,7 +98,7 @@
 
   @Override
   public Explanation explain(LeafReaderContext context, int doc) throws IOException {
-    SpanScorer scorer = (SpanScorer) scorer(context, context.reader().getLiveDocs());
+    SpanScorer scorer = (SpanScorer) scorer(context, DocsEnum.FLAG_POSITIONS, context.reader().getLiveDocs());
     if (scorer != null) {
       int newDoc = scorer.advance(doc);
       if (newDoc == doc) {
diff --git a/lucene/core/src/java/org/apache/lucene/search/spans/TermSpans.java b/lucene/core/src/java/org/apache/lucene/search/spans/TermSpans.java
index d4974a5..39f72eb 100644
--- a/lucene/core/src/java/org/apache/lucene/search/spans/TermSpans.java
+++ b/lucene/core/src/java/org/apache/lucene/search/spans/TermSpans.java
@@ -17,7 +17,7 @@
 
 
 import org.apache.lucene.index.Term;
-import org.apache.lucene.index.DocsAndPositionsEnum;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.util.BytesRef;
 
@@ -30,7 +30,7 @@
  * Public for extension only
  */
 public class TermSpans extends Spans {
-  protected final DocsAndPositionsEnum postings;
+  protected final DocsEnum postings;
   protected final Term term;
   protected int doc;
   protected int freq;
@@ -38,7 +38,7 @@
   protected int position;
   protected boolean readPayload;
 
-  public TermSpans(DocsAndPositionsEnum postings, Term term) {
+  public TermSpans(DocsEnum postings, Term term) {
     this.postings = postings;
     this.term = term;
     doc = -1;
@@ -132,7 +132,7 @@
             (doc == -1 ? "START" : (doc == Integer.MAX_VALUE) ? "END" : doc + "-" + position);
   }
 
-  public DocsAndPositionsEnum getPostings() {
+  public DocsEnum getPostings() {
     return postings;
   }
 
diff --git a/lucene/core/src/test/org/apache/lucene/TestSearchForDuplicates.java b/lucene/core/src/test/org/apache/lucene/TestSearchForDuplicates.java
index 4747557..35d7b0b 100644
--- a/lucene/core/src/test/org/apache/lucene/TestSearchForDuplicates.java
+++ b/lucene/core/src/test/org/apache/lucene/TestSearchForDuplicates.java
@@ -22,13 +22,32 @@
 import java.io.StringWriter;
 import java.util.Random;
 
-import org.apache.lucene.store.*;
-import org.apache.lucene.document.*;
-import org.apache.lucene.analysis.*;
-import org.apache.lucene.index.*;
-import org.apache.lucene.search.*;
+import com.carrotsearch.randomizedtesting.annotations.Seed;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.MockAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.IntField;
+import org.apache.lucene.document.NumericDocValuesField;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.index.MergePolicy;
+import org.apache.lucene.index.StoredDocument;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.store.Directory;
 import org.apache.lucene.util.LuceneTestCase;
 
+@Seed("1614EBB06B1F4D76:B454400327B737DA")
 public class TestSearchForDuplicates extends LuceneTestCase {
 
   static final String PRIORITY_FIELD ="priority";
diff --git a/lucene/core/src/test/org/apache/lucene/analysis/TestCachingTokenFilter.java b/lucene/core/src/test/org/apache/lucene/analysis/TestCachingTokenFilter.java
index af307bb..c421033 100644
--- a/lucene/core/src/test/org/apache/lucene/analysis/TestCachingTokenFilter.java
+++ b/lucene/core/src/test/org/apache/lucene/analysis/TestCachingTokenFilter.java
@@ -26,7 +26,7 @@
 import org.apache.lucene.document.TextField;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.MultiFields;
-import org.apache.lucene.index.DocsAndPositionsEnum;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.RandomIndexWriter;
 import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.store.Directory;
@@ -72,7 +72,7 @@
     writer.addDocument(doc);
     
     IndexReader reader = writer.getReader();
-    DocsAndPositionsEnum termPositions = MultiFields.getTermPositionsEnum(reader,
+    DocsEnum termPositions = MultiFields.getTermPositionsEnum(reader,
                                                                           MultiFields.getLiveDocs(reader),
                                                                           "preanalyzed",
                                                                           new BytesRef("term1"));
diff --git a/lucene/core/src/test/org/apache/lucene/analysis/TestMockAnalyzer.java b/lucene/core/src/test/org/apache/lucene/analysis/TestMockAnalyzer.java
index 485ea3f..b407adb 100644
--- a/lucene/core/src/test/org/apache/lucene/analysis/TestMockAnalyzer.java
+++ b/lucene/core/src/test/org/apache/lucene/analysis/TestMockAnalyzer.java
@@ -17,15 +17,10 @@
  * limitations under the License.
  */
 
-import java.io.Reader;
-import java.io.StringReader;
-import java.util.Arrays;
-import java.util.Random;
-
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.document.FieldType;
-import org.apache.lucene.index.DocsAndPositionsEnum;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.Fields;
 import org.apache.lucene.index.IndexOptions;
 import org.apache.lucene.index.LeafReader;
@@ -40,6 +35,11 @@
 import org.apache.lucene.util.automaton.Operations;
 import org.apache.lucene.util.automaton.RegExp;
 
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Arrays;
+import java.util.Random;
+
 import static org.apache.lucene.util.automaton.Operations.DEFAULT_MAX_DETERMINIZED_STATES;
 
 public class TestMockAnalyzer extends BaseTokenStreamTestCase {
@@ -321,7 +321,7 @@
     final Terms terms = fields.terms("f");
     final TermsEnum te = terms.iterator(null);
     assertEquals(new BytesRef("a"), te.next());
-    final DocsAndPositionsEnum dpe = te.docsAndPositions(null, null);
+    final DocsEnum dpe = te.docsAndPositions(null, null);
     assertEquals(0, dpe.nextDoc());
     assertEquals(2, dpe.freq());
     assertEquals(0, dpe.nextPosition());
diff --git a/lucene/core/src/test/org/apache/lucene/codecs/lucene50/TestBlockPostingsFormat3.java b/lucene/core/src/test/org/apache/lucene/codecs/lucene50/TestBlockPostingsFormat3.java
index fbb4055..f8d82a0 100644
--- a/lucene/core/src/test/org/apache/lucene/codecs/lucene50/TestBlockPostingsFormat3.java
+++ b/lucene/core/src/test/org/apache/lucene/codecs/lucene50/TestBlockPostingsFormat3.java
@@ -33,7 +33,6 @@
 import org.apache.lucene.document.FieldType;
 import org.apache.lucene.document.TextField;
 import org.apache.lucene.index.DirectoryReader;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexOptions;
 import org.apache.lucene.index.IndexWriter;
@@ -284,8 +283,8 @@
   public void assertTermsEnum(TermsEnum leftTermsEnum, TermsEnum rightTermsEnum, boolean deep) throws Exception {
     BytesRef term;
     Bits randomBits = new RandomBits(MAXDOC, random().nextDouble(), random());
-    DocsAndPositionsEnum leftPositions = null;
-    DocsAndPositionsEnum rightPositions = null;
+    DocsEnum leftPositions = null;
+    DocsEnum rightPositions = null;
     DocsEnum leftDocs = null;
     DocsEnum rightDocs = null;
     
@@ -306,30 +305,30 @@
                                 leftPositions = leftTermsEnum.docsAndPositions(randomBits, leftPositions),
                                 rightPositions = rightTermsEnum.docsAndPositions(randomBits, rightPositions));
         // with payloads only
-        assertDocsAndPositionsEnum(leftPositions = leftTermsEnum.docsAndPositions(null, leftPositions, DocsAndPositionsEnum.FLAG_PAYLOADS),
-                                   rightPositions = rightTermsEnum.docsAndPositions(null, rightPositions, DocsAndPositionsEnum.FLAG_PAYLOADS));
-        assertDocsAndPositionsEnum(leftPositions = leftTermsEnum.docsAndPositions(randomBits, leftPositions, DocsAndPositionsEnum.FLAG_PAYLOADS),
-                                   rightPositions = rightTermsEnum.docsAndPositions(randomBits, rightPositions, DocsAndPositionsEnum.FLAG_PAYLOADS));
+        assertDocsAndPositionsEnum(leftPositions = leftTermsEnum.docsAndPositions(null, leftPositions, DocsEnum.FLAG_PAYLOADS),
+                                   rightPositions = rightTermsEnum.docsAndPositions(null, rightPositions, DocsEnum.FLAG_PAYLOADS));
+        assertDocsAndPositionsEnum(leftPositions = leftTermsEnum.docsAndPositions(randomBits, leftPositions, DocsEnum.FLAG_PAYLOADS),
+                                   rightPositions = rightTermsEnum.docsAndPositions(randomBits, rightPositions, DocsEnum.FLAG_PAYLOADS));
 
         assertPositionsSkipping(leftTermsEnum.docFreq(), 
-                                leftPositions = leftTermsEnum.docsAndPositions(null, leftPositions, DocsAndPositionsEnum.FLAG_PAYLOADS),
-                                rightPositions = rightTermsEnum.docsAndPositions(null, rightPositions, DocsAndPositionsEnum.FLAG_PAYLOADS));
+                                leftPositions = leftTermsEnum.docsAndPositions(null, leftPositions, DocsEnum.FLAG_PAYLOADS),
+                                rightPositions = rightTermsEnum.docsAndPositions(null, rightPositions, DocsEnum.FLAG_PAYLOADS));
         assertPositionsSkipping(leftTermsEnum.docFreq(), 
-                                leftPositions = leftTermsEnum.docsAndPositions(randomBits, leftPositions, DocsAndPositionsEnum.FLAG_PAYLOADS),
-                                rightPositions = rightTermsEnum.docsAndPositions(randomBits, rightPositions, DocsAndPositionsEnum.FLAG_PAYLOADS));
+                                leftPositions = leftTermsEnum.docsAndPositions(randomBits, leftPositions, DocsEnum.FLAG_PAYLOADS),
+                                rightPositions = rightTermsEnum.docsAndPositions(randomBits, rightPositions, DocsEnum.FLAG_PAYLOADS));
 
         // with offsets only
-        assertDocsAndPositionsEnum(leftPositions = leftTermsEnum.docsAndPositions(null, leftPositions, DocsAndPositionsEnum.FLAG_OFFSETS),
-                                   rightPositions = rightTermsEnum.docsAndPositions(null, rightPositions, DocsAndPositionsEnum.FLAG_OFFSETS));
-        assertDocsAndPositionsEnum(leftPositions = leftTermsEnum.docsAndPositions(randomBits, leftPositions, DocsAndPositionsEnum.FLAG_OFFSETS),
-                                   rightPositions = rightTermsEnum.docsAndPositions(randomBits, rightPositions, DocsAndPositionsEnum.FLAG_OFFSETS));
+        assertDocsAndPositionsEnum(leftPositions = leftTermsEnum.docsAndPositions(null, leftPositions, DocsEnum.FLAG_OFFSETS),
+                                   rightPositions = rightTermsEnum.docsAndPositions(null, rightPositions, DocsEnum.FLAG_OFFSETS));
+        assertDocsAndPositionsEnum(leftPositions = leftTermsEnum.docsAndPositions(randomBits, leftPositions, DocsEnum.FLAG_OFFSETS),
+                                   rightPositions = rightTermsEnum.docsAndPositions(randomBits, rightPositions, DocsEnum.FLAG_OFFSETS));
 
         assertPositionsSkipping(leftTermsEnum.docFreq(), 
-                                leftPositions = leftTermsEnum.docsAndPositions(null, leftPositions, DocsAndPositionsEnum.FLAG_OFFSETS),
-                                rightPositions = rightTermsEnum.docsAndPositions(null, rightPositions, DocsAndPositionsEnum.FLAG_OFFSETS));
+                                leftPositions = leftTermsEnum.docsAndPositions(null, leftPositions, DocsEnum.FLAG_OFFSETS),
+                                rightPositions = rightTermsEnum.docsAndPositions(null, rightPositions, DocsEnum.FLAG_OFFSETS));
         assertPositionsSkipping(leftTermsEnum.docFreq(), 
-                                leftPositions = leftTermsEnum.docsAndPositions(randomBits, leftPositions, DocsAndPositionsEnum.FLAG_OFFSETS),
-                                rightPositions = rightTermsEnum.docsAndPositions(randomBits, rightPositions, DocsAndPositionsEnum.FLAG_OFFSETS));
+                                leftPositions = leftTermsEnum.docsAndPositions(randomBits, leftPositions, DocsEnum.FLAG_OFFSETS),
+                                rightPositions = rightTermsEnum.docsAndPositions(randomBits, rightPositions, DocsEnum.FLAG_OFFSETS));
         
         // with positions only
         assertDocsAndPositionsEnum(leftPositions = leftTermsEnum.docsAndPositions(null, leftPositions, DocsEnum.FLAG_NONE),
@@ -389,7 +388,7 @@
   /**
    * checks docs + freqs + positions + payloads, sequentially
    */
-  public void assertDocsAndPositionsEnum(DocsAndPositionsEnum leftDocs, DocsAndPositionsEnum rightDocs) throws Exception {
+  public void assertDocsAndPositionsEnum(DocsEnum leftDocs, DocsEnum rightDocs) throws Exception {
     if (leftDocs == null || rightDocs == null) {
       assertNull(leftDocs);
       assertNull(rightDocs);
@@ -462,7 +461,7 @@
   /**
    * checks advancing docs + positions
    */
-  public void assertPositionsSkipping(int docFreq, DocsAndPositionsEnum leftDocs, DocsAndPositionsEnum rightDocs) throws Exception {
+  public void assertPositionsSkipping(int docFreq, DocsEnum leftDocs, DocsEnum rightDocs) throws Exception {
     if (leftDocs == null || rightDocs == null) {
       assertNull(leftDocs);
       assertNull(rightDocs);
diff --git a/lucene/core/src/test/org/apache/lucene/codecs/perfield/TestPerFieldPostingsFormat2.java b/lucene/core/src/test/org/apache/lucene/codecs/perfield/TestPerFieldPostingsFormat2.java
index 3b19087..5be8c78 100644
--- a/lucene/core/src/test/org/apache/lucene/codecs/perfield/TestPerFieldPostingsFormat2.java
+++ b/lucene/core/src/test/org/apache/lucene/codecs/perfield/TestPerFieldPostingsFormat2.java
@@ -16,6 +16,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 import java.io.IOException;
 
 import org.apache.lucene.analysis.MockAnalyzer;
@@ -265,7 +266,7 @@
     }
     dir.close();
   }
-  
+
   public void testSameCodecDifferentInstance() throws Exception {
     Codec codec = new AssertingCodec() {
       @Override
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestCodecs.java b/lucene/core/src/test/org/apache/lucene/index/TestCodecs.java
index 5ae4d89..fd34333 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestCodecs.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestCodecs.java
@@ -343,7 +343,7 @@
         assertTrue(doc != DocIdSetIterator.NO_MORE_DOCS);
         assertEquals(docs[i], doc);
         if (doPos) {
-          this.verifyPositions(positions[i], ((DocsAndPositionsEnum) docsEnum));
+          this.verifyPositions(positions[i], docsEnum);
         }
       }
       assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsEnum.nextDoc());
@@ -351,7 +351,7 @@
 
     byte[] data = new byte[10];
 
-    private void verifyPositions(final PositionData[] positions, final DocsAndPositionsEnum posEnum) throws Throwable {
+    private void verifyPositions(final PositionData[] positions, final DocsEnum posEnum) throws Throwable {
       for(int i=0;i<positions.length;i++) {
         final int pos = posEnum.nextPosition();
         assertEquals(positions[i].pos, pos);
@@ -463,7 +463,7 @@
           term = field.terms[upto];
           if (random().nextInt(3) == 1) {
             final DocsEnum docs;
-            final DocsAndPositionsEnum postings;
+            final DocsEnum postings;
             if (!field.omitTF) {
               postings = termsEnum.docsAndPositions(null, null);
               if (postings != null) {
@@ -694,22 +694,21 @@
     @Override
     public DocsEnum docs(Bits liveDocs, DocsEnum reuse, int flags) {
       assert liveDocs == null;
-      return new DataDocsAndPositionsEnum(fieldData.terms[upto]);
+      return new DataDocsEnum(fieldData.terms[upto]);
     }
 
     @Override
-    public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) {
-      assert liveDocs == null;
-      return new DataDocsAndPositionsEnum(fieldData.terms[upto]);
+    public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) {
+      return docs(liveDocs, reuse, flags);
     }
   }
 
-  private static class DataDocsAndPositionsEnum extends DocsAndPositionsEnum {
+  private static class DataDocsEnum extends DocsEnum {
     final TermData termData;
     int docUpto = -1;
     int posUpto;
 
-    public DataDocsAndPositionsEnum(TermData termData) {
+    public DataDocsEnum(TermData termData) {
       this.termData = termData;
     }
 
@@ -756,6 +755,16 @@
     }
 
     @Override
+    public int startPosition() throws IOException {
+      return termData.positions[docUpto][posUpto].pos;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return termData.positions[docUpto][posUpto].pos;
+    }
+
+    @Override
     public BytesRef getPayload() {
       return termData.positions[docUpto][posUpto].payload;
     }
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestDirectoryReader.java b/lucene/core/src/test/org/apache/lucene/index/TestDirectoryReader.java
index b0552be..f467b7e 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestDirectoryReader.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestDirectoryReader.java
@@ -633,8 +633,8 @@
 
       while(enum1.next() != null) {
         assertEquals("Different terms", enum1.term(), enum2.next());
-        DocsAndPositionsEnum tp1 = enum1.docsAndPositions(liveDocs, null);
-        DocsAndPositionsEnum tp2 = enum2.docsAndPositions(liveDocs, null);
+        DocsEnum tp1 = enum1.docsAndPositions(liveDocs, null);
+        DocsEnum tp2 = enum2.docsAndPositions(liveDocs, null);
 
         while(tp1.nextDoc() != DocIdSetIterator.NO_MORE_DOCS) {
           assertTrue(tp2.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestDoc.java b/lucene/core/src/test/org/apache/lucene/index/TestDoc.java
index 9900c7c..85a4431 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestDoc.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestDoc.java
@@ -261,7 +261,7 @@
           out.print("  term=" + field + ":" + tis.term());
           out.println("    DF=" + tis.docFreq());
 
-          DocsAndPositionsEnum positions = tis.docsAndPositions(reader.getLiveDocs(), null);
+          DocsEnum positions = tis.docsAndPositions(reader.getLiveDocs(), null);
 
           while (positions.nextDoc() != DocIdSetIterator.NO_MORE_DOCS) {
             out.print(" doc=" + positions.docID());
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestDocsAndPositions.java b/lucene/core/src/test/org/apache/lucene/index/TestDocsAndPositions.java
index 2c185cb..f0d27bd 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestDocsAndPositions.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestDocsAndPositions.java
@@ -16,10 +16,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-
 import org.apache.lucene.analysis.MockAnalyzer;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
@@ -32,6 +28,10 @@
 import org.apache.lucene.util.LuceneTestCase;
 import org.apache.lucene.util.TestUtil;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+
 public class TestDocsAndPositions extends LuceneTestCase {
   private String fieldName;
 
@@ -42,7 +42,7 @@
   }
 
   /**
-   * Simple testcase for {@link DocsAndPositionsEnum}
+   * Simple testcase for {@link DocsEnum}
    */
   public void testPositionsSimple() throws IOException {
     Directory directory = newDirectory();
@@ -65,7 +65,7 @@
       BytesRef bytes = new BytesRef("1");
       IndexReaderContext topReaderContext = reader.getContext();
       for (LeafReaderContext leafReaderContext : topReaderContext.leaves()) {
-        DocsAndPositionsEnum docsAndPosEnum = getDocsAndPositions(
+        DocsEnum docsAndPosEnum = getDocsAndPositions(
             leafReaderContext.reader(), bytes, null);
         assertNotNull(docsAndPosEnum);
         if (leafReaderContext.reader().maxDoc() == 0) {
@@ -90,7 +90,7 @@
     directory.close();
   }
 
-  public DocsAndPositionsEnum getDocsAndPositions(LeafReader reader,
+  public DocsEnum getDocsAndPositions(LeafReader reader,
       BytesRef bytes, Bits liveDocs) throws IOException {
     Terms terms = reader.terms(fieldName);
     if (terms != null) {
@@ -148,7 +148,7 @@
       BytesRef bytes = new BytesRef("" + term);
       IndexReaderContext topReaderContext = reader.getContext();
       for (LeafReaderContext leafReaderContext : topReaderContext.leaves()) {
-        DocsAndPositionsEnum docsAndPosEnum = getDocsAndPositions(
+        DocsEnum docsAndPosEnum = getDocsAndPositions(
             leafReaderContext.reader(), bytes, null);
         assertNotNull(docsAndPosEnum);
         int initDoc = 0;
@@ -303,7 +303,7 @@
 
       IndexReaderContext topReaderContext = reader.getContext();
       for (LeafReaderContext leafReaderContext : topReaderContext.leaves()) {
-        DocsAndPositionsEnum docsAndPosEnum = getDocsAndPositions(
+        DocsEnum docsAndPosEnum = getDocsAndPositions(
             leafReaderContext.reader(), bytes, null);
         assertNotNull(docsAndPosEnum);
 
@@ -361,7 +361,7 @@
     writer.addDocument(doc);
     DirectoryReader reader = writer.getReader();
     LeafReader r = getOnlySegmentReader(reader);
-    DocsAndPositionsEnum disi = r.termPositionsEnum(new Term("foo", "bar"));
+    DocsEnum disi = r.termPositionsEnum(new Term("foo", "bar"));
     int docid = disi.docID();
     assertEquals(-1, docid);
     assertTrue(disi.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestDocumentWriter.java b/lucene/core/src/test/org/apache/lucene/index/TestDocumentWriter.java
index 37dc798..1596aa9 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestDocumentWriter.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestDocumentWriter.java
@@ -125,7 +125,7 @@
     writer.close();
     SegmentReader reader = new SegmentReader(info, newIOContext(random()));
 
-    DocsAndPositionsEnum termPositions = MultiFields.getTermPositionsEnum(reader, MultiFields.getLiveDocs(reader),
+    DocsEnum termPositions = MultiFields.getTermPositionsEnum(reader, MultiFields.getLiveDocs(reader),
                                                                           "repeated", new BytesRef("repeated"));
     assertTrue(termPositions.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
     int freq = termPositions.freq();
@@ -197,7 +197,7 @@
     writer.close();
     SegmentReader reader = new SegmentReader(info, newIOContext(random()));
 
-    DocsAndPositionsEnum termPositions = MultiFields.getTermPositionsEnum(reader, reader.getLiveDocs(), "f1", new BytesRef("a"));
+    DocsEnum termPositions = MultiFields.getTermPositionsEnum(reader, reader.getLiveDocs(), "f1", new BytesRef("a"));
     assertTrue(termPositions.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
     int freq = termPositions.freq();
     assertEquals(3, freq);
@@ -239,7 +239,7 @@
     writer.close();
     SegmentReader reader = new SegmentReader(info, newIOContext(random()));
 
-    DocsAndPositionsEnum termPositions = reader.termPositionsEnum(new Term("preanalyzed", "term1"));
+    DocsEnum termPositions = reader.termPositionsEnum(new Term("preanalyzed", "term1"));
     assertTrue(termPositions.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
     assertEquals(1, termPositions.freq());
     assertEquals(0, termPositions.nextPosition());
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestDuelingCodecs.java b/lucene/core/src/test/org/apache/lucene/index/TestDuelingCodecs.java
index d8ce513..652d170 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestDuelingCodecs.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestDuelingCodecs.java
@@ -17,9 +17,6 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.util.Random;
-
 import org.apache.lucene.analysis.MockAnalyzer;
 import org.apache.lucene.codecs.Codec;
 import org.apache.lucene.document.Document;
@@ -32,6 +29,9 @@
 import org.apache.lucene.util.LuceneTestCase;
 import org.apache.lucene.util.TestUtil;
 
+import java.io.IOException;
+import java.util.Random;
+
 /**
  * Compares one codec against another
  */
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestFilterLeafReader.java b/lucene/core/src/test/org/apache/lucene/index/TestFilterLeafReader.java
index 16e27ff..8b112e6 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestFilterLeafReader.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestFilterLeafReader.java
@@ -18,10 +18,6 @@
  */
 
 
-import java.io.IOException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-
 import org.apache.lucene.analysis.MockAnalyzer;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
@@ -32,6 +28,10 @@
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.LuceneTestCase;
 
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
 public class TestFilterLeafReader extends LuceneTestCase {
 
   private static class TestReader extends FilterLeafReader {
@@ -76,14 +76,14 @@
       }
 
       @Override
-      public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
-        return new TestPositions(super.docsAndPositions(liveDocs, reuse == null ? null : ((FilterDocsAndPositionsEnum) reuse).in, flags));
+      public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
+        return new TestPositions(super.docsAndPositions(liveDocs, reuse == null ? null : ((FilterDocsEnum) reuse).in, flags));
       }
     }
 
     /** Filter that only returns odd numbered documents. */
-    private static class TestPositions extends FilterDocsAndPositionsEnum {
-      public TestPositions(DocsAndPositionsEnum in) {
+    private static class TestPositions extends FilterDocsEnum {
+      public TestPositions(DocsEnum in) {
         super(in);
       }
 
@@ -151,7 +151,7 @@
     
     assertEquals(TermsEnum.SeekStatus.FOUND, terms.seekCeil(new BytesRef("one")));
     
-    DocsAndPositionsEnum positions = terms.docsAndPositions(MultiFields.getLiveDocs(reader), null);
+    DocsEnum positions = terms.docsAndPositions(MultiFields.getLiveDocs(reader), null);
     while (positions.nextDoc() != DocIdSetIterator.NO_MORE_DOCS) {
       assertTrue((positions.docID() % 2) == 1);
     }
@@ -189,7 +189,6 @@
     checkOverrideMethods(FilterLeafReader.FilterTerms.class);
     checkOverrideMethods(FilterLeafReader.FilterTermsEnum.class);
     checkOverrideMethods(FilterLeafReader.FilterDocsEnum.class);
-    checkOverrideMethods(FilterLeafReader.FilterDocsAndPositionsEnum.class);
   }
 
   public void testUnwrap() throws IOException {
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestIndexWriter.java b/lucene/core/src/test/org/apache/lucene/index/TestIndexWriter.java
index 54da47c..ace31af 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestIndexWriter.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestIndexWriter.java
@@ -833,7 +833,7 @@
     Terms tpv = r.getTermVectors(0).terms("field");
     TermsEnum termsEnum = tpv.iterator(null);
     assertNotNull(termsEnum.next());
-    DocsAndPositionsEnum dpEnum = termsEnum.docsAndPositions(null, null);
+    DocsEnum dpEnum = termsEnum.docsAndPositions(null, null);
     assertNotNull(dpEnum);
     assertTrue(dpEnum.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
     assertEquals(1, dpEnum.freq());
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestIndexableField.java b/lucene/core/src/test/org/apache/lucene/index/TestIndexableField.java
index 85c95dc..3ed2967 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestIndexableField.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestIndexableField.java
@@ -332,7 +332,7 @@
             TermsEnum termsEnum = tfv.iterator(null);
             assertEquals(new BytesRef(""+counter), termsEnum.next());
             assertEquals(1, termsEnum.totalTermFreq());
-            DocsAndPositionsEnum dpEnum = termsEnum.docsAndPositions(null, null);
+            DocsEnum dpEnum = termsEnum.docsAndPositions(null, null);
             assertTrue(dpEnum.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
             assertEquals(1, dpEnum.freq());
             assertEquals(1, dpEnum.nextPosition());
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestLazyProxSkipping.java b/lucene/core/src/test/org/apache/lucene/index/TestLazyProxSkipping.java
index 9e4ae9c..49cc6fb 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestLazyProxSkipping.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestLazyProxSkipping.java
@@ -154,7 +154,7 @@
         writer.close();
         IndexReader reader = DirectoryReader.open(directory);
 
-        DocsAndPositionsEnum tp = MultiFields.getTermPositionsEnum(reader,
+        DocsEnum tp = MultiFields.getTermPositionsEnum(reader,
                                                                    MultiFields.getLiveDocs(reader),
                                                                    this.field,
                                                                    new BytesRef("b"));
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestLongPostings.java b/lucene/core/src/test/org/apache/lucene/index/TestLongPostings.java
index 0212f66..1179428 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestLongPostings.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestLongPostings.java
@@ -167,7 +167,7 @@
         System.out.println("\nTEST: iter=" + iter + " doS1=" + doS1);
       }
         
-      final DocsAndPositionsEnum postings = MultiFields.getTermPositionsEnum(r, null, "field", new BytesRef(term));
+      final DocsEnum postings = MultiFields.getTermPositionsEnum(r, null, "field", new BytesRef(term));
 
       int docID = -1;
       while(docID < DocIdSetIterator.NO_MORE_DOCS) {
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestMultiLevelSkipList.java b/lucene/core/src/test/org/apache/lucene/index/TestMultiLevelSkipList.java
index d1a9cd8..2ced769 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestMultiLevelSkipList.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestMultiLevelSkipList.java
@@ -84,7 +84,7 @@
     
     for (int i = 0; i < 2; i++) {
       counter = 0;
-      DocsAndPositionsEnum tp = reader.termPositionsEnum(term);
+      DocsEnum tp = reader.termPositionsEnum(term);
       checkSkipTo(tp, 14, 185); // no skips
       checkSkipTo(tp, 17, 190); // one skip on level 0
       checkSkipTo(tp, 287, 200); // one skip on level 1, two on level 0
@@ -95,7 +95,7 @@
     }
   }
 
-  public void checkSkipTo(DocsAndPositionsEnum tp, int target, int maxCounter) throws IOException {
+  public void checkSkipTo(DocsEnum tp, int target, int maxCounter) throws IOException {
     tp.advance(target);
     if (maxCounter < counter) {
       fail("Too many bytes read: " + counter + " vs " + maxCounter);
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestPayloads.java b/lucene/core/src/test/org/apache/lucene/index/TestPayloads.java
index a9e068c..0f36796 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestPayloads.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestPayloads.java
@@ -17,6 +17,27 @@
  * limitations under the License.
  */
 
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.CannedTokenStream;
+import org.apache.lucene.analysis.MockAnalyzer;
+import org.apache.lucene.analysis.MockTokenizer;
+import org.apache.lucene.analysis.Token;
+import org.apache.lucene.analysis.TokenFilter;
+import org.apache.lucene.analysis.TokenStream;
+import org.apache.lucene.analysis.Tokenizer;
+import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
+import org.apache.lucene.analysis.tokenattributes.PayloadAttribute;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.TextField;
+import org.apache.lucene.index.IndexWriterConfig.OpenMode;
+import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.util.TestUtil;
+
 import java.io.IOException;
 import java.io.StringReader;
 import java.nio.charset.Charset;
@@ -26,20 +47,6 @@
 import java.util.List;
 import java.util.Map;
 
-import org.apache.lucene.analysis.*;
-import org.apache.lucene.analysis.tokenattributes.PayloadAttribute;
-import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.Field;
-import org.apache.lucene.document.TextField;
-import org.apache.lucene.index.IndexWriterConfig.OpenMode;
-import org.apache.lucene.search.DocIdSetIterator;
-import org.apache.lucene.store.Directory;
-import org.apache.lucene.util.BytesRef;
-import org.apache.lucene.util.Bits;
-import org.apache.lucene.util.LuceneTestCase;
-import org.apache.lucene.util.TestUtil;
-
 public class TestPayloads extends LuceneTestCase {
     
     // Simple tests to test the Payload class
@@ -183,7 +190,7 @@
 
         byte[] verifyPayloadData = new byte[payloadDataLength];
         offset = 0;
-        DocsAndPositionsEnum[] tps = new DocsAndPositionsEnum[numTerms];
+        DocsEnum[] tps = new DocsEnum[numTerms];
         for (int i = 0; i < numTerms; i++) {
           tps[i] = MultiFields.getTermPositionsEnum(reader,
                                                     MultiFields.getLiveDocs(reader),
@@ -214,7 +221,7 @@
         /*
          *  test lazy skipping
          */        
-        DocsAndPositionsEnum tp = MultiFields.getTermPositionsEnum(reader,
+        DocsEnum tp = MultiFields.getTermPositionsEnum(reader,
                                                                    MultiFields.getLiveDocs(reader),
                                                                    terms[0].field(),
                                                                    new BytesRef(terms[0].text()));
@@ -481,7 +488,7 @@
         IndexReader reader = DirectoryReader.open(dir);
         TermsEnum terms = MultiFields.getFields(reader).terms(field).iterator(null);
         Bits liveDocs = MultiFields.getLiveDocs(reader);
-        DocsAndPositionsEnum tp = null;
+        DocsEnum tp = null;
         while (terms.next() != null) {
           String termText = terms.term().utf8ToString();
           tp = terms.docsAndPositions(liveDocs, tp);
@@ -604,7 +611,7 @@
     writer.addDocument(doc);
     DirectoryReader reader = writer.getReader();
     LeafReader sr = SlowCompositeReaderWrapper.wrap(reader);
-    DocsAndPositionsEnum de = sr.termPositionsEnum(new Term("field", "withPayload"));
+    DocsEnum de = sr.termPositionsEnum(new Term("field", "withPayload"));
     de.nextDoc();
     de.nextPosition();
     assertEquals(new BytesRef("test"), de.getPayload());
@@ -638,7 +645,7 @@
     writer.addDocument(doc);
     DirectoryReader reader = writer.getReader();
     SegmentReader sr = getOnlySegmentReader(reader);
-    DocsAndPositionsEnum de = sr.termPositionsEnum(new Term("field", "withPayload"));
+    DocsEnum de = sr.termPositionsEnum(new Term("field", "withPayload"));
     de.nextDoc();
     de.nextPosition();
     assertEquals(new BytesRef("test"), de.getPayload());
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestPayloadsOnVectors.java b/lucene/core/src/test/org/apache/lucene/index/TestPayloadsOnVectors.java
index 3fa9a81..080c3b3 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestPayloadsOnVectors.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestPayloadsOnVectors.java
@@ -72,7 +72,7 @@
     assert terms != null;
     TermsEnum termsEnum = terms.iterator(null);
     assertTrue(termsEnum.seekExact(new BytesRef("withPayload")));
-    DocsAndPositionsEnum de = termsEnum.docsAndPositions(null, null);
+    DocsEnum de = termsEnum.docsAndPositions(null, null);
     assertEquals(0, de.nextDoc());
     assertEquals(0, de.nextPosition());
     assertEquals(new BytesRef("test"), de.getPayload());
@@ -114,7 +114,7 @@
     assert terms != null;
     TermsEnum termsEnum = terms.iterator(null);
     assertTrue(termsEnum.seekExact(new BytesRef("withPayload")));
-    DocsAndPositionsEnum de = termsEnum.docsAndPositions(null, null);
+    DocsEnum de = termsEnum.docsAndPositions(null, null);
     assertEquals(0, de.nextDoc());
     assertEquals(3, de.nextPosition());
     assertEquals(new BytesRef("test"), de.getPayload());
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestPostingsOffsets.java b/lucene/core/src/test/org/apache/lucene/index/TestPostingsOffsets.java
index f7eac40..3693365 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestPostingsOffsets.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestPostingsOffsets.java
@@ -17,12 +17,6 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.CannedTokenStream;
 import org.apache.lucene.analysis.MockAnalyzer;
@@ -45,6 +39,12 @@
 import org.apache.lucene.util.LuceneTestCase;
 import org.apache.lucene.util.TestUtil;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 // TODO: we really need to test indexingoffsets, but then getting only docs / docs + freqs.
 // not all codecs store prx separate...
 // TODO: fix sep codec to index offsets so we can greatly reduce this list!
@@ -82,7 +82,7 @@
     IndexReader r = w.getReader();
     w.close();
 
-    DocsAndPositionsEnum dp = MultiFields.getTermPositionsEnum(r, null, "content", new BytesRef("a"));
+    DocsEnum dp = MultiFields.getTermPositionsEnum(r, null, "content", new BytesRef("a"));
     assertNotNull(dp);
     assertEquals(0, dp.nextDoc());
     assertEquals(2, dp.freq());
@@ -154,7 +154,7 @@
     String terms[] = { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "hundred" };
     
     for (String term : terms) {
-      DocsAndPositionsEnum dp = MultiFields.getTermPositionsEnum(reader, null, "numbers", new BytesRef(term));
+      DocsEnum dp = MultiFields.getTermPositionsEnum(reader, null, "numbers", new BytesRef(term));
       int doc;
       while((doc = dp.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
         String storedNumbers = reader.document(doc).get("numbers");
@@ -182,7 +182,7 @@
     
     for (int j = 0; j < numSkippingTests; j++) {
       int num = TestUtil.nextInt(random(), 100, Math.min(numDocs - 1, 999));
-      DocsAndPositionsEnum dp = MultiFields.getTermPositionsEnum(reader, null, "numbers", new BytesRef("hundred"));
+      DocsEnum dp = MultiFields.getTermPositionsEnum(reader, null, "numbers", new BytesRef("hundred"));
       int doc = dp.advance(num);
       assertEquals(num, doc);
       int freq = dp.freq();
@@ -295,8 +295,8 @@
       //System.out.println("\nsub=" + sub);
       final TermsEnum termsEnum = sub.fields().terms("content").iterator(null);
       DocsEnum docs = null;
-      DocsAndPositionsEnum docsAndPositions = null;
-      DocsAndPositionsEnum docsAndPositionsAndOffsets = null;
+      DocsEnum docsAndPositions = null;
+      DocsEnum docsAndPositionsAndOffsets = null;
       final NumericDocValues docIDToID = DocValues.getNumeric(sub, "id");
       for(String term : terms) {
         //System.out.println("  term=" + term);
@@ -313,7 +313,7 @@
           }
 
           // explicitly exclude offsets here
-          docsAndPositions = termsEnum.docsAndPositions(null, docsAndPositions, DocsAndPositionsEnum.FLAG_PAYLOADS);
+          docsAndPositions = termsEnum.docsAndPositions(null, docsAndPositions, DocsEnum.FLAG_PAYLOADS);
           assertNotNull(docsAndPositions);
           //System.out.println("    doc/freq/pos");
           while((doc = docsAndPositions.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestSegmentReader.java b/lucene/core/src/test/org/apache/lucene/index/TestSegmentReader.java
index 918b915..4a34aa5 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestSegmentReader.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestSegmentReader.java
@@ -145,7 +145,7 @@
     assertTrue(termDocs.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
 
     
-    DocsAndPositionsEnum positions = MultiFields.getTermPositionsEnum(reader,
+    DocsEnum positions = MultiFields.getTermPositionsEnum(reader,
                                                                       MultiFields.getLiveDocs(reader),
                                                                       DocHelper.TEXT_FIELD_1_KEY,
                                                                       new BytesRef("field"));
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestStressIndexing2.java b/lucene/core/src/test/org/apache/lucene/index/TestStressIndexing2.java
index 6203f1e..922da09 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestStressIndexing2.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestStressIndexing2.java
@@ -396,7 +396,7 @@
         Fields tv1 = r1.getTermVectors(id1);
         System.out.println("  d1=" + tv1);
         if (tv1 != null) {
-          DocsAndPositionsEnum dpEnum = null;
+          DocsEnum dpEnum = null;
           DocsEnum dEnum = null;
           for (String field : tv1) {
             System.out.println("    " + field + ":");
@@ -428,7 +428,7 @@
         Fields tv2 = r2.getTermVectors(id2);
         System.out.println("  d2=" + tv2);
         if (tv2 != null) {
-          DocsAndPositionsEnum dpEnum = null;
+          DocsEnum dpEnum = null;
           DocsEnum dEnum = null;
           for (String field : tv2) {
             System.out.println("    " + field + ":");
@@ -616,8 +616,8 @@
       assertNotNull(terms2);
       TermsEnum termsEnum2 = terms2.iterator(null);
 
-      DocsAndPositionsEnum dpEnum1 = null;
-      DocsAndPositionsEnum dpEnum2 = null;
+      DocsEnum dpEnum1 = null;
+      DocsEnum dpEnum2 = null;
       DocsEnum dEnum1 = null;
       DocsEnum dEnum2 = null;
       
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestTermVectorsReader.java b/lucene/core/src/test/org/apache/lucene/index/TestTermVectorsReader.java
index a8adfb7..92bab48 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestTermVectorsReader.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestTermVectorsReader.java
@@ -247,7 +247,7 @@
     assertNotNull(vector);
     assertEquals(testTerms.length, vector.size());
     TermsEnum termsEnum = vector.iterator(null);
-    DocsAndPositionsEnum dpEnum = null;
+    DocsEnum dpEnum = null;
     for (int i = 0; i < testTerms.length; i++) {
       final BytesRef text = termsEnum.next();
       assertNotNull(text);
@@ -304,7 +304,7 @@
     TermsEnum termsEnum = vector.iterator(null);
     assertNotNull(termsEnum);
     assertEquals(testTerms.length, vector.size());
-    DocsAndPositionsEnum dpEnum = null;
+    DocsEnum dpEnum = null;
     for (int i = 0; i < testTerms.length; i++) {
       final BytesRef text = termsEnum.next();
       assertNotNull(text);
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestTermVectorsWriter.java b/lucene/core/src/test/org/apache/lucene/index/TestTermVectorsWriter.java
index bfa5963..71dfb8d 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestTermVectorsWriter.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestTermVectorsWriter.java
@@ -68,7 +68,7 @@
     // Token "" occurred once
     assertEquals(1, termsEnum.totalTermFreq());
 
-    DocsAndPositionsEnum dpEnum = termsEnum.docsAndPositions(null, null);
+    DocsEnum dpEnum = termsEnum.docsAndPositions(null, null);
     assertTrue(dpEnum.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
     dpEnum.nextPosition();
     assertEquals(8, dpEnum.startOffset());
@@ -117,7 +117,7 @@
     IndexReader r = DirectoryReader.open(dir);
     TermsEnum termsEnum = r.getTermVectors(0).terms("field").iterator(null);
     assertNotNull(termsEnum.next());
-    DocsAndPositionsEnum dpEnum = termsEnum.docsAndPositions(null, null);
+    DocsEnum dpEnum = termsEnum.docsAndPositions(null, null);
     assertEquals(2, termsEnum.totalTermFreq());
 
     assertTrue(dpEnum.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
@@ -152,7 +152,7 @@
     IndexReader r = DirectoryReader.open(dir);
     TermsEnum termsEnum = r.getTermVectors(0).terms("field").iterator(null);
     assertNotNull(termsEnum.next());
-    DocsAndPositionsEnum dpEnum = termsEnum.docsAndPositions(null, null);
+    DocsEnum dpEnum = termsEnum.docsAndPositions(null, null);
     assertEquals(2, termsEnum.totalTermFreq());
 
     assertTrue(dpEnum.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
@@ -192,7 +192,7 @@
     IndexReader r = DirectoryReader.open(dir);
     TermsEnum termsEnum = r.getTermVectors(0).terms("field").iterator(null);
     assertNotNull(termsEnum.next());
-    DocsAndPositionsEnum dpEnum = termsEnum.docsAndPositions(null, null);
+    DocsEnum dpEnum = termsEnum.docsAndPositions(null, null);
     assertEquals(2, termsEnum.totalTermFreq());
 
     assertTrue(dpEnum.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
@@ -227,7 +227,7 @@
     IndexReader r = DirectoryReader.open(dir);
     TermsEnum termsEnum = r.getTermVectors(0).terms("field").iterator(null);
     assertNotNull(termsEnum.next());
-    DocsAndPositionsEnum dpEnum = termsEnum.docsAndPositions(null, null);
+    DocsEnum dpEnum = termsEnum.docsAndPositions(null, null);
     assertEquals(2, termsEnum.totalTermFreq());
 
     assertTrue(dpEnum.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
@@ -263,7 +263,7 @@
     IndexReader r = DirectoryReader.open(dir);
     TermsEnum termsEnum = r.getTermVectors(0).terms("field").iterator(null);
     assertNotNull(termsEnum.next());
-    DocsAndPositionsEnum dpEnum = termsEnum.docsAndPositions(null, null);
+    DocsEnum dpEnum = termsEnum.docsAndPositions(null, null);
 
     assertTrue(dpEnum.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
     dpEnum.nextPosition();
@@ -307,7 +307,7 @@
     IndexReader r = DirectoryReader.open(dir);
     TermsEnum termsEnum = r.getTermVectors(0).terms("field").iterator(null);
     assertNotNull(termsEnum.next());
-    DocsAndPositionsEnum dpEnum = termsEnum.docsAndPositions(null, null);
+    DocsEnum dpEnum = termsEnum.docsAndPositions(null, null);
 
     assertEquals(1, (int) termsEnum.totalTermFreq());
     assertTrue(dpEnum.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
@@ -349,7 +349,7 @@
     IndexReader r = DirectoryReader.open(dir);
     TermsEnum termsEnum = r.getTermVectors(0).terms("field").iterator(null);
     assertNotNull(termsEnum.next());
-    DocsAndPositionsEnum dpEnum = termsEnum.docsAndPositions(null, null);
+    DocsEnum dpEnum = termsEnum.docsAndPositions(null, null);
 
     assertEquals(1, (int) termsEnum.totalTermFreq());
     assertTrue(dpEnum.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
diff --git a/lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java b/lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java
index cf11e26..bbfbc0a 100644
--- a/lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java
+++ b/lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java
@@ -19,10 +19,11 @@
 
 import java.io.IOException;
 
+import org.apache.lucene.index.FieldInvertState;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.search.similarities.Similarity;
 import org.apache.lucene.util.Bits;
-import org.apache.lucene.index.FieldInvertState;
+import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.PriorityQueue;
 
 /**
@@ -215,6 +216,36 @@
     }
 
     @Override
+    public int nextPosition() throws IOException {
+      throw new UnsupportedOperationException(UNSUPPORTED_MSG);
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      throw new UnsupportedOperationException(UNSUPPORTED_MSG);
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      throw new UnsupportedOperationException(UNSUPPORTED_MSG);
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      throw new UnsupportedOperationException(UNSUPPORTED_MSG);
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      throw new UnsupportedOperationException(UNSUPPORTED_MSG);
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      throw new UnsupportedOperationException(UNSUPPORTED_MSG);
+    }
+
+    @Override
     public int docID() {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
@@ -228,7 +259,7 @@
     public int advance(int target) {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
-    
+
     @Override
     public long cost() {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
@@ -319,7 +350,7 @@
     }
 
     @Override
-    public Scorer scorer(LeafReaderContext context, Bits acceptDocs) {
+    public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
     
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestBooleanCoord.java b/lucene/core/src/test/org/apache/lucene/search/TestBooleanCoord.java
index 5a9dbdd..b60072a 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestBooleanCoord.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestBooleanCoord.java
@@ -17,14 +17,12 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.util.concurrent.atomic.AtomicBoolean;
-
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.document.StringField;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInvertState;
 import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.index.IndexWriterConfig;
@@ -36,6 +34,9 @@
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
 /** 
  * Tests coord() computation by BooleanQuery
  */
@@ -707,7 +708,7 @@
   private void assertScore(final float expected, Query query) throws Exception {
     // test in-order
     Weight weight = searcher.createNormalizedWeight(query);
-    Scorer scorer = weight.scorer(reader.leaves().get(0), null);
+    Scorer scorer = weight.scorer(reader.leaves().get(0), DocsEnum.FLAG_FREQS,  null);
     assertTrue(scorer.docID() == -1 || scorer.docID() == DocIdSetIterator.NO_MORE_DOCS);
     assertEquals(0, scorer.nextDoc());
     assertEquals(expected, scorer.score(), 0.0001f);
@@ -715,7 +716,7 @@
     // test out-of-order (if supported)
     if (weight.scoresDocsOutOfOrder()) {
       final AtomicBoolean seen = new AtomicBoolean(false);
-      BulkScorer bulkScorer = weight.bulkScorer(reader.leaves().get(0), false, null);
+      BulkScorer bulkScorer = weight.bulkScorer(reader.leaves().get(0), false, DocsEnum.FLAG_FREQS, null);
       assertNotNull(bulkScorer);
       bulkScorer.score(new LeafCollector() {
         Scorer scorer;
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestBooleanOr.java b/lucene/core/src/test/org/apache/lucene/search/TestBooleanOr.java
index bf07fe6..1e1cbc6 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestBooleanOr.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestBooleanOr.java
@@ -15,12 +15,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import java.io.IOException;
-import java.util.concurrent.atomic.AtomicInteger;
-
 import org.apache.lucene.analysis.MockAnalyzer;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.TextField;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.RandomIndexWriter;
 import org.apache.lucene.index.Term;
@@ -29,6 +27,9 @@
 import org.apache.lucene.util.LuceneTestCase;
 import org.apache.lucene.util.TestUtil;
 
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicInteger;
+
 public class TestBooleanOr extends LuceneTestCase {
 
   private static String FIELD_T = "T";
@@ -182,7 +183,7 @@
     Weight w = s.createNormalizedWeight(bq);
 
     assertEquals(1, s.getIndexReader().leaves().size());
-    BulkScorer scorer = w.bulkScorer(s.getIndexReader().leaves().get(0), false, null);
+    BulkScorer scorer = w.bulkScorer(s.getIndexReader().leaves().get(0), false, DocsEnum.FLAG_FREQS, null);
 
     final FixedBitSet hits = new FixedBitSet(docCount);
     final AtomicInteger end = new AtomicInteger();
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestBooleanQuery.java b/lucene/core/src/test/org/apache/lucene/search/TestBooleanQuery.java
index 937597a..48cd6ef 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestBooleanQuery.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestBooleanQuery.java
@@ -17,20 +17,13 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.MockAnalyzer;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.document.TextField;
 import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.index.IndexWriterConfig;
@@ -46,6 +39,14 @@
 import org.apache.lucene.util.NamedThreadFactory;
 import org.apache.lucene.util.TestUtil;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
 public class TestBooleanQuery extends LuceneTestCase {
   
   public void testEquality() throws Exception {
@@ -235,7 +236,7 @@
 
       Weight weight = s.createNormalizedWeight(q);
 
-      Scorer scorer = weight.scorer(s.leafContexts.get(0), null);
+      Scorer scorer = weight.scorer(s.leafContexts.get(0), DocsEnum.FLAG_FREQS, null);
 
       // First pass: just use .nextDoc() to gather all hits
       final List<ScoreDoc> hits = new ArrayList<>();
@@ -252,7 +253,7 @@
       for(int iter2=0;iter2<10;iter2++) {
 
         weight = s.createNormalizedWeight(q);
-        scorer = weight.scorer(s.leafContexts.get(0), null);
+        scorer = weight.scorer(s.leafContexts.get(0), DocsEnum.FLAG_FREQS, null);
 
         if (VERBOSE) {
           System.out.println("  iter2=" + iter2);
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestBooleanQueryVisitSubscorers.java b/lucene/core/src/test/org/apache/lucene/search/TestBooleanQueryVisitSubscorers.java
index 9f8568c..d30b78f 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestBooleanQueryVisitSubscorers.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestBooleanQueryVisitSubscorers.java
@@ -30,9 +30,9 @@
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field.Store;
 import org.apache.lucene.document.TextField;
-import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.RandomIndexWriter;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.BooleanClause.Occur;
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java b/lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java
index a4ffa0d..e4fa73c 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java
@@ -17,12 +17,6 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.document.TextField;
@@ -35,6 +29,12 @@
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.LuceneTestCase;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
 public class TestBooleanScorer extends LuceneTestCase {
   private static final String FIELD = "category";
   
@@ -207,12 +207,12 @@
         }
 
         @Override
-        public Scorer scorer(LeafReaderContext context, Bits acceptDocs) {
+        public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) {
           throw new UnsupportedOperationException();
         }
 
         @Override
-        public BulkScorer bulkScorer(LeafReaderContext context, boolean scoreDocsInOrder, Bits acceptDocs) {
+        public BulkScorer bulkScorer(LeafReaderContext context, boolean scoreDocsInOrder, int flags, Bits acceptDocs) {
           return new BulkScorer() {
 
             @Override
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestCachingCollector.java b/lucene/core/src/test/org/apache/lucene/search/TestCachingCollector.java
index e842909..1113188 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestCachingCollector.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestCachingCollector.java
@@ -19,6 +19,7 @@
 
 import java.io.IOException;
 
+import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.LuceneTestCase;
 
 public class TestCachingCollector extends LuceneTestCase {
@@ -38,6 +39,36 @@
     public int freq() throws IOException { return 0; }
 
     @Override
+    public int nextPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
+    @Override
     public int docID() { return 0; }
 
     @Override
@@ -45,7 +76,7 @@
 
     @Override
     public int advance(int target) throws IOException { return 0; }
-    
+
     @Override
     public long cost() {
       return 1;
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestConstantScoreQuery.java b/lucene/core/src/test/org/apache/lucene/search/TestConstantScoreQuery.java
index 10a1893..9236797 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestConstantScoreQuery.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestConstantScoreQuery.java
@@ -17,6 +17,8 @@
  * limitations under the License.
  */
 
+import java.io.IOException;
+
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.index.IndexReader;
@@ -26,8 +28,6 @@
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.util.LuceneTestCase;
 
-import java.io.IOException;
-
 /** This class only tests some basic functionality in CSQ, the main parts are mostly
  * tested by MultiTermQuery tests, explanations seems to be tested in TestExplanations! */
 public class TestConstantScoreQuery extends LuceneTestCase {
@@ -56,9 +56,9 @@
       public void setScorer(Scorer scorer) {
         this.scorer = scorer;
         assertEquals("Scorer is implemented by wrong class", scorerClassName, scorer.getClass().getName());
-        if (innerScorerClassName != null && scorer instanceof ConstantScoreQuery.ConstantScorer) {
-          final ConstantScoreQuery.ConstantScorer innerScorer = (ConstantScoreQuery.ConstantScorer) scorer;
-          assertEquals("inner Scorer is implemented by wrong class", innerScorerClassName, innerScorer.docIdSetIterator.getClass().getName());
+        if (innerScorerClassName != null && scorer instanceof ConstantScoreQuery.ConstantScoreScorer) {
+          final ConstantScoreQuery.ConstantScoreScorer innerScorer = (ConstantScoreQuery.ConstantScoreScorer) scorer;
+          assertEquals("inner Scorer is implemented by wrong class", innerScorerClassName, innerScorer.in.getClass().getName());
         }
       }
       
@@ -113,13 +113,13 @@
       final Query csqbq = new ConstantScoreQuery(bq);
       csqbq.setBoost(17.0f);
       
-      checkHits(searcher, csq1, csq1.getBoost(), ConstantScoreQuery.ConstantScorer.class.getName(), null);
-      checkHits(searcher, csq2, csq2.getBoost(), ConstantScoreQuery.ConstantScorer.class.getName(), ConstantScoreQuery.ConstantScorer.class.getName());
+      checkHits(searcher, csq1, csq1.getBoost(), ConstantScoreQuery.ConstantScoreScorer.class.getName(), null);
+      checkHits(searcher, csq2, csq2.getBoost(), ConstantScoreQuery.ConstantScoreScorer.class.getName(), ConstantScoreQuery.ConstantScoreScorer.class.getName());
       
       // for the combined BQ, the scorer should always be BooleanScorer's BucketScorer, because our scorer supports out-of order collection!
       final String bucketScorerClass = FakeScorer.class.getName();
       checkHits(searcher, bq, csq1.getBoost() + csq2.getBoost(), bucketScorerClass, null);
-      checkHits(searcher, csqbq, csqbq.getBoost(), ConstantScoreQuery.ConstantScorer.class.getName(), bucketScorerClass);
+      checkHits(searcher, csqbq, csqbq.getBoost(), ConstantScoreQuery.ConstantScoreScorer.class.getName(), bucketScorerClass);
     } finally {
       if (reader != null) reader.close();
       if (directory != null) directory.close();
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestDisjunctionMaxQuery.java b/lucene/core/src/test/org/apache/lucene/search/TestDisjunctionMaxQuery.java
index 2817bf5..f75c0bd 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestDisjunctionMaxQuery.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestDisjunctionMaxQuery.java
@@ -17,21 +17,21 @@
  * limitations under the License.
  */
 
-import org.apache.lucene.document.Field;
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.util.LuceneTestCase;
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.MockAnalyzer;
 import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
 import org.apache.lucene.document.FieldType;
 import org.apache.lucene.document.TextField;
 import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.index.FieldInvertState;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.index.IndexWriterConfig;
-import org.apache.lucene.index.SlowCompositeReaderWrapper;
-import org.apache.lucene.index.FieldInvertState;
+import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.index.SlowCompositeReaderWrapper;
 import org.apache.lucene.index.StoredDocument;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.similarities.DefaultSimilarity;
@@ -39,11 +39,12 @@
 import org.apache.lucene.search.spans.SpanQuery;
 import org.apache.lucene.search.spans.SpanTermQuery;
 import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.LuceneTestCase;
 
+import java.io.IOException;
 import java.text.DecimalFormat;
 import java.text.DecimalFormatSymbols;
 import java.util.Locale;
-import java.io.IOException;
 
 /**
  * Test of the DisjunctionMaxQuery.
@@ -180,7 +181,7 @@
     assertTrue(s.getTopReaderContext() instanceof LeafReaderContext);
     final Weight dw = s.createNormalizedWeight(dq);
     LeafReaderContext context = (LeafReaderContext)s.getTopReaderContext();
-    final Scorer ds = dw.scorer(context, context.reader().getLiveDocs());
+    final Scorer ds = dw.scorer(context, DocsEnum.FLAG_FREQS, context.reader().getLiveDocs());
     final boolean skipOk = ds.advance(3) != DocIdSetIterator.NO_MORE_DOCS;
     if (skipOk) {
       fail("firsttime skipTo found a match? ... "
@@ -196,7 +197,7 @@
     QueryUtils.check(random(), dq, s);
     final Weight dw = s.createNormalizedWeight(dq);
     LeafReaderContext context = (LeafReaderContext)s.getTopReaderContext();
-    final Scorer ds = dw.scorer(context, context.reader().getLiveDocs());
+    final Scorer ds = dw.scorer(context, DocsEnum.FLAG_FREQS, context.reader().getLiveDocs());
     assertTrue("firsttime skipTo found no match",
         ds.advance(3) != DocIdSetIterator.NO_MORE_DOCS);
     assertEquals("found wrong docid", "d4", r.document(ds.docID()).get("id"));
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestMinShouldMatch2.java b/lucene/core/src/test/org/apache/lucene/search/TestMinShouldMatch2.java
index 2216ed5..11922e0 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestMinShouldMatch2.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestMinShouldMatch2.java
@@ -29,8 +29,9 @@
 import org.apache.lucene.document.Field;
 import org.apache.lucene.document.SortedSetDocValuesField;
 import org.apache.lucene.document.StringField;
-import org.apache.lucene.index.LeafReader;
 import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.index.LeafReader;
 import org.apache.lucene.index.RandomIndexWriter;
 import org.apache.lucene.index.SortedSetDocValues;
 import org.apache.lucene.index.Term;
@@ -124,7 +125,7 @@
     if (slow) {
       return new SlowMinShouldMatchScorer(weight, reader, searcher);
     } else {
-      return weight.scorer(reader.getContext(), null);
+      return weight.scorer(reader.getContext(), DocsEnum.FLAG_FREQS, null);
     }
   }
   
@@ -315,6 +316,36 @@
     }
 
     @Override
+    public int nextPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
+    @Override
     public int docID() {
       return currentDoc;
     }
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestPhrasePrefixQuery.java b/lucene/core/src/test/org/apache/lucene/search/TestPhrasePrefixQuery.java
index 26cf76a..3de48e9 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestPhrasePrefixQuery.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestPhrasePrefixQuery.java
@@ -17,20 +17,20 @@
  * limitations under the License.
  */
 
-import org.apache.lucene.document.Field;
-import org.apache.lucene.util.LuceneTestCase;
-import org.apache.lucene.document.Document;
-import org.apache.lucene.index.RandomIndexWriter;
-import org.apache.lucene.index.TermsEnum;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.index.MultiFields;
-import org.apache.lucene.util.BytesRef;
-import org.apache.lucene.store.Directory;
-
 import java.io.IOException;
 import java.util.LinkedList;
 
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.MultiFields;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermsEnum;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.LuceneTestCase;
+
 /**
  * This class tests PhrasePrefixQuery class.
  */
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestPhraseQuery.java b/lucene/core/src/test/org/apache/lucene/search/TestPhraseQuery.java
index 68e958e..4ac6bff 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestPhraseQuery.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestPhraseQuery.java
@@ -17,17 +17,27 @@
  * limitations under the License.
  */
 
-import java.io.*;
-import java.util.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
 
-import org.apache.lucene.analysis.*;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.MockAnalyzer;
+import org.apache.lucene.analysis.MockTokenFilter;
+import org.apache.lucene.analysis.MockTokenizer;
+import org.apache.lucene.analysis.TokenStream;
 import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
-import org.apache.lucene.document.*;
-import org.apache.lucene.index.*;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.IndexWriterConfig.OpenMode;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.index.Term;
 import org.apache.lucene.search.similarities.DefaultSimilarity;
 import org.apache.lucene.store.Directory;
-import org.apache.lucene.util.*;
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.util.TestUtil;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 
@@ -35,7 +45,7 @@
  * Tests {@link PhraseQuery}.
  *
  * @see TestPositionIncrement
- */ 
+ */
 public class TestPhraseQuery extends LuceneTestCase {
 
   /** threshold for comparing floats */
@@ -184,7 +194,7 @@
    * slop is the total number of positional moves allowed
    * to line up a phrase
    */
-  public void testMulipleTerms() throws Exception {
+  public void testMultipleTerms() throws Exception {
     query.setSlop(2);
     query.add(new Term("field", "one"));
     query.add(new Term("field", "three"));
@@ -670,7 +680,7 @@
         }
       }
 
-      assertTrue("phrase '" + sb + "' not found; start=" + start, found);
+      assertTrue("phrase '" + sb + "' not found; start=" + start + ", it=" + i + ", expected doc " + docID, found);
     }
 
     reader.close();
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestPositionIncrement.java b/lucene/core/src/test/org/apache/lucene/search/TestPositionIncrement.java
index 6086ff6..27d8822 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestPositionIncrement.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestPositionIncrement.java
@@ -31,7 +31,7 @@
 import org.apache.lucene.document.TextField;
 import org.apache.lucene.index.LeafReader;
 import org.apache.lucene.index.MultiFields;
-import org.apache.lucene.index.DocsAndPositionsEnum;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.RandomIndexWriter;
 import org.apache.lucene.index.SlowCompositeReaderWrapper;
@@ -100,7 +100,7 @@
 
     IndexSearcher searcher = newSearcher(reader);
     
-    DocsAndPositionsEnum pos = MultiFields.getTermPositionsEnum(searcher.getIndexReader(),
+    DocsEnum pos = MultiFields.getTermPositionsEnum(searcher.getIndexReader(),
                                                                 MultiFields.getLiveDocs(searcher.getIndexReader()),
                                                                 "field",
                                                                 new BytesRef("1"));
@@ -212,7 +212,7 @@
     final IndexReader readerFromWriter = writer.getReader();
     LeafReader r = SlowCompositeReaderWrapper.wrap(readerFromWriter);
 
-    DocsAndPositionsEnum tp = r.termPositionsEnum(new Term("content", "a"));
+    DocsEnum tp = r.termPositionsEnum(new Term("content", "a"));
     
     int count = 0;
     assertTrue(tp.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestPositiveScoresOnlyCollector.java b/lucene/core/src/test/org/apache/lucene/search/TestPositiveScoresOnlyCollector.java
index 4a51978..5b3807d 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestPositiveScoresOnlyCollector.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestPositiveScoresOnlyCollector.java
@@ -17,12 +17,15 @@
  * limitations under the License.
  */
 
+import java.io.IOException;
+
+import org.apache.lucene.document.Document;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.RandomIndexWriter;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.LuceneTestCase;
-import org.apache.lucene.document.Document;
 
 public class TestPositiveScoresOnlyCollector extends LuceneTestCase {
 
@@ -41,6 +44,36 @@
       return 1;
     }
 
+    @Override
+    public int nextPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
     @Override public int docID() { return idx; }
 
     @Override public int nextDoc() {
@@ -51,7 +84,7 @@
       idx = target;
       return idx < scores.length ? idx : NO_MORE_DOCS;
     }
-    
+
     @Override
     public long cost() {
       return scores.length;
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestQueryRescorer.java b/lucene/core/src/test/org/apache/lucene/search/TestQueryRescorer.java
index 20e337c..0e404b1 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestQueryRescorer.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestQueryRescorer.java
@@ -25,8 +25,8 @@
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.document.NumericDocValuesField;
-import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.RandomIndexWriter;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.BooleanClause.Occur;
@@ -36,6 +36,7 @@
 import org.apache.lucene.search.spans.SpanTermQuery;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.LuceneTestCase;
 import org.apache.lucene.util.TestUtil;
 
@@ -443,7 +444,7 @@
         }
 
         @Override
-        public Scorer scorer(final LeafReaderContext context, Bits acceptDocs) throws IOException {
+        public Scorer scorer(final LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
 
           return new Scorer(null) {
             int docID = -1;
@@ -459,6 +460,36 @@
             }
 
             @Override
+            public int nextPosition() throws IOException {
+              return NO_MORE_POSITIONS;
+            }
+
+            @Override
+            public int startPosition() throws IOException {
+              return NO_MORE_POSITIONS;
+            }
+
+            @Override
+            public int endPosition() throws IOException {
+              return NO_MORE_POSITIONS;
+            }
+
+            @Override
+            public int startOffset() throws IOException {
+              return -1;
+            }
+
+            @Override
+            public int endOffset() throws IOException {
+              return -1;
+            }
+
+            @Override
+            public BytesRef getPayload() throws IOException {
+              return null;
+            }
+
+            @Override
             public long cost() {
               return 1;
             }
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestScoreCachingWrappingScorer.java b/lucene/core/src/test/org/apache/lucene/search/TestScoreCachingWrappingScorer.java
index b082da9..fc4c794 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestScoreCachingWrappingScorer.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestScoreCachingWrappingScorer.java
@@ -23,6 +23,7 @@
 import org.apache.lucene.index.RandomIndexWriter;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.LuceneTestCase;
 
 public class TestScoreCachingWrappingScorer extends LuceneTestCase {
@@ -47,6 +48,36 @@
       return 1;
     }
 
+    @Override
+    public int nextPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
     @Override public int docID() { return doc; }
 
     @Override public int nextDoc() {
@@ -57,7 +88,7 @@
       doc = target;
       return doc < scores.length ? doc : NO_MORE_DOCS;
     }
-    
+
     @Override
     public long cost() {
       return scores.length;
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestSearchWithThreads.java b/lucene/core/src/test/org/apache/lucene/search/TestSearchWithThreads.java
index cd5ac03..58de70d 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestSearchWithThreads.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestSearchWithThreads.java
@@ -20,16 +20,18 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 
+import com.carrotsearch.randomizedtesting.annotations.Seed;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.RandomIndexWriter;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.store.Directory;
-import org.apache.lucene.util.LuceneTestCase.SuppressCodecs;
 import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.util.LuceneTestCase.SuppressCodecs;
 
 @SuppressCodecs({ "SimpleText", "Memory", "Direct" })
+@Seed("12017F5C55C9DD62")
 public class TestSearchWithThreads extends LuceneTestCase {
   int NUM_DOCS;
   final int NUM_SEARCH_THREADS = 5;
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestSimpleExplanations.java b/lucene/core/src/test/org/apache/lucene/search/TestSimpleExplanations.java
index afbabc2..ec9b4de 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestSimpleExplanations.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestSimpleExplanations.java
@@ -242,6 +242,13 @@
   }
   
   /* MultiPhraseQuery */
+
+  /*
+      "w1 w2 w3 w4 w5",
+    "w1 w3 w2 w3 zz",
+    "w1 xx w2 yy w3",
+    "w1 w3 xx w2 yy w3 zz"
+   */
   
   public void testMPQ1() throws Exception {
     MultiPhraseQuery q = new MultiPhraseQuery();
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestSloppyPhraseQuery.java b/lucene/core/src/test/org/apache/lucene/search/TestSloppyPhraseQuery.java
index 5b97ade2..18f5715 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestSloppyPhraseQuery.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestSloppyPhraseQuery.java
@@ -19,7 +19,7 @@
 
 import java.io.IOException;
 
-import org.apache.lucene.util.LuceneTestCase;
+import com.carrotsearch.randomizedtesting.annotations.Seed;
 import org.apache.lucene.analysis.MockAnalyzer;
 import org.apache.lucene.analysis.MockTokenizer;
 import org.apache.lucene.document.Document;
@@ -32,7 +32,9 @@
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.MockDirectoryWrapper;
 import org.apache.lucene.store.RAMDirectory;
+import org.apache.lucene.util.LuceneTestCase;
 
+@Seed("2542F68A58928060")
 public class TestSloppyPhraseQuery extends LuceneTestCase {
 
   private static final String S_1 = "A A A";
@@ -144,7 +146,7 @@
     IndexReader reader = writer.getReader();
 
     IndexSearcher searcher = newSearcher(reader);
-    MaxFreqCollector c = new MaxFreqCollector();
+    MaxScoreCollector c = new MaxScoreCollector();
     searcher.search(query, c);
     assertEquals("slop: "+slop+"  query: "+query+"  doc: "+doc+"  Wrong number of hits", expectedNumResults, c.totalHits);
 
@@ -176,7 +178,7 @@
     return query;
   }
 
-  static class MaxFreqCollector extends SimpleCollector {
+  static class MaxScoreCollector extends SimpleCollector {
     float max;
     int totalHits;
     Scorer scorer;
@@ -189,7 +191,7 @@
     @Override
     public void collect(int doc) throws IOException {
       totalHits++;
-      max = Math.max(max, scorer.freq());
+      max = Math.max(max, scorer.score());
     }
 
     @Override
@@ -210,7 +212,6 @@
       
       @Override
       public void collect(int doc) throws IOException {
-        assertFalse(Float.isInfinite(scorer.freq()));
         assertFalse(Float.isInfinite(scorer.score()));
       }
       
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestTermScorer.java b/lucene/core/src/test/org/apache/lucene/search/TestTermScorer.java
index 1a37f96..baa3600 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestTermScorer.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestTermScorer.java
@@ -17,13 +17,10 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
 import org.apache.lucene.analysis.MockAnalyzer;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.RandomIndexWriter;
@@ -33,6 +30,10 @@
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.util.LuceneTestCase;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
 public class TestTermScorer extends LuceneTestCase {
   protected Directory directory;
   private static final String FIELD = "field";
@@ -78,7 +79,7 @@
     Weight weight = indexSearcher.createNormalizedWeight(termQuery);
     assertTrue(indexSearcher.getTopReaderContext() instanceof LeafReaderContext);
     LeafReaderContext context = (LeafReaderContext)indexSearcher.getTopReaderContext();
-    BulkScorer ts = weight.bulkScorer(context, true, context.reader().getLiveDocs());
+    BulkScorer ts = weight.bulkScorer(context, true, DocsEnum.FLAG_FREQS, context.reader().getLiveDocs());
     // we have 2 documents with the term all in them, one document for all the
     // other values
     final List<TestHit> docs = new ArrayList<>();
@@ -140,7 +141,7 @@
     Weight weight = indexSearcher.createNormalizedWeight(termQuery);
     assertTrue(indexSearcher.getTopReaderContext() instanceof LeafReaderContext);
     LeafReaderContext context = (LeafReaderContext) indexSearcher.getTopReaderContext();
-    Scorer ts = weight.scorer(context, context.reader().getLiveDocs());
+    Scorer ts = weight.scorer(context, DocsEnum.FLAG_FREQS, context.reader().getLiveDocs());
     assertTrue("next did not return a doc",
         ts.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
     assertTrue("score is not correct", ts.score() == 1.6931472f);
@@ -159,7 +160,7 @@
     Weight weight = indexSearcher.createNormalizedWeight(termQuery);
     assertTrue(indexSearcher.getTopReaderContext() instanceof LeafReaderContext);
     LeafReaderContext context = (LeafReaderContext) indexSearcher.getTopReaderContext();
-    Scorer ts = weight.scorer(context, context.reader().getLiveDocs());
+    Scorer ts = weight.scorer(context, DocsEnum.FLAG_FREQS, context.reader().getLiveDocs());
     assertTrue("Didn't skip", ts.advance(3) != DocIdSetIterator.NO_MORE_DOCS);
     // The next doc should be doc 5
     assertTrue("doc should be number 5", ts.docID() == 5);
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestTermVectors.java b/lucene/core/src/test/org/apache/lucene/search/TestTermVectors.java
index bac1e39..4ca9c34 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestTermVectors.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestTermVectors.java
@@ -17,8 +17,6 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-
 import org.apache.lucene.analysis.MockAnalyzer;
 import org.apache.lucene.analysis.MockTokenizer;
 import org.apache.lucene.document.Document;
@@ -36,6 +34,8 @@
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 
+import java.io.IOException;
+
 public class TestTermVectors extends LuceneTestCase {
   private static IndexReader reader;
   private static Directory directory;
diff --git a/lucene/core/src/test/org/apache/lucene/search/posfilter/IntervalTestBase.java b/lucene/core/src/test/org/apache/lucene/search/posfilter/IntervalTestBase.java
new file mode 100644
index 0000000..8431957
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/search/posfilter/IntervalTestBase.java
@@ -0,0 +1,235 @@
+package org.apache.lucene.search.posfilter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.util.Locale;
+
+import org.apache.lucene.analysis.MockAnalyzer;
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.index.SlowCompositeReaderWrapper;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.CheckHits;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.search.Weight;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.LuceneTestCase;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+
+public abstract class IntervalTestBase extends LuceneTestCase {
+
+  protected Directory directory;
+  protected IndexReader reader;
+  protected IndexSearcher searcher;
+
+  enum AssertionType { POSITIONS, OFFSETS }
+
+  protected static void checkMatches(Scorer scorer, int[][] expectedResults, AssertionType type) throws IOException {
+
+    int doc;
+    int docUpto = -1;
+
+    while ((doc = scorer.nextDoc()) != DocsEnum.NO_MORE_DOCS) {
+
+      docUpto++;
+      if (doc != expectedResults[docUpto][0])
+        fail("Expected next hit in document " + expectedResults[docUpto][0] + " but was in " + doc);
+
+      int posUpto = 0;
+      while (scorer.nextPosition() != DocsEnum.NO_MORE_POSITIONS) {
+
+        if (posUpto > ((expectedResults[docUpto].length - 1) / 2) - 1)
+          fail("Unexpected hit in document " + doc + ": " + scorer.toString());
+
+        if (type == AssertionType.POSITIONS) {
+          if (expectedResults[docUpto][posUpto * 2 + 1] != scorer.startPosition() ||
+              expectedResults[docUpto][posUpto * 2 + 2] != scorer.endPosition())
+            fail("Expected next position in document " + doc + " to be [" + expectedResults[docUpto][posUpto * 2 + 1] + ", " +
+                expectedResults[docUpto][posUpto * 2 + 2] + "] but was [" + scorer.startPosition() + ", " + scorer.endPosition() + "]");
+        } else {
+          // check offsets
+          if (expectedResults[docUpto][posUpto * 2 + 1] != scorer.startOffset() ||
+              expectedResults[docUpto][posUpto * 2 + 2] != scorer.endOffset())
+            fail("Expected next offset in document to be [" + expectedResults[docUpto][posUpto * 2 + 1] + ", " +
+                expectedResults[docUpto][posUpto * 2 + 2] + "] but was [" + scorer.startOffset() + ", " + scorer.endOffset() + "]");
+        }
+
+        posUpto++;
+      }
+
+      if (posUpto < (expectedResults[docUpto].length - 1) / 2)
+        fail("Missing expected hit in document " + expectedResults[docUpto][0] + ": [" +
+            expectedResults[docUpto][posUpto] + ", " + expectedResults[docUpto][posUpto + 1] + "]");
+
+    }
+
+    if (docUpto < expectedResults.length - 1)
+      fail("Missing expected match to document " + expectedResults[docUpto + 1][0]);
+
+  }
+
+  /**
+   * Run a query against a searcher, and check that the collected intervals from the query match
+   * the expected results.
+   * @param q the query
+   * @param searcher the searcher
+   * @param expectedResults an int[][] detailing the expected results, in the format
+   *                        { { docid1, startoffset1, endoffset1, startoffset2, endoffset2, ... },
+   *                          { docid2, startoffset1, endoffset1, startoffset2, endoffset2, ...}, ... }
+   * @throws IOException
+   */
+  public static void checkIntervalOffsets(Query q, IndexSearcher searcher, int[][] expectedResults) throws IOException {
+
+    Weight weight = searcher.createNormalizedWeight(q);
+    LeafReaderContext ctx = (LeafReaderContext) searcher.getTopReaderContext();
+    Scorer scorer = weight.scorer(ctx, DocsEnum.FLAG_OFFSETS, ctx.reader().getLiveDocs());
+    checkMatches(scorer, expectedResults, AssertionType.OFFSETS);
+
+  }
+
+  /**
+   * Run a query against a searcher, and check that the collected intervals from the query match
+   * the expected results.
+   * @param q the query
+   * @param searcher the searcher
+   * @param expectedResults an int[][] detailing the expected results, in the format
+   *                        { { docid1, startpos1, endpos1, startpos2, endpos2, ... },
+   *                          { docid2, startpos1, endpos1, startpos2, endpos2, ...}, ... }
+   * @throws IOException
+   */
+  public static void checkIntervals(Query q, IndexSearcher searcher, int[][] expectedResults) throws IOException {
+
+    Weight weight = searcher.createNormalizedWeight(q);
+    LeafReaderContext ctx = (LeafReaderContext) searcher.getTopReaderContext();
+    Scorer scorer = weight.scorer(ctx, DocsEnum.FLAG_POSITIONS, ctx.reader().getLiveDocs());
+    checkMatches(scorer, expectedResults, AssertionType.POSITIONS);
+
+  }
+
+  public static void checkScores(Query q, IndexSearcher searcher, int... expectedDocs) throws IOException {
+    TopDocs hits = searcher.search(q, 1000);
+    Assert.assertEquals("Wrong number of hits", expectedDocs.length, hits.totalHits);
+    for (int i = 0; i < expectedDocs.length; i++) {
+      Assert.assertEquals("Docs not scored in order", expectedDocs[i], hits.scoreDocs[i].doc);
+    }
+    CheckHits.checkExplanations(q, "field", searcher);
+  }
+
+  protected abstract void addDocs(RandomIndexWriter writer) throws IOException;
+
+  @Before
+  public void setUp() throws Exception {
+    super.setUp();
+    directory = newDirectory();
+    IndexWriterConfig config = newIndexWriterConfig(new MockAnalyzer(random()));
+    //config.setCodec(Codec.forName("SimpleText"));
+    //config.setCodec(Codec.forName("Asserting"));
+    RandomIndexWriter writer = new RandomIndexWriter(random(), directory, config);
+    addDocs(writer);
+    reader = SlowCompositeReaderWrapper.wrap(writer.getReader());
+    writer.close();
+    searcher = new IndexSearcher(reader);
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    reader.close();
+    directory.close();
+    super.tearDown();
+  }
+
+  public TermQuery makeTermQuery(String text) {
+    return new TermQuery(new Term(TestBasicIntervals.field, text));
+  }
+
+  protected Query makeOrQuery(Query... queries) {
+    BooleanQuery q = new BooleanQuery();
+    for (Query subquery : queries) {
+      q.add(subquery, BooleanClause.Occur.SHOULD);
+    }
+    return q;
+  }
+
+  protected Query makeAndQuery(Query... queries) {
+    BooleanQuery q = new BooleanQuery();
+    for (Query subquery : queries) {
+      q.add(subquery, BooleanClause.Occur.MUST);
+    }
+    return q;
+  }
+
+  protected Query makeBooleanQuery(BooleanClause... clauses) {
+    BooleanQuery q = new BooleanQuery();
+    for (BooleanClause clause : clauses) {
+      q.add(clause);
+    }
+    return q;
+  }
+
+  protected BooleanClause makeBooleanClause(String text, BooleanClause.Occur occur) {
+    return new BooleanClause(makeTermQuery(text), occur);
+  }
+
+  public static class Match implements Comparable<Match> {
+
+    public final int docid;
+    public final int start;
+    public final int end;
+    public final int startOffset;
+    public final int endOffset;
+    public final boolean composite;
+
+    public Match(int docid, Interval interval, boolean composite) {
+      this.docid = docid;
+      this.start = interval.begin;
+      this.end = interval.end;
+      this.startOffset = interval.offsetBegin;
+      this.endOffset = interval.offsetEnd;
+      this.composite = composite;
+    }
+
+    @Override
+    public int compareTo(Match o) {
+      if (this.docid != o.docid)
+        return this.docid - o.docid;
+      if (this.start != o.start)
+        return this.start - o.start;
+      return o.end - this.end;
+    }
+
+    @Override
+    public String toString() {
+      return String.format(Locale.ROOT, "%d:%d[%d]->%d[%d]%s",
+                            docid, start, startOffset, end, endOffset, composite ? "C" : "");
+    }
+  }
+
+
+}
diff --git a/lucene/core/src/test/org/apache/lucene/search/posfilter/TestBasicIntervals.java b/lucene/core/src/test/org/apache/lucene/search/posfilter/TestBasicIntervals.java
new file mode 100644
index 0000000..01f7380
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/search/posfilter/TestBasicIntervals.java
@@ -0,0 +1,191 @@
+package org.apache.lucene.search.posfilter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.TextField;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Query;
+
+public class TestBasicIntervals extends IntervalTestBase {
+
+  public static final String field = "field";
+
+  @Override
+  protected void addDocs(RandomIndexWriter writer) throws IOException {
+    for (String content : docFields) {
+      Document doc = new Document();
+      doc.add(newField(field, content, TextField.TYPE_NOT_STORED));
+      writer.addDocument(doc);
+    }
+  }
+  
+  private String[] docFields = {
+      "w1 w2 w3 w4 w5", //0
+      "w1 w3 w2 w3",//1
+      "w1 xx w2 yy w3",//2
+      "w1 w3 xx w2 yy w3",//3
+      "u2 u2 u1", //4
+      "u2 xx u2 u1",//5
+      "u2 u2 xx u1", //6
+      "u2 xx u2 yy u1", //7
+      "u2 xx u1 u2",//8
+      "u1 u2 xx u2",//9
+      "u2 u1 xx u2",//10
+      "t1 t2 t1 t3 t2 t3",//11
+      "v1 v2 v3",//12
+      "v1 v3 v2 v3 v4",//13
+      "v4 v2 v2 v4",//14
+      "v3 v4 v3"};//15
+
+  public void testSimpleConjunction() throws IOException {
+    Query q = makeAndQuery(makeTermQuery("v2"), makeTermQuery("v4"));
+    checkIntervals(q, searcher, new int[][]{
+        { 13, 2, 2, 4, 4 },
+        { 14, 0, 0, 1, 1, 2, 2, 3, 3 }
+    });
+  }
+
+  public void testExclusion() throws IOException {
+    Query q = makeBooleanQuery(makeBooleanClause("v2", BooleanClause.Occur.MUST),
+                               makeBooleanClause("v3", BooleanClause.Occur.MUST_NOT));
+    checkIntervals(q, searcher, new int[][]{
+        { 14, 1, 1, 2, 2 }
+    });
+  }
+
+  public void testOptExclusion() throws IOException {
+    Query q = makeBooleanQuery(makeBooleanClause("w2", BooleanClause.Occur.SHOULD),
+                               makeBooleanClause("w3", BooleanClause.Occur.SHOULD),
+                               makeBooleanClause("xx", BooleanClause.Occur.MUST_NOT));
+    checkIntervals(q, searcher, new int[][]{
+        { 0, 1, 1, 2, 2 },
+        { 1, 1, 1, 2, 2, 3, 3 }
+    });
+  }
+
+  public void testNestedConjunctions() throws IOException {
+    Query q = makeAndQuery(makeTermQuery("v2"), makeOrQuery(makeTermQuery("v3"), makeTermQuery("v4")));
+    checkIntervals(q, searcher, new int[][]{
+        { 12, 1, 1, 2, 2 },
+        { 13, 1, 1, 2, 2, 3, 3, 4, 4 },
+        { 14, 0, 0, 1, 1, 2, 2, 3, 3 }
+    });
+  }
+
+  public void testSingleRequiredManyOptional() throws IOException {
+    Query q = makeBooleanQuery(makeBooleanClause("v2", BooleanClause.Occur.MUST),
+                               makeBooleanClause("v3", BooleanClause.Occur.SHOULD),
+                               makeBooleanClause("v4", BooleanClause.Occur.SHOULD));
+    checkIntervals(q, searcher, new int[][]{
+        { 12, 1, 1, 2, 2 },
+        { 13, 1, 1, 2, 2, 3, 3, 4, 4 },
+        { 14, 0, 0, 1, 1, 2, 2, 3, 3 }
+    });
+  }
+
+  public void testSimpleTerm() throws IOException {
+    Query q = makeTermQuery("u2");
+    checkIntervals(q, searcher, new int[][]{
+        { 4, 0, 0, 1, 1 },
+        { 5, 0, 0, 2, 2 },
+        { 6, 0, 0, 1, 1 },
+        { 7, 0, 0, 2, 2 },
+        { 8, 0, 0, 3, 3 },
+        { 9, 1, 1, 3, 3 },
+        { 10, 0, 0, 3, 3 }
+    });
+  }
+
+  public void testBasicDisjunction() throws IOException {
+    Query q = makeOrQuery(makeTermQuery("v3"), makeTermQuery("v2"));
+    checkIntervals(q, searcher, new int[][]{
+        { 12, 1, 1, 2, 2 },
+        { 13, 1, 1, 2, 2, 3, 3 },
+        { 14, 1, 1, 2, 2 },
+        { 15, 0, 0, 2, 2 }
+    });
+  }
+
+
+  
+  public void testOrSingle() throws Exception {
+    Query q = makeOrQuery(makeTermQuery("w5"));
+    checkIntervals(q, searcher, new int[][]{
+        { 0, 4, 4 }
+    });
+  }
+
+  public void testOrPartialMatch() throws Exception {
+    Query q = makeOrQuery(makeTermQuery("w5"), makeTermQuery("xx"));
+    checkIntervals(q, searcher, new int[][]{
+        { 0, 4, 4 },
+        { 2, 1, 1 },
+        { 3, 2, 2 },
+        { 5, 1, 1 },
+        { 6, 2, 2 },
+        { 7, 1, 1 },
+        { 8, 1, 1 },
+        { 9, 2, 2 },
+        { 10, 2, 2 },
+    });
+  }
+
+  public void testOrDisjunctionMatch() throws Exception {
+    Query q = makeOrQuery(makeTermQuery("w5"), makeTermQuery("yy"));
+    checkIntervals(q, searcher, new int[][]{
+        { 0, 4, 4 },
+        { 2, 3, 3 },
+        { 3, 4, 4 },
+        { 7, 3, 3 }
+    });
+  }
+
+  // "t1 t2 t1 t3 t2 t3"
+  //  -----------
+  //     --------
+  //        --------
+  public void testOrSingleDocument() throws Exception {
+    Query q = makeOrQuery(makeTermQuery("t1"), makeTermQuery("t2"), makeTermQuery("t3"));
+    checkIntervals(q, searcher, new int[][]{
+        { 11, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 }
+    });
+  }
+
+  // andnot(andnot(w1, or(w2, flurble)), or(foo, bar))
+  public void testConjunctionExclusionQuery() throws IOException {
+    BooleanQuery andnotinner = new BooleanQuery();
+    andnotinner.add(makeTermQuery("w1"), BooleanClause.Occur.MUST);
+    BooleanQuery andnotinneror = new BooleanQuery();
+    andnotinneror.add(makeTermQuery("w2"), BooleanClause.Occur.SHOULD);
+    andnotinneror.add(makeTermQuery("flurble"), BooleanClause.Occur.SHOULD);
+    andnotinner.add(andnotinneror, BooleanClause.Occur.MUST_NOT);
+    BooleanQuery outer = new BooleanQuery();
+    outer.add(andnotinner, BooleanClause.Occur.MUST);
+    BooleanQuery andnotouteror = new BooleanQuery();
+    andnotouteror.add(makeTermQuery("foo"), BooleanClause.Occur.SHOULD);
+    andnotouteror.add(makeTermQuery("bar"), BooleanClause.Occur.SHOULD);
+    outer.add(andnotouteror, BooleanClause.Occur.MUST_NOT);
+    checkIntervals(outer, searcher, new int[][]{});
+  }
+  
+}
diff --git a/lucene/core/src/test/org/apache/lucene/search/posfilter/TestBrouwerianQuery.java b/lucene/core/src/test/org/apache/lucene/search/posfilter/TestBrouwerianQuery.java
new file mode 100644
index 0000000..b54211c
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/search/posfilter/TestBrouwerianQuery.java
@@ -0,0 +1,103 @@
+package org.apache.lucene.search.posfilter;
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.TextField;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.search.Query;
+
+public class TestBrouwerianQuery extends IntervalTestBase {
+  
+  protected void addDocs(RandomIndexWriter writer) throws IOException {
+    {
+      Document doc = new Document();
+      doc.add(newField(
+          "field",
+          "The quick brown fox jumps over the lazy dog",
+              TextField.TYPE_STORED));
+      writer.addDocument(doc);
+    }
+    
+    {
+      Document doc = new Document();
+      doc.add(newField(
+          "field",
+          "The quick brown duck jumps over the lazy dog with the quick brown fox jumps",
+              TextField.TYPE_STORED));
+      writer.addDocument(doc);
+    }
+  }
+  
+  public void testBrouwerianBooleanQuery() throws IOException {
+
+    Query query = new OrderedNearQuery(2, makeTermQuery("the"),
+                                        makeTermQuery("quick"), makeTermQuery("jumps"));
+    Query sub = makeTermQuery("fox");
+    NonOverlappingQuery q = new NonOverlappingQuery(query, sub);
+
+    checkIntervals(q, searcher, new int[][]{
+        { 1, 0, 4 }
+    });
+  }
+
+  public void testBrouwerianBooleanQueryExcludedDoesNotExist() throws IOException {
+
+    Query query = new OrderedNearQuery(2, makeTermQuery("the"),
+        makeTermQuery("quick"), makeTermQuery("jumps"));
+    Query sub = makeTermQuery("blox");
+    NonOverlappingQuery q = new NonOverlappingQuery(query, sub);
+
+    checkIntervals(q, searcher, new int[][]{
+        { 0, 0, 4 },
+        { 1, 0, 4, 10, 14 }
+    });
+  }
+
+  public void testBrouwerianOverlapQuery() throws IOException {
+    // We want to find 'jumps NOT WITHIN 2 positions of fox'
+    Query sub = new UnorderedNearQuery(2, makeTermQuery("jumps"), makeTermQuery("fox"));
+    Query query = makeTermQuery("jumps");
+    NonOverlappingQuery q = new NonOverlappingQuery(query, sub);
+
+    checkIntervals(q, searcher, new int[][]{
+        { 1, 4, 4 }
+    });
+  }
+
+  public void testBrouwerianNonExistentOverlapQuery() throws IOException {
+    Query sub = new UnorderedNearQuery(2, makeTermQuery("dog"), makeTermQuery("over"));
+    Query query = makeTermQuery("dog");
+    NonOverlappingQuery q = new NonOverlappingQuery(query, sub);
+
+    checkIntervals(q, searcher, new int[][]{});
+  }
+
+  public void testBrouwerianExistentOverlapQuery() throws IOException {
+    Query sub = new UnorderedNearQuery(1, makeTermQuery("dog"), makeTermQuery("over"));
+    Query query = makeTermQuery("dog");
+    NonOverlappingQuery q = new NonOverlappingQuery(query, sub);
+
+    checkIntervals(q, searcher, new int[][]{
+        { 0, 8, 8 },
+        { 1, 8, 8 }
+    });
+  }
+
+}
diff --git a/lucene/core/src/test/org/apache/lucene/search/posfilter/TestIntervalScoring.java b/lucene/core/src/test/org/apache/lucene/search/posfilter/TestIntervalScoring.java
new file mode 100644
index 0000000..7dbac6f
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/search/posfilter/TestIntervalScoring.java
@@ -0,0 +1,69 @@
+package org.apache.lucene.search.posfilter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.TextField;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.RegexpQuery;
+import org.apache.lucene.search.TopDocs;
+
+public class TestIntervalScoring extends IntervalTestBase {
+
+  @Override
+  protected void addDocs(RandomIndexWriter writer) throws IOException {
+    for (String content : docFields) {
+      Document doc = new Document();
+      doc.add(newField("field", content, TextField.TYPE_NOT_STORED));
+      writer.addDocument(doc);
+    }
+  }
+
+  private String[] docFields = {
+      "Should we could we would we?",
+      "It should -  would it?",
+      "It shouldn't",
+      "Should we should we should we"
+  };
+
+  public void testOrderedNearQueryScoring() throws IOException {
+    OrderedNearQuery q = new OrderedNearQuery(10, makeTermQuery("should"),
+                                                  makeTermQuery("would"));
+    checkScores(q, searcher, 1, 0);
+  }
+
+  public void testUnorderedNearQueryScoring() throws IOException {
+
+    Query q = new UnorderedNearQuery(10, makeTermQuery("we"), makeTermQuery("should"));
+    checkScores(q, searcher, 3, 0);
+
+  }
+
+  public void testEmptyMultiTermQueryScoring() throws IOException {
+    OrderedNearQuery q = new OrderedNearQuery(10, new RegexpQuery(new Term("field", "bar.*")),
+                                                  new RegexpQuery(new Term("field", "foo.*")));
+    TopDocs docs = searcher.search(q, 10);
+    assertEquals(docs.totalHits, 0);
+  }
+
+
+}
diff --git a/lucene/core/src/test/org/apache/lucene/search/posfilter/TestNestedPositionFilterQueries.java b/lucene/core/src/test/org/apache/lucene/search/posfilter/TestNestedPositionFilterQueries.java
new file mode 100644
index 0000000..c93ad5b
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/search/posfilter/TestNestedPositionFilterQueries.java
@@ -0,0 +1,122 @@
+package org.apache.lucene.search.posfilter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.TextField;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Query;
+
+import java.io.IOException;
+
+public class TestNestedPositionFilterQueries extends IntervalTestBase {
+
+  @Override
+  protected void addDocs(RandomIndexWriter writer) throws IOException {
+    for (int i = 0; i < docFields.length; i++) {
+      Document doc = new Document();
+      doc.add(newField("field", docFields[i], TextField.TYPE_STORED));
+      writer.addDocument(doc);
+    }
+  }
+
+  private String[] docFields = {
+    "w1 w2 w3 w4 w5 w6 w7 w8 w9 w10 w11 w12", //0
+    "w1 w3 w4 w5 w6 w7 w8", //1
+    "w1 w3 w10 w4 w5 w6 w7 w8", //2
+    "w1 w3 w2 w4 w5 w6 w7 w8", //3
+  };
+
+  public void testOrderedDisjunctionQueries() throws IOException {
+    // Two phrases whose subparts appear in a document, but that do not fulfil the slop
+    // requirements of the parent IntervalFilterQuery
+    Query sentence1 = new OrderedNearQuery(0, makeTermQuery("w1"), makeTermQuery("w8"), makeTermQuery("w4"));
+    Query sentence2 = new OrderedNearQuery(0, makeTermQuery("w3"), makeTermQuery("w7"), makeTermQuery("w6"));
+    BooleanQuery bq = new BooleanQuery();
+    bq.add(sentence1, BooleanClause.Occur.SHOULD);
+    bq.add(sentence2, BooleanClause.Occur.SHOULD);
+    checkIntervals(bq, searcher, new int[][]{});
+  }
+
+  public void testFilterDisjunctionQuery() throws IOException {
+    Query near1 = makeTermQuery("w4");
+    Query near2 = new OrderedNearQuery(3, makeTermQuery("w1"), makeTermQuery("w10"));
+    BooleanQuery bq = new BooleanQuery();
+    bq.add(near1, BooleanClause.Occur.SHOULD);
+    bq.add(near2, BooleanClause.Occur.SHOULD);
+    checkIntervals(bq, searcher, new int[][]{
+        { 0, 3, 3 },
+        { 1, 2, 2 },
+        { 2, 0, 2, 3, 3 },
+        { 3, 3, 3 }
+    });
+  }
+
+  // or(w1 pre/2 w2, w1 pre/3 w10)
+  public void testOrNearNearQuery() throws IOException {
+    Query near1 = new OrderedNearQuery(2, makeTermQuery("w1"), makeTermQuery("w2"));
+    Query near2 = new OrderedNearQuery(3, makeTermQuery("w1"), makeTermQuery("w10"));
+    BooleanQuery bq = new BooleanQuery();
+    bq.add(near1, BooleanClause.Occur.SHOULD);
+    bq.add(near2, BooleanClause.Occur.SHOULD);
+    checkIntervals(bq, searcher, new int[][]{
+        { 0, 0, 1 },
+        { 2, 0, 2 },
+        { 3, 0, 2 }
+    });
+  }
+
+  // or(w2 within/2 w1, w10 within/3 w1)
+  public void testUnorderedNearNearQuery() throws IOException {
+    Query near1 = new UnorderedNearQuery(2, makeTermQuery("w2"), makeTermQuery("w1"));
+    Query near2 = new UnorderedNearQuery(3, makeTermQuery("w10"), makeTermQuery("w1"));
+    BooleanQuery bq = new BooleanQuery();
+    bq.add(near1, BooleanClause.Occur.SHOULD);
+    bq.add(near2, BooleanClause.Occur.SHOULD);
+    checkIntervals(bq, searcher, new int[][]{
+        {0, 0, 1},
+        {2, 0, 2},
+        {3, 0, 2}
+    });
+  }
+
+  // (a pre/2 b) pre/6 (c pre/2 d)
+  public void testNearNearNearQuery() throws IOException {
+    Query near1 = new OrderedNearQuery(2, makeTermQuery("w1"), makeTermQuery("w4"));
+    Query near2 = new OrderedNearQuery(2, makeTermQuery("w10"), makeTermQuery("w12"));
+    Query near3 = new OrderedNearQuery(6, near1, near2);
+    checkIntervals(near3, searcher, new int[][]{
+        { 0, 0, 11 }
+    });
+  }
+
+  public void testOrNearNearNonExistentQuery() throws IOException {
+    Query near1 = new OrderedNearQuery(2, makeTermQuery("w1"), makeTermQuery("w12"));
+    Query near2 = new OrderedNearQuery(2, makeTermQuery("w3"), makeTermQuery("w8"));
+    BooleanQuery bq = new BooleanQuery();
+    bq.add(near1, BooleanClause.Occur.SHOULD);
+    bq.add(near2, BooleanClause.Occur.SHOULD);
+    BooleanQuery wrapper = new BooleanQuery();
+    wrapper.add(bq, BooleanClause.Occur.MUST);
+    wrapper.add(makeTermQuery("foo"), BooleanClause.Occur.MUST_NOT);
+    checkIntervals(wrapper, searcher, new int[][]{});
+  }
+
+}
diff --git a/lucene/core/src/test/org/apache/lucene/search/posfilter/TestPhraseQueryPositions.java b/lucene/core/src/test/org/apache/lucene/search/posfilter/TestPhraseQueryPositions.java
new file mode 100644
index 0000000..1cd181d
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/search/posfilter/TestPhraseQueryPositions.java
@@ -0,0 +1,191 @@
+package org.apache.lucene.search.posfilter;
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.TextField;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.MultiPhraseQuery;
+import org.apache.lucene.search.PhraseQuery;
+
+public class TestPhraseQueryPositions extends IntervalTestBase {
+  
+  protected void addDocs(RandomIndexWriter writer) throws IOException {
+    {
+      Document doc = new Document();
+      doc.add(newField(
+          "field",
+        //  0       1      2     3     4       5     6      7      8   9  10   11   12   13
+          "Pease porridge hot! Pease porridge cold! Pease porridge in the pot nine days old! "
+        //  14   15  16 17    18   19  20  21    22   23  24 25 26  27   28   29   30
+        + "Some like it hot, some like it cold, Some like it in the pot nine days old! "
+        //  31      32     33    34     35     36
+        + "Pease porridge hot! Pease porridge cold!",
+              TextField.TYPE_STORED));
+      writer.addDocument(doc);
+    }
+    
+    {
+      Document doc = new Document();
+      doc.add(newField(
+          "field",
+        //  0       1      2     3     4       5     6      7      8   9  10   11   12   13
+          "Pease porridge cold! Pease porridge hot! Pease porridge in the pot nine days old! "
+        //  14   15  16 17    18   19  20  21    22   23  24 25 26  27   28   29   30
+        + "Some like it cold, some like it hot, Some like it in the pot nine days old! "
+        //  31      32     33    34     35     36
+        + "Pease porridge cold! Pease porridge hot!",
+          TextField.TYPE_STORED));
+      writer.addDocument(doc);
+    }
+    {
+      Document doc = new Document();
+      doc.add(newField("sloppy", "x a x b a", TextField.TYPE_STORED));
+      writer.addDocument(doc);
+    }
+  }
+
+  public void testOutOfOrderSloppyPhraseQuery() throws IOException {
+    PhraseQuery query = new PhraseQuery();
+    query.add(new Term("field", "pease"));
+    query.add(new Term("field", "cold!"));
+    query.add(new Term("field", "porridge"));
+    query.setSlop(2);
+    checkIntervals(query, searcher, new int[][]{
+        {0, 3, 5, 3, 7, 5, 7, 34, 36},
+        {1, 0, 2, 0, 4, 2, 4, 31, 33, 31, 35, 33, 35 }
+    });
+  }
+
+  public void testOverlappingOutOfOrderSloppyPhraseQuery() throws IOException {
+    PhraseQuery query = new PhraseQuery();
+    query.add(new Term("sloppy", "x"));
+    query.add(new Term("sloppy", "a"));
+    query.add(new Term("sloppy", "a"));
+    query.setSlop(2);
+    checkIntervals(query, searcher, new int[][]{
+        {2, 0, 4, 1, 4}
+    });
+  }
+
+  public void testSloppyPhraseQuery() throws IOException {
+    PhraseQuery query = new PhraseQuery();
+    query.add(new Term("field", "pease"));
+    query.add(new Term("field", "hot!"));
+    query.setSlop(1);
+    checkIntervals(query, searcher, new int[][]{
+        {0, 0, 2, 31, 33},
+        {1, 3, 5, 34, 36}
+    });
+  }
+
+  public void testSloppyPhraseQueryWithRepeats() throws IOException {
+    PhraseQuery query = new PhraseQuery();
+    query.add(new Term("field", "pease"));
+    query.add(new Term("field", "porridge"));
+    query.add(new Term("field", "pease"));
+    query.setSlop(1);
+    checkIntervals(query, searcher, new int[][]{
+        {0, 0, 3, 3, 6, 31, 34},
+        {1, 0, 3, 3, 6, 31, 34}
+    });
+  }
+
+  public void testManyTermSloppyPhraseQuery() throws IOException {
+    PhraseQuery query = new PhraseQuery();
+    query.add(new Term("field", "pease"));
+    query.add(new Term("field", "porridge"));
+    query.add(new Term("field", "pot"));
+    query.setSlop(2);
+    checkIntervals(query, searcher, new int[][]{
+        {0, 6, 10},
+        {1, 6, 10}
+    });
+  }
+
+  public void testMultiPhrases() throws IOException {
+
+    MultiPhraseQuery q = new MultiPhraseQuery();
+    q.add(new Term("field", "pease"));
+    q.add(new Term("field", "porridge"));
+    q.add(new Term[]{ new Term("field", "hot!"), new Term("field", "cold!") });
+
+    checkIntervals(q, searcher, new int[][]{
+        { 0, 0, 2, 3, 5, 31, 33, 34, 36 },
+        { 1, 0, 2, 3, 5, 31, 33, 34, 36 }
+    });
+  }
+
+  public void testOverlaps() throws IOException {
+    PhraseQuery q = new PhraseQuery();
+    q.add(new Term("field", "some"));
+    q.add(new Term("field", "like"));
+    q.add(new Term("field", "it"));
+    q.add(new Term("field", "cold,"));
+    q.add(new Term("field", "some"));
+    q.add(new Term("field", "like"));
+    checkIntervals(q, searcher, new int[][]{
+        {0, 18, 23},
+        {1, 14, 19}
+    });
+  }
+
+  public void testMatching() throws IOException {
+
+    PhraseQuery q = new PhraseQuery();
+    q.add(new Term("field", "pease"));
+    q.add(new Term("field", "porridge"));
+    q.add(new Term("field", "hot!"));
+
+    checkIntervals(q, searcher, new int[][]{
+        {0, 0, 2, 31, 33},
+        {1, 3, 5, 34, 36}
+    });
+
+  }
+
+  public void testPartialMatching() throws IOException {
+
+    PhraseQuery q = new PhraseQuery();
+    q.add(new Term("field", "pease"));
+    q.add(new Term("field", "porridge"));
+    q.add(new Term("field", "hot!"));
+    q.add(new Term("field", "pease"));
+    q.add(new Term("field", "porridge"));
+    q.add(new Term("field", "cold!"));
+
+    checkIntervals(q, searcher, new int[][]{
+        {0, 0, 5, 31, 36},
+    });
+
+  }
+
+  public void testNonMatching() throws IOException {
+
+    PhraseQuery q = new PhraseQuery();
+    q.add(new Term("field", "pease"));
+    q.add(new Term("field", "hot!"));
+
+    checkIntervals(q, searcher, new int[][]{});
+
+  }
+
+
+}
diff --git a/lucene/core/src/test/org/apache/lucene/search/posfilter/TestPositionFilteredIntervals.java b/lucene/core/src/test/org/apache/lucene/search/posfilter/TestPositionFilteredIntervals.java
new file mode 100644
index 0000000..65d9ab3
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/search/posfilter/TestPositionFilteredIntervals.java
@@ -0,0 +1,224 @@
+package org.apache.lucene.search.posfilter;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.TextField;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Query;
+
+public class TestPositionFilteredIntervals extends IntervalTestBase {
+
+  public static final String field = "field";
+
+  @Override
+  protected void addDocs(RandomIndexWriter writer) throws IOException {
+    for (String content : docFields) {
+      Document doc = new Document();
+      doc.add(newField(field, content, TextField.TYPE_NOT_STORED));
+      writer.addDocument(doc);
+    }
+  }
+
+  private String[] docFields = {
+      "w1 w2 w3 w4 w5", //0
+      "w1 w3 w2 w3",//1
+      "w1 xx w2 yy w3",//2
+      "w1 w3 xx w2 yy w3",//3
+      "u2 u2 u1", //4
+      "u2 xx u2 u1",//5
+      "u2 u2 xx u1", //6
+      "u2 xx u2 yy u1", //7
+      "u2 xx u1 u2",//8
+      "u1 u2 xx u2",//9
+      "u2 u1 xx u2",//10
+      "t1 t2 t1 t3 t2 t3",//11
+      "v1 v2 v3",//12
+      "v1 v3 v2 v3 v4",//13
+      "v4 v2 v2 v4",//14
+      "v3 v4 v3"};//15
+
+  public void testNearOrdered01() throws Exception {
+    Query q = new OrderedNearQuery(0, makeTermQuery("w1"), makeTermQuery("w2"), makeTermQuery("w3"));
+    checkIntervals(q, searcher, new int[][]{
+        { 0, 0, 2 }
+    });
+  }
+
+  public void testNearOrdered02() throws Exception {
+    Query q = new OrderedNearQuery(1, makeTermQuery("w1"), makeTermQuery("w2"), makeTermQuery("w3"));
+    checkIntervals(q, searcher, new int[][]{
+        { 0, 0, 2 },
+        { 1, 0, 3 }
+    });
+  }
+
+  public void testNearOrdered03() throws Exception {
+    Query q = new OrderedNearQuery(2, makeTermQuery("w1"), makeTermQuery("w2"), makeTermQuery("w3"));
+    checkIntervals(q, searcher, new int[][]{
+        { 0, 0, 2 },
+        { 1, 0, 3 },
+        { 2, 0, 4 }
+    });
+  }
+
+  public void testNearOrdered04() throws Exception {
+    Query q = new OrderedNearQuery(3, makeTermQuery("w1"), makeTermQuery("w2"), makeTermQuery("w3"));
+    checkIntervals(q, searcher, new int[][]{
+        { 0, 0, 2 },
+        { 1, 0, 3 },
+        { 2, 0, 4 },
+        { 3, 0, 5 }
+    });
+  }
+
+  public void testNearOrdered05() throws Exception {
+    Query q = new OrderedNearQuery(4, makeTermQuery("w1"), makeTermQuery("w2"), makeTermQuery("w3"));
+    checkIntervals(q, searcher, new int[][]{
+        { 0, 0, 2 },
+        { 1, 0, 3 },
+        { 2, 0, 4 },
+        { 3, 0, 5 }
+    });
+  }
+
+  public void testNearOrderedEqual01() throws Exception {
+    Query q = new OrderedNearQuery(0, makeTermQuery("w1"), makeTermQuery("w3"), makeTermQuery("w3"));
+    checkIntervals(q, searcher, new int[][]{});
+  }
+
+  public void testNearOrderedEqual02() throws Exception {
+    Query q = new OrderedNearQuery(1, makeTermQuery("w1"), makeTermQuery("w3"), makeTermQuery("w3"));
+    checkIntervals(q, searcher, new int[][]{
+        { 1, 0, 3 }
+    });
+  }
+
+  public void testNearOrderedEqual03() throws Exception {
+    Query q = new OrderedNearQuery(2, makeTermQuery("w1"), makeTermQuery("w3"), makeTermQuery("w3"));
+    checkIntervals(q, searcher, new int[][]{
+        { 1, 0, 3 }
+    });
+  }
+
+  public void testNearOrderedEqual04() throws Exception {
+    Query q = new OrderedNearQuery(3, makeTermQuery("w1"), makeTermQuery("w3"), makeTermQuery("w3"));
+    checkIntervals(q, searcher, new int[][]{
+        { 1, 0, 3 },
+        { 3, 0, 5 }
+    });
+  }
+
+  public void testNearOrderedEqual11() throws Exception {
+    Query q = new OrderedNearQuery(0, makeTermQuery("u2"), makeTermQuery("u2"), makeTermQuery("u1"));
+    checkIntervals(q, searcher, new int[][]{
+        { 4, 0, 2 }
+    });
+  }
+
+  public void testNearOrderedEqual13() throws Exception {
+    Query q = new OrderedNearQuery(1, makeTermQuery("u2"), makeTermQuery("u2"), makeTermQuery("u1"));
+    checkIntervals(q, searcher, new int[][]{
+        { 4, 0, 2 },
+        { 5, 0, 3 },
+        { 6, 0, 3 }
+    });
+  }
+
+  public void testNearOrderedEqual14() throws Exception {
+    Query q = new OrderedNearQuery(2, makeTermQuery("u2"), makeTermQuery("u2"), makeTermQuery("u1"));
+    checkIntervals(q, searcher, new int[][]{
+        { 4, 0, 2 },
+        { 5, 0, 3 },
+        { 6, 0, 3 },
+        { 7, 0, 4 }
+    });
+  }
+
+  public void testNearOrderedEqual15() throws Exception {
+    Query q = new OrderedNearQuery(3, makeTermQuery("u2"), makeTermQuery("u2"), makeTermQuery("u1"));
+    checkIntervals(q, searcher, new int[][]{
+        { 4, 0, 2 },
+        { 5, 0, 3 },
+        { 6, 0, 3 },
+        { 7, 0, 4 }
+    });
+  }
+
+  public void testNearOrderedOverlap() throws Exception {
+    Query q = new OrderedNearQuery(3, makeTermQuery("t1"), makeTermQuery("t2"), makeTermQuery("t3"));
+    checkIntervals(q, searcher, new int[][]{
+        { 11, 0, 3, 2, 5 }
+    });
+  }
+
+  public void testNearUnordered() throws Exception {
+    Query q = new UnorderedNearQuery(0, makeTermQuery("u1"), makeTermQuery("u2"));
+    checkIntervals(q, searcher, new int[][]{
+        { 4, 1, 2 },
+        { 5, 2, 3 },
+        { 8, 2, 3 },
+        { 9, 0, 1 },
+        { 10, 0, 1 }
+    });
+  }
+
+  public void testMultipleNearUnordered() throws Exception {
+    Query q = new UnorderedNearQuery(1, makeTermQuery("w1"), makeTermQuery("w2"), makeTermQuery("w3"));
+    checkIntervals(q, searcher, new int[][]{
+        { 0, 0, 2 },
+        { 1, 0, 2 },
+        { 3, 0, 3 }
+    });
+  }
+  /*
+        "w1 w2 w3 w4 w5", //0
+      "w1 w3 w2 w3",//1
+      "w1 xx w2 yy w3",//2
+      "w1 w3 xx w2 yy w3",//3
+      "u2 u2 u1", //4
+      "u2 xx u2 u1",//5
+      "u2 u2 xx u1", //6
+      "u2 xx u2 yy u1", //7
+      "u2 xx u1 u2",//8
+      "u1 u2 xx u2",//9
+      "u2 u1 xx u2",//10
+      "t1 t2 t1 t3 t2 t3"};//11
+   */
+
+  // ((u1 near u2) and xx)
+  public void testNestedNear() throws Exception {
+
+    Query q = new UnorderedNearQuery(0, makeTermQuery("u1"), makeTermQuery("u2"));
+    BooleanQuery topq = new BooleanQuery();
+    topq.add(q, BooleanClause.Occur.MUST);
+    topq.add(makeTermQuery("xx"), BooleanClause.Occur.MUST);
+
+    checkIntervals(topq, searcher, new int[][]{
+        { 5, 1, 1, 2, 3 },
+        { 8, 1, 1, 2, 3 },
+        { 9, 0, 1, 2, 2 },
+        { 10, 0, 1, 2, 2 }
+    });
+
+  }
+}
diff --git a/lucene/core/src/test/org/apache/lucene/search/posfilter/TestPositionsAndOffsets.java b/lucene/core/src/test/org/apache/lucene/search/posfilter/TestPositionsAndOffsets.java
new file mode 100644
index 0000000..a7393d0
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/search/posfilter/TestPositionsAndOffsets.java
@@ -0,0 +1,68 @@
+package org.apache.lucene.search.posfilter;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.FieldType;
+import org.apache.lucene.document.TextField;
+import org.apache.lucene.index.IndexOptions;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.util.LuceneTestCase.SuppressCodecs;
+
+// We need to store offsets here, so don't use the following Codecs, which don't
+// support them.
+@SuppressCodecs({"MockFixedIntBlock", "MockVariableIntBlock", "MockSep", "MockRandom"})
+public class TestPositionsAndOffsets extends IntervalTestBase {
+
+  protected void addDocs(RandomIndexWriter writer) throws IOException {
+    FieldType fieldType = new FieldType(TextField.TYPE_NOT_STORED);
+    fieldType.setIndexOptions(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS);
+    Document doc = new Document();
+    doc.add(newField(
+        "field",
+        "Pease porridge hot! Pease porridge cold! Pease porridge in the pot nine days old! Some like it hot, some"
+            + " like it cold, Some like it in the pot nine days old! Pease porridge hot! Pease porridge cold!",
+        fieldType));
+    writer.addDocument(doc);
+  }
+
+  public void testTermQueryOffsets() throws IOException {
+    Query query = new TermQuery(new Term("field", "porridge"));
+    checkIntervalOffsets(query, searcher, new int[][]{
+        { 0, 6, 14, 26, 34, 47, 55, 164, 172, 184, 192 }
+    });
+  }
+
+  public void testBooleanQueryOffsets() throws IOException {
+    BooleanQuery query = new BooleanQuery();
+    query.add(new BooleanClause(new TermQuery(new Term("field", "porridge")),
+        BooleanClause.Occur.MUST));
+    query.add(new BooleanClause(new TermQuery(new Term("field", "nine")),
+        BooleanClause.Occur.MUST));
+    checkIntervalOffsets(query,  searcher, new int[][]{
+        { 0, 6, 14, 26, 34, 47, 55, 67, 71, 143, 147, 164, 172, 184, 192 }
+    });
+  }
+
+}
\ No newline at end of file
diff --git a/lucene/core/src/test/org/apache/lucene/search/posfilter/TestRangeFilterQuery.java b/lucene/core/src/test/org/apache/lucene/search/posfilter/TestRangeFilterQuery.java
new file mode 100644
index 0000000..4b5dde0
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/search/posfilter/TestRangeFilterQuery.java
@@ -0,0 +1,72 @@
+package org.apache.lucene.search.posfilter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.TextField;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.posfilter.OrderedNearQuery;
+import org.apache.lucene.search.posfilter.RangeFilterQuery;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class TestRangeFilterQuery extends IntervalTestBase {
+
+  @Override
+  protected void addDocs(RandomIndexWriter writer) throws IOException {
+    for (int i = 0; i < docFields.length; i++) {
+      Document doc = new Document();
+      doc.add(newField("field", docFields[i], TextField.TYPE_STORED));
+      writer.addDocument(doc);
+    }
+  }
+
+  private String[] docFields = {
+      "w1 w2 w3 w4 w5 w6 w7 w8 w9 w10 w11 w12", //0
+      "w1 w3 w4 w5 w6 w7 w8 w4", //1
+      "w1 w3 w10 w4 w5 w6 w7 w8", //2
+      "w1 w3 w2 w4 w10 w5 w6 w7 w8", //3
+  };
+
+  @Test
+  public void testSimpleTermRangeFilter() throws IOException {
+    Query q = new RangeFilterQuery(2, makeTermQuery("w4"));
+    checkIntervals(q, searcher, new int[][]{
+        { 1, 2, 2 }
+    });
+  }
+
+  @Test
+  public void testStartEndTermRangeFilter() throws IOException {
+    Query q = new RangeFilterQuery(2, 4, makeTermQuery("w3"));
+    checkIntervals(q, searcher, new int[][]{
+        { 0, 2, 2 }
+    });
+  }
+
+  public void testRangeFilteredPositionFilter() throws IOException {
+    Query q = new OrderedNearQuery(0, makeTermQuery("w4"), makeTermQuery("w5"));
+    q = new RangeFilterQuery(3, 10, q);
+    checkIntervals(q, searcher, new int[][]{
+        { 0, 3, 4 },
+        { 2, 3, 4 }
+    });
+  }
+}
diff --git a/lucene/core/src/test/org/apache/lucene/search/spans/TestNearSpansOrdered.java b/lucene/core/src/test/org/apache/lucene/search/spans/TestNearSpansOrdered.java
index 649f301..4c1db28 100644
--- a/lucene/core/src/test/org/apache/lucene/search/spans/TestNearSpansOrdered.java
+++ b/lucene/core/src/test/org/apache/lucene/search/spans/TestNearSpansOrdered.java
@@ -20,16 +20,17 @@
 import org.apache.lucene.analysis.MockAnalyzer;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.RandomIndexWriter;
 import org.apache.lucene.index.IndexReaderContext;
+import org.apache.lucene.index.RandomIndexWriter;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.CheckHits;
 import org.apache.lucene.search.Explanation;
 import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.Weight;
 import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Weight;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.util.LuceneTestCase;
 
@@ -183,7 +184,7 @@
     Weight w = searcher.createNormalizedWeight(q);
     IndexReaderContext topReaderContext = searcher.getTopReaderContext();
     LeafReaderContext leave = topReaderContext.leaves().get(0);
-    Scorer s = w.scorer(leave, leave.reader().getLiveDocs());
+    Scorer s = w.scorer(leave, DocsEnum.FLAG_POSITIONS, leave.reader().getLiveDocs());
     assertEquals(1, s.advance(1));
   }
 
diff --git a/lucene/core/src/test/org/apache/lucene/search/spans/TestSpans.java b/lucene/core/src/test/org/apache/lucene/search/spans/TestSpans.java
index 5a960f3..5517599 100644
--- a/lucene/core/src/test/org/apache/lucene/search/spans/TestSpans.java
+++ b/lucene/core/src/test/org/apache/lucene/search/spans/TestSpans.java
@@ -17,14 +17,12 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.util.List;
-
 import org.apache.lucene.analysis.MockAnalyzer;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.IndexReaderContext;
 import org.apache.lucene.index.IndexWriter;
@@ -43,6 +41,9 @@
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.util.LuceneTestCase;
 
+import java.io.IOException;
+import java.util.List;
+
 public class TestSpans extends LuceneTestCase {
   private IndexSearcher searcher;
   private IndexReader reader;
@@ -429,7 +430,7 @@
                                 slop,
                                 ordered);
   
-        spanScorer = searcher.createNormalizedWeight(snq).scorer(ctx, ctx.reader().getLiveDocs());
+        spanScorer = searcher.createNormalizedWeight(snq).scorer(ctx, DocsEnum.FLAG_POSITIONS, ctx.reader().getLiveDocs());
       } finally {
         searcher.setSimilarity(oldSim);
       }
diff --git a/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionRescorer.java b/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionRescorer.java
index 18ec02c..8f77e95 100644
--- a/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionRescorer.java
+++ b/lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionRescorer.java
@@ -18,7 +18,6 @@
  */
 
 import java.io.IOException;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -27,12 +26,11 @@
 import org.apache.lucene.index.ReaderUtil;
 import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.FakeScorer;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Rescorer;
-import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.Sort;
 import org.apache.lucene.search.SortRescorer;
-import org.apache.lucene.search.Weight;
 
 /**
  * A {@link Rescorer} that uses an expression to re-score
@@ -58,56 +56,6 @@
     this.bindings = bindings;
   }
 
-  private static class FakeScorer extends Scorer {
-    float score;
-    int doc = -1;
-    int freq = 1;
-
-    public FakeScorer() {
-      super(null);
-    }
-    
-    @Override
-    public int advance(int target) {
-      throw new UnsupportedOperationException("FakeScorer doesn't support advance(int)");
-    }
-
-    @Override
-    public int docID() {
-      return doc;
-    }
-
-    @Override
-    public int freq() {
-      return freq;
-    }
-
-    @Override
-    public int nextDoc() {
-      throw new UnsupportedOperationException("FakeScorer doesn't support nextDoc()");
-    }
-    
-    @Override
-    public float score() {
-      return score;
-    }
-
-    @Override
-    public long cost() {
-      return 1;
-    }
-
-    @Override
-    public Weight getWeight() {
-      throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public Collection<ChildScorer> getChildren() {
-      throw new UnsupportedOperationException();
-    }
-  }
-
   @Override
   public Explanation explain(IndexSearcher searcher, Explanation firstPassExplanation, int docID) throws IOException {
     Explanation result = super.explain(searcher, firstPassExplanation, docID);
diff --git a/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysQuery.java b/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysQuery.java
index 45f647e..92044385 100644
--- a/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysQuery.java
+++ b/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysQuery.java
@@ -16,11 +16,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import java.io.IOException;
-import java.util.Arrays;
-
-import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.search.BulkScorer;
 import org.apache.lucene.search.Collector;
 import org.apache.lucene.search.DocIdSet;
 import org.apache.lucene.search.DocIdSetIterator;
@@ -29,10 +27,11 @@
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.Scorer;
-import org.apache.lucene.search.BulkScorer;
 import org.apache.lucene.search.Weight;
 import org.apache.lucene.util.Bits;
 
+import java.io.IOException;
+import java.util.Arrays;
 /** Only purpose is to punch through and return a
  *  DrillSidewaysScorer */ 
 
@@ -118,17 +117,17 @@
       }
 
       @Override
-      public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
+      public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
         // We can only run as a top scorer:
         throw new UnsupportedOperationException();
       }
 
       @Override
-      public BulkScorer bulkScorer(LeafReaderContext context, boolean scoreDocsInOrder, Bits acceptDocs) throws IOException {
+      public BulkScorer bulkScorer(LeafReaderContext context, boolean scoreDocsInOrder, int flags, Bits acceptDocs) throws IOException {
 
         // TODO: it could be better if we take acceptDocs
         // into account instead of baseScorer?
-        Scorer baseScorer = baseWeight.scorer(context, acceptDocs);
+        Scorer baseScorer = baseWeight.scorer(context, flags, acceptDocs);
 
         DrillSidewaysScorer.DocsAndCost[] dims = new DrillSidewaysScorer.DocsAndCost[drillDowns.length];
         int nullCount = 0;
@@ -173,7 +172,7 @@
               dims[dim].disi = disi;
             }
           } else {
-            DocIdSetIterator disi = ((Weight) drillDowns[dim]).scorer(context, null);
+            DocIdSetIterator disi = ((Weight) drillDowns[dim]).scorer(context, flags, null);
             if (disi == null) {
               nullCount++;
               continue;
diff --git a/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysScorer.java b/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysScorer.java
index 3b5b175..3ad692e 100644
--- a/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysScorer.java
+++ b/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysScorer.java
@@ -21,15 +21,16 @@
 import java.util.Collection;
 import java.util.Collections;
 
-import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.DocsEnum;
-import org.apache.lucene.search.LeafCollector;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.search.BulkScorer;
 import org.apache.lucene.search.Collector;
 import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.search.LeafCollector;
 import org.apache.lucene.search.Scorer;
-import org.apache.lucene.search.BulkScorer;
 import org.apache.lucene.search.Weight;
 import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.FixedBitSet;
 
 class DrillSidewaysScorer extends BulkScorer {
@@ -645,6 +646,36 @@
     }
 
     @Override
+    public int nextPosition() throws IOException {
+      throw new UnsupportedOperationException("FakeScorer doesn't support nextPosition()");
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
     public int nextDoc() {
       throw new UnsupportedOperationException("FakeScorer doesn't support nextDoc()");
     }
diff --git a/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyFacetSumValueSource.java b/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyFacetSumValueSource.java
index a35660d..974c6d0 100644
--- a/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyFacetSumValueSource.java
+++ b/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyFacetSumValueSource.java
@@ -18,21 +18,20 @@
  */
 
 import java.io.IOException;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import org.apache.lucene.facet.FacetsCollector.MatchingDocs;
 import org.apache.lucene.facet.FacetsCollector;
+import org.apache.lucene.facet.FacetsCollector.MatchingDocs;
 import org.apache.lucene.facet.FacetsConfig;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.queries.function.FunctionValues;
 import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.queries.function.docvalues.DoubleDocValues;
 import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.search.FakeScorer;
 import org.apache.lucene.search.Scorer;
-import org.apache.lucene.search.Weight;
 import org.apache.lucene.util.IntsRef;
 
 /** Aggregates sum of values from {@link
@@ -62,28 +61,6 @@
     sumValues(fc.getMatchingDocs(), fc.getKeepScores(), valueSource);
   }
 
-  private static final class FakeScorer extends Scorer {
-    float score;
-    int docID;
-    FakeScorer() { super(null); }
-    @Override public float score() throws IOException { return score; }
-    @Override public int freq() throws IOException { throw new UnsupportedOperationException(); }
-    @Override public int docID() { return docID; }
-    @Override public int nextDoc() throws IOException { throw new UnsupportedOperationException(); }
-    @Override public int advance(int target) throws IOException { throw new UnsupportedOperationException(); }
-    @Override public long cost() { return 0; }
-
-    @Override
-    public Weight getWeight() {
-      throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public Collection<ChildScorer> getChildren() {
-      throw new UnsupportedOperationException();
-    }
-  }
-
   private final void sumValues(List<MatchingDocs> matchingDocs, boolean keepScores, ValueSource valueSource) throws IOException {
     final FakeScorer scorer = new FakeScorer();
     Map<String, Scorer> context = new HashMap<>();
@@ -104,7 +81,7 @@
       while ((doc = docs.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
         ords.get(doc, scratch);
         if (keepScores) {
-          scorer.docID = doc;
+          scorer.doc = doc;
           scorer.score = scores[scoresIdx++];
         }
         float value = (float) functionValues.doubleVal(doc);
diff --git a/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/TaxonomyIndexArrays.java b/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/TaxonomyIndexArrays.java
index 36adfa8..550952b 100644
--- a/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/TaxonomyIndexArrays.java
+++ b/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/TaxonomyIndexArrays.java
@@ -1,16 +1,16 @@
 package org.apache.lucene.facet.taxonomy.directory;
 
-import java.io.IOException;
-
 import org.apache.lucene.facet.taxonomy.ParallelTaxonomyArrays;
 import org.apache.lucene.facet.taxonomy.TaxonomyReader;
 import org.apache.lucene.index.CorruptIndexException;
-import org.apache.lucene.index.DocsAndPositionsEnum;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.MultiFields;
 import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.util.ArrayUtil;
 
+import java.io.IOException;
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -129,9 +129,9 @@
     // it's ok to use MultiFields because we only iterate on one posting list.
     // breaking it to loop over the leaves() only complicates code for no
     // apparent gain.
-    DocsAndPositionsEnum positions = MultiFields.getTermPositionsEnum(reader, null,
+    DocsEnum positions = MultiFields.getTermPositionsEnum(reader, null,
         Consts.FIELD_PAYLOADS, Consts.PAYLOAD_PARENT_BYTES_REF,
-        DocsAndPositionsEnum.FLAG_PAYLOADS);
+        DocsEnum.FLAG_PAYLOADS);
 
     // shouldn't really happen, if it does, something's wrong
     if (positions == null || positions.advance(first) == DocIdSetIterator.NO_MORE_DOCS) {
diff --git a/lucene/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java b/lucene/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java
index 4c0c6b5..ddea4f5 100644
--- a/lucene/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java
+++ b/lucene/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java
@@ -17,13 +17,22 @@
  * limitations under the License.
  */
 
-
 import java.io.IOException;
-import java.util.Collection;
 
-import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.IndexWriter;
-import org.apache.lucene.search.*;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.search.FakeScorer;
+import org.apache.lucene.search.FieldComparator;
+import org.apache.lucene.search.Filter;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.SimpleCollector;
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.search.TopDocsCollector;
+import org.apache.lucene.search.TopFieldCollector;
+import org.apache.lucene.search.TopScoreDocCollector;
 import org.apache.lucene.util.ArrayUtil;
 import org.apache.lucene.util.PriorityQueue;
 
@@ -85,56 +94,6 @@
   private final GroupQueue groupQueue;
   private boolean groupCompetes;
 
-  private final static class FakeScorer extends Scorer {
-
-    float score;
-    int doc;
-
-    public FakeScorer() {
-      super(null);
-    }
-
-    @Override
-    public float score() {
-      return score;
-    }
-    
-    @Override
-    public int freq() {
-      throw new UnsupportedOperationException(); // TODO: wtf does this class do?
-    }
-
-    @Override
-    public int docID() {
-      return doc;
-    }
-
-    @Override
-    public int advance(int target) {
-      throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public int nextDoc() {
-      throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public long cost() {
-      return 1;
-    }
-
-    @Override
-    public Weight getWeight() {
-      throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public Collection<ChildScorer> getChildren() {
-      throw new UnsupportedOperationException();
-    }
-  }
-
   private static final class OneGroup {
     LeafReaderContext readerContext;
     //int groupOrd;
diff --git a/lucene/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java b/lucene/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java
index f4ab2c8..4355182 100644
--- a/lucene/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java
+++ b/lucene/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java
@@ -18,10 +18,27 @@
 package org.apache.lucene.search.grouping;
 
 import java.io.IOException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
+import com.carrotsearch.randomizedtesting.annotations.Seed;
 import org.apache.lucene.analysis.MockAnalyzer;
-import org.apache.lucene.document.*;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.FieldType;
+import org.apache.lucene.document.IntField;
+import org.apache.lucene.document.NumericDocValuesField;
+import org.apache.lucene.document.SortedDocValuesField;
+import org.apache.lucene.document.StringField;
+import org.apache.lucene.document.TextField;
 import org.apache.lucene.index.DirectoryReader;
 import org.apache.lucene.index.IndexOptions;
 import org.apache.lucene.index.IndexReaderContext;
@@ -34,7 +51,20 @@
 import org.apache.lucene.index.Term;
 import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.queries.function.valuesource.BytesRefFieldSource;
-import org.apache.lucene.search.*;
+import org.apache.lucene.search.CachingCollector;
+import org.apache.lucene.search.CachingWrapperFilter;
+import org.apache.lucene.search.Collector;
+import org.apache.lucene.search.FieldDoc;
+import org.apache.lucene.search.Filter;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.MultiCollector;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryWrapperFilter;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.Weight;
 import org.apache.lucene.search.grouping.function.FunctionAllGroupsCollector;
 import org.apache.lucene.search.grouping.function.FunctionFirstPassGroupingCollector;
 import org.apache.lucene.search.grouping.function.FunctionSecondPassGroupingCollector;
@@ -54,6 +84,7 @@
 //   - test ties
 //   - test compound sort
 
+@Seed("3C4E441C6A8DA6A2:4E026113DBED10D1")
 public class TestGrouping extends LuceneTestCase {
 
   public void testBasic() throws Exception {
diff --git a/lucene/highlighter/src/java/org/apache/lucene/search/highlight/TokenStreamFromTermPositionVector.java b/lucene/highlighter/src/java/org/apache/lucene/search/highlight/TokenStreamFromTermPositionVector.java
new file mode 100644
index 0000000..bed9505
--- /dev/null
+++ b/lucene/highlighter/src/java/org/apache/lucene/search/highlight/TokenStreamFromTermPositionVector.java
@@ -0,0 +1,137 @@
+package org.apache.lucene.search.highlight;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.lucene.analysis.Token;
+import org.apache.lucene.analysis.TokenStream;
+import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
+import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
+import org.apache.lucene.analysis.tokenattributes.PayloadAttribute;
+import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.index.Terms;
+import org.apache.lucene.index.TermsEnum;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.CollectionUtil;
+
+/**
+ * TokenStream created from a term vector field.
+ */
+public final class TokenStreamFromTermPositionVector extends TokenStream {
+
+  private final List<Token> positionedTokens = new ArrayList<>();
+
+  private Iterator<Token> tokensAtCurrentPosition;
+
+  private CharTermAttribute termAttribute;
+
+  private PositionIncrementAttribute positionIncrementAttribute;
+
+  private OffsetAttribute offsetAttribute;
+
+  private PayloadAttribute payloadAttribute;
+
+  /**
+   * Constructor.
+   * 
+   * @param vector Terms that contains the data for
+   *        creating the TokenStream. Must have positions and offsets.
+   */
+  public TokenStreamFromTermPositionVector(
+      final Terms vector) throws IOException {
+    termAttribute = addAttribute(CharTermAttribute.class);
+    positionIncrementAttribute = addAttribute(PositionIncrementAttribute.class);
+    offsetAttribute = addAttribute(OffsetAttribute.class);
+    payloadAttribute = addAttribute(PayloadAttribute.class);
+    final boolean hasOffsets = vector.hasOffsets();
+    final boolean hasPayloads = vector.hasPayloads();
+    final TermsEnum termsEnum = vector.iterator(null);
+    BytesRef text;
+    DocsEnum dpEnum = null;
+    while((text = termsEnum.next()) != null) {
+      dpEnum = termsEnum.docsAndPositions(null, dpEnum);
+      assert dpEnum != null; // presumably checked by TokenSources.hasPositions earlier
+      dpEnum.nextDoc();
+      final int freq = dpEnum.freq();
+      for (int j = 0; j < freq; j++) {
+        int pos = dpEnum.nextPosition();
+        Token token;
+        if (hasOffsets) {
+          token = new Token(text.utf8ToString(),
+                            dpEnum.startOffset(),
+                            dpEnum.endOffset());
+        } else {
+          token = new Token();
+          token.setEmpty().append(text.utf8ToString());
+        }
+        if (hasPayloads) {
+          // Must make a deep copy of the returned payload,
+          // since D&PEnum API is allowed to re-use on every
+          // call:
+          token.setPayload(BytesRef.deepCopyOf(dpEnum.getPayload()));
+        }
+
+        // Yes - this is the position, not the increment! This is for
+        // sorting. This value
+        // will be corrected before use.
+        token.setPositionIncrement(pos);
+        this.positionedTokens.add(token);
+      }
+    }
+    CollectionUtil.timSort(this.positionedTokens, tokenComparator);
+    int lastPosition = -1;
+    for (final Token token : this.positionedTokens) {
+      int thisPosition = token.getPositionIncrement();
+      token.setPositionIncrement(thisPosition - lastPosition);
+      lastPosition = thisPosition;
+    }
+    this.tokensAtCurrentPosition = this.positionedTokens.iterator();
+  }
+
+  private static final Comparator<Token> tokenComparator = new Comparator<Token>() {
+    @Override
+    public int compare(final Token o1, final Token o2) {
+      return o1.getPositionIncrement() - o2.getPositionIncrement();
+    }
+  };
+  
+  @Override
+  public boolean incrementToken() {
+    if (this.tokensAtCurrentPosition.hasNext()) {
+      final Token next = this.tokensAtCurrentPosition.next();
+      clearAttributes();
+      termAttribute.setEmpty().append(next);
+      positionIncrementAttribute.setPositionIncrement(next
+          .getPositionIncrement());
+      offsetAttribute.setOffset(next.startOffset(), next.endOffset());
+      payloadAttribute.setPayload(next.getPayload());
+      return true;
+    }
+    return false;
+  }
+
+  @Override
+  public void reset() {
+    this.tokensAtCurrentPosition = this.positionedTokens.iterator();
+  }
+}
diff --git a/lucene/highlighter/src/java/org/apache/lucene/search/highlight/TokenStreamFromTermVector.java b/lucene/highlighter/src/java/org/apache/lucene/search/highlight/TokenStreamFromTermVector.java
index 2787dee..23b3106 100644
--- a/lucene/highlighter/src/java/org/apache/lucene/search/highlight/TokenStreamFromTermVector.java
+++ b/lucene/highlighter/src/java/org/apache/lucene/search/highlight/TokenStreamFromTermVector.java
@@ -16,6 +16,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 import java.io.IOException;
 
 import org.apache.lucene.analysis.TokenStream;
@@ -23,7 +24,7 @@
 import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
 import org.apache.lucene.analysis.tokenattributes.PayloadAttribute;
 import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
-import org.apache.lucene.index.DocsAndPositionsEnum;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.Terms;
 import org.apache.lucene.index.TermsEnum;
 import org.apache.lucene.util.BytesRef;
@@ -105,7 +106,7 @@
 
     final TermsEnum termsEnum = vector.iterator(null);
     BytesRef termBytesRef;
-    DocsAndPositionsEnum dpEnum = null;
+    DocsEnum dpEnum = null;
     //int sumFreq = 0;
     while ((termBytesRef = termsEnum.next()) != null) {
       //Grab the term (in same way as BytesRef.utf8ToString() but we don't want a String obj)
diff --git a/lucene/highlighter/src/java/org/apache/lucene/search/postingshighlight/MultiTermHighlighting.java b/lucene/highlighter/src/java/org/apache/lucene/search/postingshighlight/MultiTermHighlighting.java
index bf2f1d2..a334eaf 100644
--- a/lucene/highlighter/src/java/org/apache/lucene/search/postingshighlight/MultiTermHighlighting.java
+++ b/lucene/highlighter/src/java/org/apache/lucene/search/postingshighlight/MultiTermHighlighting.java
@@ -26,7 +26,7 @@
 import org.apache.lucene.analysis.TokenStream;
 import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
 import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
-import org.apache.lucene.index.DocsAndPositionsEnum;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.AutomatonQuery;
 import org.apache.lucene.search.BooleanClause;
@@ -47,10 +47,10 @@
 import org.apache.lucene.util.CharsRef;
 import org.apache.lucene.util.UnicodeUtil;
 import org.apache.lucene.util.automaton.Automata;
-import org.apache.lucene.util.automaton.Operations;
+import org.apache.lucene.util.automaton.Automaton;
 import org.apache.lucene.util.automaton.CharacterRunAutomaton;
 import org.apache.lucene.util.automaton.LevenshteinAutomata;
-import org.apache.lucene.util.automaton.Automaton;
+import org.apache.lucene.util.automaton.Operations;
 
 /**
  * Support for highlighting multiterm queries in PostingsHighlighter.
@@ -197,7 +197,7 @@
    * <p>
    * This is solely used internally by PostingsHighlighter: <b>DO NOT USE THIS METHOD!</b>
    */
-  static DocsAndPositionsEnum getDocsEnum(final TokenStream ts, final CharacterRunAutomaton[] matchers) throws IOException {
+  static DocsEnum getDocsEnum(final TokenStream ts, final CharacterRunAutomaton[] matchers) throws IOException {
     final CharTermAttribute charTermAtt = ts.addAttribute(CharTermAttribute.class);
     final OffsetAttribute offsetAtt = ts.addAttribute(OffsetAttribute.class);
     ts.reset();
@@ -207,7 +207,7 @@
     // would only serve to make this method less bogus.
     // instead, we always return freq() = Integer.MAX_VALUE and let PH terminate based on offset...
     
-    return new DocsAndPositionsEnum() {
+    return new DocsEnum() {
       int currentDoc = -1;
       int currentMatch = -1;
       int currentStartOffset = -1;
@@ -237,7 +237,19 @@
         currentStartOffset = currentEndOffset = Integer.MAX_VALUE;
         return Integer.MAX_VALUE;
       }
-      
+
+      @Override
+      public int startPosition() throws IOException {
+        if (currentStartOffset < Integer.MAX_VALUE)
+          return 0;
+        return NO_MORE_POSITIONS;
+      }
+
+      @Override
+      public int endPosition() throws IOException {
+        return startPosition();
+      }
+
       @Override
       public int freq() throws IOException {
         return Integer.MAX_VALUE; // lie
diff --git a/lucene/highlighter/src/java/org/apache/lucene/search/postingshighlight/PostingsHighlighter.java b/lucene/highlighter/src/java/org/apache/lucene/search/postingshighlight/PostingsHighlighter.java
index 14f364b..b63fee9 100644
--- a/lucene/highlighter/src/java/org/apache/lucene/search/postingshighlight/PostingsHighlighter.java
+++ b/lucene/highlighter/src/java/org/apache/lucene/search/postingshighlight/PostingsHighlighter.java
@@ -31,7 +31,7 @@
 import java.util.TreeSet;
 
 import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.index.DocsAndPositionsEnum;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.IndexOptions;
 import org.apache.lucene.index.IndexReader;
@@ -455,7 +455,7 @@
     
   private Map<Integer,Object> highlightField(String field, String contents[], BreakIterator bi, BytesRef terms[], int[] docids, List<LeafReaderContext> leaves, int maxPassages, Query query) throws IOException {  
     Map<Integer,Object> highlights = new HashMap<>();
-    
+
     PassageFormatter fieldFormatter = getFormatter(field);
     if (fieldFormatter == null) {
       throw new NullPointerException("PassageFormatter cannot be null");
@@ -477,7 +477,7 @@
 
     // we are processing in increasing docid order, so we only need to reinitialize stuff on segment changes
     // otherwise, we will just advance() existing enums to the new document in the same segment.
-    DocsAndPositionsEnum postings[] = null;
+    DocsEnum postings[] = null;
     TermsEnum termsEnum = null;
     int lastLeaf = -1;
     
@@ -499,7 +499,7 @@
         Terms t = r.terms(field);
         if (t != null) {
           termsEnum = t.iterator(null);
-          postings = new DocsAndPositionsEnum[terms.length];
+          postings = new DocsEnum[terms.length];
         }
       }
       if (termsEnum == null) {
@@ -508,7 +508,7 @@
       
       // if there are multi-term matches, we have to initialize the "fake" enum for each document
       if (automata.length > 0) {
-        DocsAndPositionsEnum dp = MultiTermHighlighting.getDocsEnum(analyzer.tokenStream(field, content), automata);
+        DocsEnum dp = MultiTermHighlighting.getDocsEnum(analyzer.tokenStream(field, content), automata);
         dp.advance(doc - subContext.docBase);
         postings[terms.length-1] = dp; // last term is the multiterm matcher
       }
@@ -534,7 +534,7 @@
   // we can intersect these with the postings lists via BreakIterator.preceding(offset),s
   // score each sentence as norm(sentenceStartOffset) * sum(weight * tf(freq))
   private Passage[] highlightDoc(String field, BytesRef terms[], int contentLength, BreakIterator bi, int doc, 
-      TermsEnum termsEnum, DocsAndPositionsEnum[] postings, int n) throws IOException {
+      TermsEnum termsEnum, DocsEnum[] postings, int n) throws IOException {
     PassageScorer scorer = getScorer(field);
     if (scorer == null) {
       throw new NullPointerException("PassageScorer cannot be null");
@@ -543,7 +543,7 @@
     float weights[] = new float[terms.length];
     // initialize postings
     for (int i = 0; i < terms.length; i++) {
-      DocsAndPositionsEnum de = postings[i];
+      DocsEnum de = postings[i];
       int pDoc;
       if (de == EMPTY) {
         continue;
@@ -552,7 +552,7 @@
         if (!termsEnum.seekExact(terms[i])) {
           continue; // term not found
         }
-        de = postings[i] = termsEnum.docsAndPositions(null, null, DocsAndPositionsEnum.FLAG_OFFSETS);
+        de = postings[i] = termsEnum.docsAndPositions(null, null, DocsEnum.FLAG_OFFSETS);
         if (de == null) {
           // no positions available
           throw new IllegalArgumentException("field '" + field + "' was indexed without offsets, cannot highlight");
@@ -590,7 +590,7 @@
     
     OffsetsEnum off;
     while ((off = pq.poll()) != null) {
-      final DocsAndPositionsEnum dp = off.dp;
+      final DocsEnum dp = off.dp;
       int start = dp.startOffset();
       if (start == -1) {
         throw new IllegalArgumentException("field '" + field + "' was indexed without offsets, cannot highlight");
@@ -698,11 +698,11 @@
   }
   
   private static class OffsetsEnum implements Comparable<OffsetsEnum> {
-    DocsAndPositionsEnum dp;
+    DocsEnum dp;
     int pos;
     int id;
     
-    OffsetsEnum(DocsAndPositionsEnum dp, int id) throws IOException {
+    OffsetsEnum(DocsEnum dp, int id) throws IOException {
       this.dp = dp;
       this.id = id;
       this.pos = 1;
@@ -724,10 +724,20 @@
     }
   }
   
-  private static final DocsAndPositionsEnum EMPTY = new DocsAndPositionsEnum() {
+  private static final DocsEnum EMPTY = new DocsEnum() {
 
     @Override
-    public int nextPosition() throws IOException { return 0; }
+    public int nextPosition() throws IOException { return NO_MORE_POSITIONS; }
+
+    @Override
+    public int startPosition() throws IOException {
+      assert false; return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
 
     @Override
     public int startOffset() throws IOException { return Integer.MAX_VALUE; }
diff --git a/lucene/highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldTermStack.java b/lucene/highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldTermStack.java
index 29c307a..1f629d6 100644
--- a/lucene/highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldTermStack.java
+++ b/lucene/highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldTermStack.java
@@ -22,7 +22,7 @@
 import java.util.LinkedList;
 import java.util.Set;
 
-import org.apache.lucene.index.DocsAndPositionsEnum;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.Fields;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.Term;
@@ -93,7 +93,7 @@
 
     final CharsRefBuilder spare = new CharsRefBuilder();
     final TermsEnum termsEnum = vector.iterator(null);
-    DocsAndPositionsEnum dpEnum = null;
+    DocsEnum dpEnum = null;
     BytesRef text;
     
     int numDocs = reader.maxDoc();
diff --git a/lucene/highlighter/src/test/org/apache/lucene/search/highlight/custom/HighlightCustomQueryTest.java b/lucene/highlighter/src/test/org/apache/lucene/search/highlight/custom/HighlightCustomQueryTest.java
index 536259a..a52d9b7 100644
--- a/lucene/highlighter/src/test/org/apache/lucene/search/highlight/custom/HighlightCustomQueryTest.java
+++ b/lucene/highlighter/src/test/org/apache/lucene/search/highlight/custom/HighlightCustomQueryTest.java
@@ -16,9 +16,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import java.io.IOException;
-import java.util.Map;
-
 import org.apache.lucene.analysis.MockAnalyzer;
 import org.apache.lucene.analysis.MockTokenFilter;
 import org.apache.lucene.analysis.MockTokenizer;
@@ -36,6 +33,9 @@
 import org.apache.lucene.search.highlight.WeightedSpanTermExtractor;
 import org.apache.lucene.util.LuceneTestCase;
 
+import java.io.IOException;
+import java.util.Map;
+
 /**
  * Tests the extensibility of {@link WeightedSpanTermExtractor} and
  * {@link QueryScorer} in a user defined package
diff --git a/lucene/join/src/java/org/apache/lucene/search/join/FakeScorer.java b/lucene/join/src/java/org/apache/lucene/search/join/FakeScorer.java
deleted file mode 100644
index cbd1ff8..0000000
--- a/lucene/join/src/java/org/apache/lucene/search/join/FakeScorer.java
+++ /dev/null
@@ -1,75 +0,0 @@
-package org.apache.lucene.search.join;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import java.util.Collection;
-
-import org.apache.lucene.search.LeafCollector;
-import org.apache.lucene.search.Scorer;
-import org.apache.lucene.search.Weight;
-
-/** Passed to {@link LeafCollector#setScorer} during join collection. */
-final class FakeScorer extends Scorer {
-  float score;
-  int doc = -1;
-  int freq = 1;
-
-  public FakeScorer() {
-    super(null);
-  }
-    
-  @Override
-  public int advance(int target) {
-    throw new UnsupportedOperationException("FakeScorer doesn't support advance(int)");
-  }
-
-  @Override
-  public int docID() {
-    return doc;
-  }
-
-  @Override
-  public int freq() {
-    throw new UnsupportedOperationException("FakeScorer doesn't support freq()");
-  }
-
-  @Override
-  public int nextDoc() {
-    throw new UnsupportedOperationException("FakeScorer doesn't support nextDoc()");
-  }
-    
-  @Override
-  public float score() {
-    return score;
-  }
-
-  @Override
-  public long cost() {
-    return 1;
-  }
-
-  @Override
-  public Weight getWeight() {
-    throw new UnsupportedOperationException();
-  }
-
-  @Override
-  public Collection<ChildScorer> getChildren() {
-    throw new UnsupportedOperationException();
-  }
-}
diff --git a/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java
index 9b6eb26..8bbc2fe 100644
--- a/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java
+++ b/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java
@@ -31,6 +31,7 @@
 import org.apache.lucene.search.ComplexExplanation;
 import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.FakeScorer;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.LeafCollector;
 import org.apache.lucene.search.Query;
@@ -133,7 +134,7 @@
 
       @Override
       public Explanation explain(LeafReaderContext context, int doc) throws IOException {
-        SVInnerScorer scorer = (SVInnerScorer) bulkScorer(context, false, null);
+        SVInnerScorer scorer = (SVInnerScorer) bulkScorer(context, false, DocsEnum.FLAG_FREQS, null);
         if (scorer != null) {
           return scorer.explain(doc);
         }
@@ -163,7 +164,7 @@
       }
 
       @Override
-      public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
+      public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
         Terms terms = context.reader().terms(field);
         if (terms == null) {
           return null;
@@ -181,10 +182,10 @@
       }
 
       @Override
-      public BulkScorer bulkScorer(LeafReaderContext context, boolean scoreDocsInOrder, Bits acceptDocs) throws IOException {
+      public BulkScorer bulkScorer(LeafReaderContext context, boolean scoreDocsInOrder, int flags, Bits acceptDocs) throws IOException {
 
         if (scoreDocsInOrder) {
-          return super.bulkScorer(context, scoreDocsInOrder, acceptDocs);
+          return super.bulkScorer(context, scoreDocsInOrder, flags, acceptDocs);
         } else {
           Terms terms = context.reader().terms(field);
           if (terms == null) {
@@ -288,6 +289,7 @@
 
       return new ComplexExplanation(true, scores[ords[scoreUpto]], "Score based on join value " + termsEnum.term().utf8ToString());
     }
+
   }
 
   // This impl that tracks whether a docid has already been emitted. This check makes sure that docs aren't emitted
@@ -361,6 +363,36 @@
     }
 
     @Override
+    public int nextPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
+    @Override
     public int docID() {
       return currentDoc;
     }
diff --git a/lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java
index 65f4bcf..77cd5b4 100644
--- a/lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java
+++ b/lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java
@@ -25,7 +25,6 @@
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.Term;
-import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.search.Explanation;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Query;
@@ -34,6 +33,7 @@
 import org.apache.lucene.util.BitDocIdSet;
 import org.apache.lucene.util.BitSet;
 import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
 
 /**
  * Just like {@link ToParentBlockJoinQuery}, except this
@@ -127,9 +127,9 @@
     // NOTE: acceptDocs applies (and is checked) only in the
     // child document space
     @Override
-    public Scorer scorer(LeafReaderContext readerContext, Bits acceptDocs) throws IOException {
+    public Scorer scorer(LeafReaderContext readerContext, int flags, Bits acceptDocs) throws IOException {
 
-      final Scorer parentScorer = parentWeight.scorer(readerContext, null);
+      final Scorer parentScorer = parentWeight.scorer(readerContext, flags, null);
 
       if (parentScorer == null) {
         // No matches
@@ -278,6 +278,36 @@
     }
 
     @Override
+    public int nextPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
+    @Override
     public int advance(int childTarget) throws IOException {
       assert childTarget >= parentBits.length() || !parentBits.get(childTarget);
       
diff --git a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java
index 6fe1fc4..67b5439 100644
--- a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java
+++ b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java
@@ -17,17 +17,32 @@
  * limitations under the License.
  */
 
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Queue;
+
+import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.IndexWriter; // javadocs
-import org.apache.lucene.search.*;
+import org.apache.lucene.search.FakeScorer;
+import org.apache.lucene.search.FieldComparator;
+import org.apache.lucene.search.FieldValueHitQueue;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.ScoreCachingWrappingScorer;
+import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.Scorer.ChildScorer;
+import org.apache.lucene.search.SimpleCollector;
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.search.TopDocsCollector;
+import org.apache.lucene.search.TopFieldCollector;
+import org.apache.lucene.search.TopScoreDocCollector;
 import org.apache.lucene.search.grouping.GroupDocs;
 import org.apache.lucene.search.grouping.TopGroups;
 import org.apache.lucene.util.ArrayUtil;
 
-import java.io.IOException;
-import java.util.*;
-
 
 /** Collects parent document hits for a Query containing one more more
  *  BlockJoinQuery clauses, sorted by the
diff --git a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java
index cba85b1..b3715e6 100644
--- a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java
+++ b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java
@@ -23,6 +23,7 @@
 import java.util.Locale;
 import java.util.Set;
 
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.index.LeafReaderContext;
@@ -39,6 +40,7 @@
 import org.apache.lucene.util.BitDocIdSet;
 import org.apache.lucene.util.BitSet;
 import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
 
 /**
  * This query requires that you index
@@ -160,9 +162,9 @@
     // NOTE: acceptDocs applies (and is checked) only in the
     // parent document space
     @Override
-    public Scorer scorer(LeafReaderContext readerContext, Bits acceptDocs) throws IOException {
+    public Scorer scorer(LeafReaderContext readerContext, int flags, Bits acceptDocs) throws IOException {
 
-      final Scorer childScorer = childWeight.scorer(readerContext, readerContext.reader().getLiveDocs());
+      final Scorer childScorer = childWeight.scorer(readerContext, flags, readerContext.reader().getLiveDocs());
       if (childScorer == null) {
         // No matches
         return null;
@@ -188,7 +190,7 @@
 
     @Override
     public Explanation explain(LeafReaderContext context, int doc) throws IOException {
-      BlockJoinScorer scorer = (BlockJoinScorer) scorer(context, context.reader().getLiveDocs());
+      BlockJoinScorer scorer = (BlockJoinScorer) scorer(context, DocsEnum.FLAG_FREQS, context.reader().getLiveDocs());
       if (scorer != null && scorer.advance(doc) == doc) {
         return scorer.explain(context.docBase);
       }
@@ -375,6 +377,36 @@
     }
 
     @Override
+    public int nextPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
+    @Override
     public int advance(int parentTarget) throws IOException {
 
       //System.out.println("Q.advance parentTarget=" + parentTarget);
diff --git a/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java b/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java
index 6a0ffa6..53dfa87 100644
--- a/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java
+++ b/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java
@@ -25,14 +25,55 @@
 import java.util.Locale;
 
 import org.apache.lucene.analysis.MockAnalyzer;
-import org.apache.lucene.document.*;
-import org.apache.lucene.index.*;
-import org.apache.lucene.search.*;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.IntField;
+import org.apache.lucene.document.NumericDocValuesField;
+import org.apache.lucene.document.SortedDocValuesField;
+import org.apache.lucene.document.StoredField;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.LogDocMergePolicy;
+import org.apache.lucene.index.MultiFields;
+import org.apache.lucene.index.NoMergePolicy;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.index.ReaderUtil;
+import org.apache.lucene.index.StoredDocument;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.BooleanClause;
 import org.apache.lucene.search.BooleanClause.Occur;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.FieldDoc;
+import org.apache.lucene.search.Filter;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.MatchAllDocsQuery;
+import org.apache.lucene.search.MultiTermQuery;
+import org.apache.lucene.search.NumericRangeQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryUtils;
+import org.apache.lucene.search.QueryWrapperFilter;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.search.Weight;
 import org.apache.lucene.search.grouping.GroupDocs;
 import org.apache.lucene.search.grouping.TopGroups;
 import org.apache.lucene.store.Directory;
-import org.apache.lucene.util.*;
+import org.apache.lucene.util.BitSet;
+import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.BytesRefBuilder;
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.util.NumericUtils;
+import org.apache.lucene.util.TestUtil;
 
 public class TestBlockJoin extends LuceneTestCase {
 
@@ -1148,7 +1189,7 @@
 
     ToParentBlockJoinQuery q = new ToParentBlockJoinQuery(tq, parentFilter, ScoreMode.Avg);
     Weight weight = s.createNormalizedWeight(q);
-    DocIdSetIterator disi = weight.scorer(s.getIndexReader().leaves().get(0), null);
+    DocIdSetIterator disi = weight.scorer(s.getIndexReader().leaves().get(0), DocsEnum.FLAG_FREQS, null);
     assertEquals(1, disi.advance(1));
     r.close();
     dir.close();
@@ -1182,7 +1223,7 @@
 
     ToParentBlockJoinQuery q = new ToParentBlockJoinQuery(tq, parentFilter, ScoreMode.Avg);
     Weight weight = s.createNormalizedWeight(q);
-    DocIdSetIterator disi = weight.scorer(s.getIndexReader().leaves().get(0), null);
+    DocIdSetIterator disi = weight.scorer(s.getIndexReader().leaves().get(0), DocsEnum.FLAG_FREQS, null);
     assertEquals(2, disi.advance(0));
     r.close();
     dir.close();
diff --git a/lucene/memory/src/java/org/apache/lucene/index/memory/MemoryIndex.java b/lucene/memory/src/java/org/apache/lucene/index/memory/MemoryIndex.java
index 28c6a97..1653156 100644
--- a/lucene/memory/src/java/org/apache/lucene/index/memory/MemoryIndex.java
+++ b/lucene/memory/src/java/org/apache/lucene/index/memory/MemoryIndex.java
@@ -33,7 +33,6 @@
 import org.apache.lucene.analysis.tokenattributes.TermToBytesRefAttribute;
 import org.apache.lucene.index.BinaryDocValues;
 import org.apache.lucene.index.DocValuesType;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.FieldInfos;
@@ -968,19 +967,16 @@
 
       @Override
       public DocsEnum docs(Bits liveDocs, DocsEnum reuse, int flags) {
-        if (reuse == null || !(reuse instanceof MemoryDocsEnum)) {
-          reuse = new MemoryDocsEnum();
-        }
-        return ((MemoryDocsEnum) reuse).reset(liveDocs, info.sliceArray.freq[info.sortedTerms[termUpto]]);
+        return docsAndPositions(liveDocs, reuse, flags);
       }
 
       @Override
-      public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) {
-        if (reuse == null || !(reuse instanceof MemoryDocsAndPositionsEnum)) {
-          reuse = new MemoryDocsAndPositionsEnum();
+      public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) {
+        if (reuse == null || !(reuse instanceof MemoryDocsEnum)) {
+          reuse = new MemoryDocsEnum();
         }
         final int ord = info.sortedTerms[termUpto];
-        return ((MemoryDocsAndPositionsEnum) reuse).reset(liveDocs, info.sliceArray.start[ord], info.sliceArray.end[ord], info.sliceArray.freq[ord]);
+        return ((MemoryDocsEnum) reuse).reset(liveDocs, info.sliceArray.start[ord], info.sliceArray.end[ord], info.sliceArray.freq[ord]);
       }
 
       @Override
@@ -998,65 +994,21 @@
     }
     
     private class MemoryDocsEnum extends DocsEnum {
-      private boolean hasNext;
-      private Bits liveDocs;
-      private int doc = -1;
-      private int freq;
-
-      public DocsEnum reset(Bits liveDocs, int freq) {
-        this.liveDocs = liveDocs;
-        hasNext = true;
-        doc = -1;
-        this.freq = freq;
-        return this;
-      }
-
-      @Override
-      public int docID() {
-        return doc;
-      }
-
-      @Override
-      public int nextDoc() {
-        if (hasNext && (liveDocs == null || liveDocs.get(0))) {
-          hasNext = false;
-          return doc = 0;
-        } else {
-          return doc = NO_MORE_DOCS;
-        }
-      }
-
-      @Override
-      public int advance(int target) throws IOException {
-        return slowAdvance(target);
-      }
-
-      @Override
-      public int freq() throws IOException {
-        return freq;
-      }
-
-      @Override
-      public long cost() {
-        return 1;
-      }
-    }
-    
-    private class MemoryDocsAndPositionsEnum extends DocsAndPositionsEnum {
       private int posUpto; // for assert
       private boolean hasNext;
       private Bits liveDocs;
       private int doc = -1;
       private SliceReader sliceReader;
       private int freq;
+      private int pos;
       private int startOffset;
       private int endOffset;
       
-      public MemoryDocsAndPositionsEnum() {
+      public MemoryDocsEnum() {
         this.sliceReader = new SliceReader(intBlockPool);
       }
 
-      public DocsAndPositionsEnum reset(Bits liveDocs, int start, int end, int freq) {
+      public DocsEnum reset(Bits liveDocs, int start, int end, int freq) {
         this.liveDocs = liveDocs;
         this.sliceReader.reset(start, end);
         posUpto = 0; // for assert
@@ -1074,6 +1026,7 @@
 
       @Override
       public int nextDoc() {
+        pos = -1;
         if (hasNext && (liveDocs == null || liveDocs.get(0))) {
           hasNext = false;
           return doc = 0;
@@ -1094,10 +1047,12 @@
 
       @Override
       public int nextPosition() {
-        assert posUpto++ < freq;
+        //assert posUpto++ < freq;
+        if (posUpto++ >= freq)
+          return NO_MORE_POSITIONS;
         assert !sliceReader.endOfSlice() : " stores offsets : " + startOffset;
         if (storeOffsets) {
-          int pos = sliceReader.readInt();
+          pos = sliceReader.readInt();
           startOffset = sliceReader.readInt();
           endOffset = sliceReader.readInt();
           return pos;
@@ -1107,6 +1062,16 @@
       }
 
       @Override
+      public int startPosition() throws IOException {
+        return pos;
+      }
+
+      @Override
+      public int endPosition() throws IOException {
+        return pos;
+      }
+
+      @Override
       public int startOffset() {
         return startOffset;
       }
diff --git a/lucene/memory/src/test/org/apache/lucene/index/memory/TestMemoryIndexAgainstRAMDir.java b/lucene/memory/src/test/org/apache/lucene/index/memory/TestMemoryIndexAgainstRAMDir.java
index 65c140c..f5e8995 100644
--- a/lucene/memory/src/test/org/apache/lucene/index/memory/TestMemoryIndexAgainstRAMDir.java
+++ b/lucene/memory/src/test/org/apache/lucene/index/memory/TestMemoryIndexAgainstRAMDir.java
@@ -42,7 +42,6 @@
 import org.apache.lucene.document.TextField;
 import org.apache.lucene.index.CompositeReader;
 import org.apache.lucene.index.DirectoryReader;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.Fields;
 import org.apache.lucene.index.IndexOptions;
@@ -68,8 +67,8 @@
 import org.apache.lucene.search.spans.SpanQuery;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.RAMDirectory;
-import org.apache.lucene.util.ByteBlockPool.Allocator;
 import org.apache.lucene.util.ByteBlockPool;
+import org.apache.lucene.util.ByteBlockPool.Allocator;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.IOUtils;
 import org.apache.lucene.util.LineFileDocs;
@@ -198,9 +197,9 @@
           while(iwTermsIter.next() != null) {
             assertNotNull(memTermsIter.next());
             assertEquals(iwTermsIter.term(), memTermsIter.term());
-            DocsAndPositionsEnum iwDocsAndPos = iwTermsIter.docsAndPositions(null, null);
-            DocsAndPositionsEnum memDocsAndPos = memTermsIter.docsAndPositions(null, null);
-            while(iwDocsAndPos.nextDoc() != DocsAndPositionsEnum.NO_MORE_DOCS) {
+            DocsEnum iwDocsAndPos = iwTermsIter.docsAndPositions(null, null);
+            DocsEnum memDocsAndPos = memTermsIter.docsAndPositions(null, null);
+            while(iwDocsAndPos.nextDoc() != DocsEnum.NO_MORE_DOCS) {
               assertEquals(iwDocsAndPos.docID(), memDocsAndPos.nextDoc());
               assertEquals(iwDocsAndPos.freq(), memDocsAndPos.freq());
               for (int i = 0; i < iwDocsAndPos.freq(); i++) {
@@ -219,7 +218,7 @@
             assertEquals(iwTermsIter.term(), memTermsIter.term());
             DocsEnum iwDocsAndPos = iwTermsIter.docs(null, null);
             DocsEnum memDocsAndPos = memTermsIter.docs(null, null);
-            while(iwDocsAndPos.nextDoc() != DocsAndPositionsEnum.NO_MORE_DOCS) {
+            while(iwDocsAndPos.nextDoc() != DocsEnum.NO_MORE_DOCS) {
               assertEquals(iwDocsAndPos.docID(), memDocsAndPos.nextDoc());
               assertEquals(iwDocsAndPos.freq(), memDocsAndPos.freq());
             }
@@ -345,7 +344,7 @@
       memory.addField("foo", "bar", analyzer);
       LeafReader reader = (LeafReader) memory.createSearcher().getIndexReader();
       assertEquals(1, reader.terms("foo").getSumTotalTermFreq());
-      DocsAndPositionsEnum disi = reader.termPositionsEnum(new Term("foo", "bar"));
+      DocsEnum disi = reader.termPositionsEnum(new Term("foo", "bar"));
       int docid = disi.docID();
       assertEquals(-1, docid);
       assertTrue(disi.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
@@ -517,8 +516,8 @@
       assertNotNull(memTermEnum.next());
       assertThat(termEnum.totalTermFreq(), equalTo(memTermEnum.totalTermFreq()));
 
-      DocsAndPositionsEnum docsPosEnum = termEnum.docsAndPositions(null, null, 0);
-      DocsAndPositionsEnum memDocsPosEnum = memTermEnum.docsAndPositions(null, null, 0);
+      DocsEnum docsPosEnum = termEnum.docsAndPositions(null, null, 0);
+      DocsEnum memDocsPosEnum = memTermEnum.docsAndPositions(null, null, 0);
       String currentTerm = termEnum.term().utf8ToString();
 
       assertThat("Token mismatch for field: " + field_name, currentTerm, equalTo(memTermEnum.term().utf8ToString()));
diff --git a/lucene/misc/src/java/org/apache/lucene/index/sorter/Sorter.java b/lucene/misc/src/java/org/apache/lucene/index/sorter/Sorter.java
index a3c75ae..e77f7cf 100644
--- a/lucene/misc/src/java/org/apache/lucene/index/sorter/Sorter.java
+++ b/lucene/misc/src/java/org/apache/lucene/index/sorter/Sorter.java
@@ -20,8 +20,9 @@
 import java.io.IOException;
 import java.util.Comparator;
 
-import org.apache.lucene.index.LeafReader;
 import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReader;
+import org.apache.lucene.search.FakeScorer;
 import org.apache.lucene.search.FieldComparator;
 import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.Sort;
@@ -261,25 +262,6 @@
     return getID();
   }
   
-  static final Scorer FAKESCORER = new Scorer(null) {
-    
-    @Override
-    public float score() throws IOException { throw new UnsupportedOperationException(); }
-    
-    @Override
-    public int freq() throws IOException { throw new UnsupportedOperationException(); }
-
-    @Override
-    public int docID() { throw new UnsupportedOperationException(); }
-
-    @Override
-    public int nextDoc() throws IOException { throw new UnsupportedOperationException(); }
-
-    @Override
-    public int advance(int target) throws IOException { throw new UnsupportedOperationException(); }
-
-    @Override
-    public long cost() { throw new UnsupportedOperationException(); }
-  };
+  static final Scorer FAKESCORER = new FakeScorer();
   
 }
diff --git a/lucene/misc/src/java/org/apache/lucene/index/sorter/SortingLeafReader.java b/lucene/misc/src/java/org/apache/lucene/index/sorter/SortingLeafReader.java
index ff12908..d44fea2 100644
--- a/lucene/misc/src/java/org/apache/lucene/index/sorter/SortingLeafReader.java
+++ b/lucene/misc/src/java/org/apache/lucene/index/sorter/SortingLeafReader.java
@@ -23,7 +23,6 @@
 import org.apache.lucene.index.FilterLeafReader;
 import org.apache.lucene.index.LeafReader;
 import org.apache.lucene.index.BinaryDocValues;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexOptions;
 import org.apache.lucene.index.FieldInfos;
@@ -52,7 +51,7 @@
 /**
  * An {@link org.apache.lucene.index.LeafReader} which supports sorting documents by a given
  * {@link Sort}. You can use this class to sort an index as follows:
- * 
+ *
  * <pre class="prettyprint">
  * IndexWriter writer; // writer to which the sorted index will be added
  * DirectoryReader reader; // reader on the input index
@@ -62,7 +61,7 @@
  * writer.close();
  * reader.close();
  * </pre>
- * 
+ *
  * @lucene.experimental
  */
 public class SortingLeafReader extends FilterLeafReader {
@@ -94,7 +93,7 @@
 
     private final Sorter.DocMap docMap;
     private final IndexOptions indexOptions;
-    
+
     public SortingTerms(final Terms in, IndexOptions indexOptions, final Sorter.DocMap docMap) {
       super(in);
       this.docMap = docMap;
@@ -118,7 +117,7 @@
 
     final Sorter.DocMap docMap; // pkg-protected to avoid synthetic accessor methods
     private final IndexOptions indexOptions;
-    
+
     public SortingTermsEnum(final TermsEnum in, Sorter.DocMap docMap, IndexOptions indexOptions) {
       super(in);
       this.docMap = docMap;
@@ -146,6 +145,10 @@
 
     @Override
     public DocsEnum docs(Bits liveDocs, DocsEnum reuse, final int flags) throws IOException {
+
+      if ((flags & DocsEnum.FLAG_POSITIONS) >= DocsEnum.FLAG_POSITIONS)
+        return docsAndPositions(liveDocs, reuse, flags);
+
       final DocsEnum inReuse;
       final SortingDocsEnum wrapReuse;
       if (reuse != null && reuse instanceof SortingDocsEnum) {
@@ -164,8 +167,8 @@
     }
 
     @Override
-    public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, final int flags) throws IOException {
-      final DocsAndPositionsEnum inReuse;
+    public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, final int flags) throws IOException {
+      final DocsEnum inReuse;
       final SortingDocsAndPositionsEnum wrapReuse;
       if (reuse != null && reuse instanceof SortingDocsAndPositionsEnum) {
         // if we're asked to reuse the given DocsEnum and it is Sorting, return
@@ -177,7 +180,7 @@
         inReuse = reuse;
       }
 
-      final DocsAndPositionsEnum inDocsAndPositions = in.docsAndPositions(newToOld(liveDocs), inReuse, flags);
+      final DocsEnum inDocsAndPositions = in.docsAndPositions(newToOld(liveDocs), inReuse, flags);
       if (inDocsAndPositions == null) {
         return null;
       }
@@ -193,10 +196,10 @@
   }
 
   private static class SortingBinaryDocValues extends BinaryDocValues {
-    
+
     private final BinaryDocValues in;
     private final Sorter.DocMap docMap;
-    
+
     SortingBinaryDocValues(BinaryDocValues in, Sorter.DocMap docMap) {
       this.in = in;
       this.docMap = docMap;
@@ -207,7 +210,7 @@
       return in.get(docMap.newToOld(docID));
     }
   }
-  
+
   private static class SortingNumericDocValues extends NumericDocValues {
 
     private final NumericDocValues in;
@@ -223,33 +226,33 @@
       return in.get(docMap.newToOld(docID));
     }
   }
-  
+
   private static class SortingSortedNumericDocValues extends SortedNumericDocValues {
-    
+
     private final SortedNumericDocValues in;
     private final Sorter.DocMap docMap;
-    
+
     SortingSortedNumericDocValues(SortedNumericDocValues in, DocMap docMap) {
       this.in = in;
       this.docMap = docMap;
     }
-    
+
     @Override
     public int count() {
       return in.count();
     }
-    
+
     @Override
     public void setDocument(int doc) {
       in.setDocument(docMap.newToOld(doc));
     }
-    
+
     @Override
     public long valueAt(int index) {
       return in.valueAt(index);
     }
   }
-  
+
   private static class SortingBits implements Bits {
 
     private final Bits in;
@@ -270,12 +273,12 @@
       return in.length();
     }
   }
-  
+
   private static class SortingSortedDocValues extends SortedDocValues {
-    
+
     private final SortedDocValues in;
     private final Sorter.DocMap docMap;
-    
+
     SortingSortedDocValues(SortedDocValues in, Sorter.DocMap docMap) {
       this.in = in;
       this.docMap = docMap;
@@ -306,12 +309,12 @@
       return in.lookupTerm(key);
     }
   }
-  
+
   private static class SortingSortedSetDocValues extends SortedSetDocValues {
-    
+
     private final SortedSetDocValues in;
     private final Sorter.DocMap docMap;
-    
+
     SortingSortedSetDocValues(SortedSetDocValues in, Sorter.DocMap docMap) {
       this.in = in;
       this.docMap = docMap;
@@ -344,14 +347,14 @@
   }
 
   static class SortingDocsEnum extends FilterDocsEnum {
-    
+
     private static final class DocFreqSorter extends TimSorter {
-      
+
       private int[] docs;
       private int[] freqs;
       private final int[] tmpDocs;
       private int[] tmpFreqs;
-      
+
       public DocFreqSorter(int maxDoc) {
         super(maxDoc / 64);
         this.tmpDocs = new int[maxDoc / 64];
@@ -369,13 +372,13 @@
       protected int compare(int i, int j) {
         return docs[i] - docs[j];
       }
-      
+
       @Override
       protected void swap(int i, int j) {
         int tmpDoc = docs[i];
         docs[i] = docs[j];
         docs[j] = tmpDoc;
-        
+
         if (freqs != null) {
           int tmpFreq = freqs[i];
           freqs[i] = freqs[j];
@@ -483,43 +486,43 @@
       // don't bother to implement efficiently for now.
       return slowAdvance(target);
     }
-    
+
     @Override
     public int docID() {
       return docIt < 0 ? -1 : docIt >= upto ? NO_MORE_DOCS : docs[docIt];
     }
-    
+
     @Override
     public int freq() throws IOException {
       return withFreqs && docIt < upto ? freqs[docIt] : 1;
     }
-    
+
     @Override
     public int nextDoc() throws IOException {
       if (++docIt >= upto) return NO_MORE_DOCS;
       return docs[docIt];
     }
-    
+
     /** Returns the wrapped {@link DocsEnum}. */
     DocsEnum getWrapped() {
       return in;
     }
   }
-  
-  static class SortingDocsAndPositionsEnum extends FilterDocsAndPositionsEnum {
-    
+
+  static class SortingDocsAndPositionsEnum extends FilterDocsEnum {
+
     /**
      * A {@link TimSorter} which sorts two parallel arrays of doc IDs and
      * offsets in one go. Everytime a doc ID is 'swapped', its correponding offset
      * is swapped too.
      */
     private static final class DocOffsetSorter extends TimSorter {
-      
+
       private int[] docs;
       private long[] offsets;
       private final int[] tmpDocs;
       private final long[] tmpOffsets;
-      
+
       public DocOffsetSorter(int maxDoc) {
         super(maxDoc / 64);
         this.tmpDocs = new int[maxDoc / 64];
@@ -535,13 +538,13 @@
       protected int compare(int i, int j) {
         return docs[i] - docs[j];
       }
-      
+
       @Override
       protected void swap(int i, int j) {
         int tmpDoc = docs[i];
         docs[i] = docs[j];
         docs[j] = tmpDoc;
-        
+
         long tmpOffset = offsets[i];
         offsets[i] = offsets[j];
         offsets[j] = tmpOffset;
@@ -570,16 +573,16 @@
         return tmpDocs[i] - docs[j];
       }
     }
-    
+
     private final int maxDoc;
     private final DocOffsetSorter sorter;
     private int[] docs;
     private long[] offsets;
     private final int upto;
-    
+
     private final IndexInput postingInput;
     private final boolean storeOffsets;
-    
+
     private int docIt = -1;
     private int pos;
     private int startOffset = -1;
@@ -589,7 +592,7 @@
 
     private final RAMFile file;
 
-    SortingDocsAndPositionsEnum(int maxDoc, SortingDocsAndPositionsEnum reuse, final DocsAndPositionsEnum in, Sorter.DocMap docMap, boolean storeOffsets) throws IOException {
+    SortingDocsAndPositionsEnum(int maxDoc, SortingDocsAndPositionsEnum reuse, final DocsEnum in, Sorter.DocMap docMap, boolean storeOffsets) throws IOException {
       super(in);
       this.maxDoc = maxDoc;
       this.storeOffsets = storeOffsets;
@@ -632,14 +635,14 @@
     }
 
     // for testing
-    boolean reused(DocsAndPositionsEnum other) {
+    boolean reused(DocsEnum other) {
       if (other == null || !(other instanceof SortingDocsAndPositionsEnum)) {
         return false;
       }
       return docs == ((SortingDocsAndPositionsEnum) other).docs;
     }
 
-    private void addPositions(final DocsAndPositionsEnum in, final IndexOutput out) throws IOException {
+    private void addPositions(final DocsEnum in, final IndexOutput out) throws IOException {
       int freq = in.freq();
       out.writeVInt(freq);
       int previousPosition = 0;
@@ -648,7 +651,7 @@
         final int pos = in.nextPosition();
         final BytesRef payload = in.getPayload();
         // The low-order bit of token is set only if there is a payload, the
-        // previous bits are the delta-encoded position. 
+        // previous bits are the delta-encoded position.
         final int token = (pos - previousPosition) << 1 | (payload == null ? 0 : 1);
         out.writeVInt(token);
         previousPosition = pos;
@@ -665,34 +668,34 @@
         }
       }
     }
-    
+
     @Override
     public int advance(final int target) throws IOException {
       // need to support it for checkIndex, but in practice it won't be called, so
       // don't bother to implement efficiently for now.
       return slowAdvance(target);
     }
-    
+
     @Override
     public int docID() {
       return docIt < 0 ? -1 : docIt >= upto ? NO_MORE_DOCS : docs[docIt];
     }
-    
+
     @Override
     public int endOffset() throws IOException {
       return endOffset;
     }
-    
+
     @Override
     public int freq() throws IOException {
       return currFreq;
     }
-    
+
     @Override
     public BytesRef getPayload() throws IOException {
       return payload.length == 0 ? null : payload;
     }
-    
+
     @Override
     public int nextDoc() throws IOException {
       if (++docIt >= upto) return DocIdSetIterator.NO_MORE_DOCS;
@@ -703,7 +706,7 @@
       endOffset = 0;
       return docs[docIt];
     }
-    
+
     @Override
     public int nextPosition() throws IOException {
       final int token = postingInput.readVInt();
@@ -724,14 +727,14 @@
       }
       return pos;
     }
-    
+
     @Override
     public int startOffset() throws IOException {
       return startOffset;
     }
 
-    /** Returns the wrapped {@link DocsAndPositionsEnum}. */
-    DocsAndPositionsEnum getWrapped() {
+    /** Returns the wrapped {@link DocsEnum}. */
+    DocsEnum getWrapped() {
       return in;
     }
   }
@@ -767,12 +770,12 @@
   public void document(final int docID, final StoredFieldVisitor visitor) throws IOException {
     in.document(docMap.newToOld(docID), visitor);
   }
-  
+
   @Override
   public Fields fields() throws IOException {
     return new SortingFields(in.fields(), in.getFieldInfos(), docMap);
   }
-  
+
   @Override
   public BinaryDocValues getBinaryDocValues(String field) throws IOException {
     BinaryDocValues oldDocValues = in.getBinaryDocValues(field);
@@ -782,7 +785,7 @@
       return new SortingBinaryDocValues(oldDocValues, docMap);
     }
   }
-  
+
   @Override
   public Bits getLiveDocs() {
     final Bits inLiveDocs = in.getLiveDocs();
@@ -792,7 +795,7 @@
       return new SortingBits(inLiveDocs, docMap);
     }
   }
-  
+
   @Override
   public NumericDocValues getNormValues(String field) throws IOException {
     final NumericDocValues norm = in.getNormValues(field);
@@ -809,7 +812,7 @@
     if (oldDocValues == null) return null;
     return new SortingNumericDocValues(oldDocValues, docMap);
   }
-  
+
   @Override
   public SortedNumericDocValues getSortedNumericDocValues(String field)
       throws IOException {
@@ -830,7 +833,7 @@
       return new SortingSortedDocValues(sortedDV, docMap);
     }
   }
-  
+
   @Override
   public SortedSetDocValues getSortedSetDocValues(String field) throws IOException {
     SortedSetDocValues sortedSetDV = in.getSortedSetDocValues(field);
@@ -838,7 +841,7 @@
       return null;
     } else {
       return new SortingSortedSetDocValues(sortedSetDV, docMap);
-    }  
+    }
   }
 
   @Override
@@ -855,5 +858,5 @@
   public Fields getTermVectors(final int docID) throws IOException {
     return in.getTermVectors(docMap.newToOld(docID));
   }
-  
+
 }
diff --git a/lucene/misc/src/java/org/apache/lucene/uninverting/DocTermOrds.java b/lucene/misc/src/java/org/apache/lucene/uninverting/DocTermOrds.java
index f6acd5f..b13fd99 100644
--- a/lucene/misc/src/java/org/apache/lucene/uninverting/DocTermOrds.java
+++ b/lucene/misc/src/java/org/apache/lucene/uninverting/DocTermOrds.java
@@ -25,7 +25,6 @@
 import org.apache.lucene.codecs.PostingsFormat; // javadocs
 import org.apache.lucene.index.DocValues;
 import org.apache.lucene.index.DocValuesType;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.LeafReader;
@@ -618,7 +617,7 @@
     }
 
     @Override    
-    public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
+    public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
       return termsEnum.docsAndPositions(liveDocs, reuse, flags);
     }
 
diff --git a/lucene/misc/src/java/org/apache/lucene/uninverting/FieldCacheImpl.java b/lucene/misc/src/java/org/apache/lucene/uninverting/FieldCacheImpl.java
index db88397..c8078df 100644
--- a/lucene/misc/src/java/org/apache/lucene/uninverting/FieldCacheImpl.java
+++ b/lucene/misc/src/java/org/apache/lucene/uninverting/FieldCacheImpl.java
@@ -17,15 +17,6 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.WeakHashMap;
-
 import org.apache.lucene.index.BinaryDocValues;
 import org.apache.lucene.index.DocValues;
 import org.apache.lucene.index.DocValuesType;
@@ -51,6 +42,15 @@
 import org.apache.lucene.util.packed.PackedInts;
 import org.apache.lucene.util.packed.PackedLongValues;
 
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+
 /**
  * Expert: The default cache implementation, storing all values in memory.
  * A WeakHashMap is used for storage.
diff --git a/lucene/misc/src/test/org/apache/lucene/index/sorter/SorterTestBase.java b/lucene/misc/src/test/org/apache/lucene/index/sorter/SorterTestBase.java
index 48b8a28..1328f96 100644
--- a/lucene/misc/src/test/org/apache/lucene/index/sorter/SorterTestBase.java
+++ b/lucene/misc/src/test/org/apache/lucene/index/sorter/SorterTestBase.java
@@ -17,13 +17,6 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Random;
-
 import org.apache.lucene.analysis.MockAnalyzer;
 import org.apache.lucene.analysis.TokenStream;
 import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
@@ -31,8 +24,8 @@
 import org.apache.lucene.analysis.tokenattributes.PayloadAttribute;
 import org.apache.lucene.document.BinaryDocValuesField;
 import org.apache.lucene.document.Document;
-import org.apache.lucene.document.Field.Store;
 import org.apache.lucene.document.Field;
+import org.apache.lucene.document.Field.Store;
 import org.apache.lucene.document.FieldType;
 import org.apache.lucene.document.NumericDocValuesField;
 import org.apache.lucene.document.SortedDocValuesField;
@@ -42,7 +35,6 @@
 import org.apache.lucene.document.TextField;
 import org.apache.lucene.index.BinaryDocValues;
 import org.apache.lucene.index.DirectoryReader;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInvertState;
 import org.apache.lucene.index.IndexOptions;
@@ -57,9 +49,8 @@
 import org.apache.lucene.index.SortedSetDocValues;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.index.Terms;
-import org.apache.lucene.index.TermsEnum.SeekStatus;
 import org.apache.lucene.index.TermsEnum;
-import org.apache.lucene.index.sorter.SortingLeafReader.SortingDocsAndPositionsEnum;
+import org.apache.lucene.index.TermsEnum.SeekStatus;
 import org.apache.lucene.index.sorter.SortingLeafReader.SortingDocsEnum;
 import org.apache.lucene.search.CollectionStatistics;
 import org.apache.lucene.search.DocIdSetIterator;
@@ -75,6 +66,13 @@
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+
 public abstract class SorterTestBase extends LuceneTestCase {
 
   static final class NormsSimilarity extends Similarity {
@@ -254,7 +252,7 @@
   public void testDocsAndPositionsEnum() throws Exception {
     TermsEnum termsEnum = sortedReader.terms(DOC_POSITIONS_FIELD).iterator(null);
     assertEquals(SeekStatus.FOUND, termsEnum.seekCeil(new BytesRef(DOC_POSITIONS_TERM)));
-    DocsAndPositionsEnum sortedPositions = termsEnum.docsAndPositions(null, null);
+    DocsEnum sortedPositions = termsEnum.docsAndPositions(null, null);
     int doc;
     
     // test nextDoc()
@@ -270,10 +268,10 @@
     }
     
     // test advance()
-    final DocsAndPositionsEnum reuse = sortedPositions;
+    final DocsEnum reuse = sortedPositions;
     sortedPositions = termsEnum.docsAndPositions(null, reuse);
-    if (sortedPositions instanceof SortingDocsAndPositionsEnum) {
-      assertTrue(((SortingDocsAndPositionsEnum) sortedPositions).reused(reuse)); // make sure reuse worked
+    if (sortedPositions instanceof SortingDocsEnum) {
+      assertTrue(((SortingDocsEnum) sortedPositions).reused(reuse)); // make sure reuse worked
     }
     doc = 0;
     while ((doc = sortedPositions.advance(doc + TestUtil.nextInt(random(), 1, 5))) != DocIdSetIterator.NO_MORE_DOCS) {
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java b/lucene/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java
index e48e8f2..fb124ba 100644
--- a/lucene/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java
@@ -18,23 +18,24 @@
  */
 
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Set;
-import java.util.Arrays;
 
-import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.queries.function.FunctionQuery;
 import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.search.ComplexExplanation;
 import org.apache.lucene.search.Explanation;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.Weight;
-import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Weight;
 import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.ToStringUtils;
 
 /**
@@ -234,14 +235,14 @@
     }
 
     @Override
-    public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
-      Scorer subQueryScorer = subQueryWeight.scorer(context, acceptDocs);
+    public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
+      Scorer subQueryScorer = subQueryWeight.scorer(context, flags, acceptDocs);
       if (subQueryScorer == null) {
         return null;
       }
       Scorer[] valSrcScorers = new Scorer[valSrcWeights.length];
       for(int i = 0; i < valSrcScorers.length; i++) {
-         valSrcScorers[i] = valSrcWeights[i].scorer(context, acceptDocs);
+         valSrcScorers[i] = valSrcWeights[i].scorer(context, flags, acceptDocs);
       }
       return new CustomScorer(CustomScoreQuery.this.getCustomScoreProvider(context), this, queryWeight, subQueryScorer, valSrcScorers);
     }
@@ -291,6 +292,8 @@
     private final CustomScoreProvider provider;
     private final float[] vScores; // reused in score() to avoid allocating this array for each doc
 
+    // TODO : can we use FilterScorer here instead?
+
     // constructor
     private CustomScorer(CustomScoreProvider provider, CustomWeight w, float qWeight,
         Scorer subQueryScorer, Scorer[] valSrcScorers) {
@@ -333,6 +336,36 @@
     }
 
     @Override
+    public int nextPosition() throws IOException {
+      return subQueryScorer.nextPosition();
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return subQueryScorer.startPosition();
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return subQueryScorer.endPosition();
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return subQueryScorer.startOffset();
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return subQueryScorer.endOffset();
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return subQueryScorer.getPayload();
+    }
+
+    @Override
     public Collection<ChildScorer> getChildren() {
       return Collections.singleton(new ChildScorer(subQueryScorer, "CUSTOM"));
     }
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java b/lucene/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java
index 90bb2e0..cb46dec 100644
--- a/lucene/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java
@@ -17,18 +17,24 @@
  * limitations under the License.
  */
 
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.search.*;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.util.Bits;
-import org.apache.lucene.util.ToStringUtils;
-
 import java.io.IOException;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Set;
 import java.util.Map;
+import java.util.Set;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.ComplexExplanation;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.FilterScorer;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Weight;
+import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.ToStringUtils;
 
 /**
  * Query that is boosted by a ValueSource
@@ -97,8 +103,8 @@
     }
 
     @Override
-    public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
-      Scorer subQueryScorer = qWeight.scorer(context, acceptDocs);
+    public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
+      Scorer subQueryScorer = qWeight.scorer(context, flags, acceptDocs);
       if (subQueryScorer == null) {
         return null;
       }
@@ -122,41 +128,24 @@
   }
 
 
-  private class CustomScorer extends Scorer {
+  private class CustomScorer extends FilterScorer {
     private final BoostedQuery.BoostedWeight weight;
     private final float qWeight;
-    private final Scorer scorer;
     private final FunctionValues vals;
     private final LeafReaderContext readerContext;
 
     private CustomScorer(LeafReaderContext readerContext, BoostedQuery.BoostedWeight w, float qWeight,
         Scorer scorer, ValueSource vs) throws IOException {
-      super(w);
+      super(scorer);
       this.weight = w;
       this.qWeight = qWeight;
-      this.scorer = scorer;
       this.readerContext = readerContext;
       this.vals = vs.getValues(weight.fcontext, readerContext);
     }
 
-    @Override
-    public int docID() {
-      return scorer.docID();
-    }
-
-    @Override
-    public int advance(int target) throws IOException {
-      return scorer.advance(target);
-    }
-
-    @Override
-    public int nextDoc() throws IOException {
-      return scorer.nextDoc();
-    }
-
     @Override   
     public float score() throws IOException {
-      float score = qWeight * scorer.score() * vals.floatVal(scorer.docID());
+      float score = qWeight * in.score() * vals.floatVal(in.docID());
 
       // Current Lucene priority queues can't handle NaN and -Infinity, so
       // map to -Float.MAX_VALUE. This conditional handles both -infinity
@@ -165,13 +154,8 @@
     }
 
     @Override
-    public int freq() throws IOException {
-      return scorer.freq();
-    }
-
-    @Override
     public Collection<ChildScorer> getChildren() {
-      return Collections.singleton(new ChildScorer(scorer, "CUSTOM"));
+      return Collections.singleton(new ChildScorer(in, "CUSTOM"));
     }
 
     public Explanation explain(int doc) throws IOException {
@@ -187,10 +171,6 @@
       return res;
     }
 
-    @Override
-    public long cost() {
-      return scorer.cost();
-    }
   }
 
 
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java b/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java
index 4abc312..92c3cd8 100644
--- a/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java
@@ -17,15 +17,22 @@
  * limitations under the License.
  */
 
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.search.*;
-import org.apache.lucene.util.Bits;
-
 import java.io.IOException;
-import java.util.Set;
 import java.util.Map;
+import java.util.Set;
+
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.ComplexExplanation;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Weight;
+import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
 
 
 /**
@@ -89,13 +96,13 @@
     }
 
     @Override
-    public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
+    public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
       return new AllScorer(context, acceptDocs, this, queryWeight);
     }
 
     @Override
     public Explanation explain(LeafReaderContext context, int doc) throws IOException {
-      return ((AllScorer)scorer(context, context.reader().getLiveDocs())).explain(doc);
+      return ((AllScorer)scorer(context, DocsEnum.FLAG_FREQS, context.reader().getLiveDocs())).explain(doc);
     }
   }
 
@@ -166,6 +173,36 @@
       return 1;
     }
 
+    @Override
+    public int nextPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
     public Explanation explain(int doc) throws IOException {
       float sc = qWeight * vals.floatVal(doc);
 
@@ -177,6 +214,7 @@
       result.addDetail(new Explanation(weight.queryNorm,"queryNorm"));
       return result;
     }
+
   }
 
 
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java b/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java
index ec8aced..def8623 100644
--- a/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java
@@ -17,12 +17,13 @@
  * limitations under the License.
  */
 
+import java.io.IOException;
+
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.MultiFields;
 import org.apache.lucene.search.Scorer;
 import org.apache.lucene.util.Bits;
-
-import java.io.IOException;
+import org.apache.lucene.util.BytesRef;
 
 /**
  * {@link Scorer} which returns the result of {@link FunctionValues#floatVal(int)} as
@@ -93,6 +94,36 @@
   }
 
   @Override
+  public int nextPosition() throws IOException {
+    return NO_MORE_POSITIONS;
+  }
+
+  @Override
+  public int startPosition() throws IOException {
+    return NO_MORE_POSITIONS;
+  }
+
+  @Override
+  public int endPosition() throws IOException {
+    return NO_MORE_POSITIONS;
+  }
+
+  @Override
+  public int startOffset() throws IOException {
+    return -1;
+  }
+
+  @Override
+  public int endOffset() throws IOException {
+    return -1;
+  }
+
+  @Override
+  public BytesRef getPayload() throws IOException {
+    return null;
+  }
+
+  @Override
   public long cost() {
     return maxDoc;
   }
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/QueryValueSource.java b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/QueryValueSource.java
index 3d57315..957a8b7 100644
--- a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/QueryValueSource.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/QueryValueSource.java
@@ -17,12 +17,16 @@
 
 package org.apache.lucene.queries.function.valuesource;
 
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.ReaderUtil;
 import org.apache.lucene.queries.function.FunctionValues;
 import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.queries.function.docvalues.FloatDocValues;
-import org.apache.lucene.search.*;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Weight;
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.mutable.MutableValue;
 import org.apache.lucene.util.mutable.MutableValueFloat;
@@ -123,7 +127,7 @@
     try {
       if (doc < lastDocRequested) {
         if (noMatches) return defVal;
-        scorer = weight.scorer(readerContext, acceptDocs);
+        scorer = weight.scorer(readerContext, DocsEnum.FLAG_FREQS, acceptDocs);
         if (scorer==null) {
           noMatches = true;
           return defVal;
@@ -154,7 +158,7 @@
     try {
       if (doc < lastDocRequested) {
         if (noMatches) return false;
-        scorer = weight.scorer(readerContext, acceptDocs);
+        scorer = weight.scorer(readerContext, DocsEnum.FLAG_FREQS, acceptDocs);
         scorerDoc = -1;
         if (scorer==null) {
           noMatches = true;
@@ -212,7 +216,7 @@
             mval.exists = false;
             return;
           }
-          scorer = weight.scorer(readerContext, acceptDocs);
+          scorer = weight.scorer(readerContext, DocsEnum.FLAG_FREQS, acceptDocs);
           scorerDoc = -1;
           if (scorer==null) {
             noMatches = true;
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TFValueSource.java b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TFValueSource.java
index 4d73d55..0536cd5 100644
--- a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TFValueSource.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TFValueSource.java
@@ -17,7 +17,14 @@
  * limitations under the License.
  */
 
-import org.apache.lucene.index.*;
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.index.Fields;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.Terms;
+import org.apache.lucene.index.TermsEnum;
 import org.apache.lucene.queries.function.FunctionValues;
 import org.apache.lucene.queries.function.docvalues.FloatDocValues;
 import org.apache.lucene.search.DocIdSetIterator;
@@ -25,9 +32,6 @@
 import org.apache.lucene.search.similarities.TFIDFSimilarity;
 import org.apache.lucene.util.BytesRef;
 
-import java.io.IOException;
-import java.util.Map;
-
 /** 
  * Function that returns {@link TFIDFSimilarity#tf(float)}
  * for every document.
@@ -84,6 +88,36 @@
             }
 
             @Override
+            public int nextPosition() throws IOException {
+              return NO_MORE_POSITIONS;
+            }
+
+            @Override
+            public int startPosition() throws IOException {
+              return NO_MORE_POSITIONS;
+            }
+
+            @Override
+            public int endPosition() throws IOException {
+              return NO_MORE_POSITIONS;
+            }
+
+            @Override
+            public int startOffset() throws IOException {
+              return -1;
+            }
+
+            @Override
+            public int endOffset() throws IOException {
+              return -1;
+            }
+
+            @Override
+            public BytesRef getPayload() throws IOException {
+              return null;
+            }
+
+            @Override
             public int docID() {
               return DocIdSetIterator.NO_MORE_DOCS;
             }
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TermFreqValueSource.java b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TermFreqValueSource.java
index b5e4bc2..61ac8dc 100644
--- a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TermFreqValueSource.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TermFreqValueSource.java
@@ -17,15 +17,19 @@
 
 package org.apache.lucene.queries.function.valuesource;
 
-import org.apache.lucene.index.*;
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.index.Fields;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.Terms;
+import org.apache.lucene.index.TermsEnum;
 import org.apache.lucene.queries.function.FunctionValues;
 import org.apache.lucene.queries.function.docvalues.IntDocValues;
 import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.util.BytesRef;
 
-import java.io.IOException;
-import java.util.Map;
-
 /**
  * Function that returns {@link DocsEnum#freq()} for the
  * supplied term in every document.
@@ -77,6 +81,36 @@
             }
 
             @Override
+            public int nextPosition() throws IOException {
+              return NO_MORE_POSITIONS;
+            }
+
+            @Override
+            public int startPosition() throws IOException {
+              return NO_MORE_POSITIONS;
+            }
+
+            @Override
+            public int endPosition() throws IOException {
+              return NO_MORE_POSITIONS;
+            }
+
+            @Override
+            public int startOffset() throws IOException {
+              return -1;
+            }
+
+            @Override
+            public int endOffset() throws IOException {
+              return -1;
+            }
+
+            @Override
+            public BytesRef getPayload() throws IOException {
+              throw new UnsupportedOperationException();
+            }
+
+            @Override
             public int docID() {
               return DocIdSetIterator.NO_MORE_DOCS;
             }
diff --git a/lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/IDVersionPostingsReader.java b/lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/IDVersionPostingsReader.java
index b716acc..b11072a 100644
--- a/lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/IDVersionPostingsReader.java
+++ b/lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/IDVersionPostingsReader.java
@@ -22,7 +22,6 @@
 import org.apache.lucene.codecs.BlockTermState;
 import org.apache.lucene.codecs.CodecUtil;
 import org.apache.lucene.codecs.PostingsReaderBase;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.SegmentReadState;
@@ -78,8 +77,8 @@
   }
 
   @Override
-  public DocsAndPositionsEnum docsAndPositions(FieldInfo fieldInfo, BlockTermState _termState, Bits liveDocs,
-                                               DocsAndPositionsEnum reuse, int flags) {
+  public DocsEnum docsAndPositions(FieldInfo fieldInfo, BlockTermState _termState, Bits liveDocs,
+                                   DocsEnum reuse, int flags) {
     SingleDocsAndPositionsEnum posEnum;
 
     if (reuse instanceof SingleDocsAndPositionsEnum) {
diff --git a/lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/IDVersionSegmentTermsEnum.java b/lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/IDVersionSegmentTermsEnum.java
index 481e74d..ec7cdee 100644
--- a/lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/IDVersionSegmentTermsEnum.java
+++ b/lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/IDVersionSegmentTermsEnum.java
@@ -17,11 +17,7 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.io.PrintStream;
-
 import org.apache.lucene.codecs.BlockTermState;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexOptions;
 import org.apache.lucene.index.TermState;
@@ -37,6 +33,9 @@
 import org.apache.lucene.util.fst.PairOutputs.Pair;
 import org.apache.lucene.util.fst.Util;
 
+import java.io.IOException;
+import java.io.PrintStream;
+
 /** Iterates through terms in this field; this class is public so users
  *  can cast it to call {@link #seekExact(BytesRef, long)} for
  *  optimistic-concurreny, and also {@link #getVersion} to get the
@@ -1010,7 +1009,7 @@
   }
 
   @Override
-  public DocsAndPositionsEnum docsAndPositions(Bits skipDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
+  public DocsEnum docsAndPositions(Bits skipDocs, DocsEnum reuse, int flags) throws IOException {
     if (fr.fieldInfo.getIndexOptions().compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) < 0) {
       // Positions were not indexed:
       return null;
diff --git a/lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/SingleDocsAndPositionsEnum.java b/lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/SingleDocsAndPositionsEnum.java
index eecc700..d34104b 100644
--- a/lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/SingleDocsAndPositionsEnum.java
+++ b/lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/SingleDocsAndPositionsEnum.java
@@ -17,11 +17,13 @@
  * limitations under the License.
  */
 
-import org.apache.lucene.index.DocsAndPositionsEnum;
+import java.io.IOException;
+
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.BytesRef;
 
-class SingleDocsAndPositionsEnum extends DocsAndPositionsEnum {
+class SingleDocsAndPositionsEnum extends DocsEnum {
   private int doc;
   private int pos;
   private int singleDocID;
@@ -89,6 +91,16 @@
   }
 
   @Override
+  public int startPosition() throws IOException {
+    return pos;
+  }
+
+  @Override
+  public int endPosition() throws IOException {
+    return pos;
+  }
+
+  @Override
   public BytesRef getPayload() {
     return payload;
   }
diff --git a/lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/SingleDocsEnum.java b/lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/SingleDocsEnum.java
index b29619c..504d14e 100644
--- a/lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/SingleDocsEnum.java
+++ b/lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/SingleDocsEnum.java
@@ -17,8 +17,11 @@
  * limitations under the License.
  */
 
+import java.io.IOException;
+
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
 
 class SingleDocsEnum extends DocsEnum {
 
@@ -68,4 +71,34 @@
   public int freq() {
     return 1;
   }
+
+  @Override
+  public int nextPosition() throws IOException {
+    return NO_MORE_POSITIONS;
+  }
+
+  @Override
+  public int startPosition() throws IOException {
+    return NO_MORE_POSITIONS;
+  }
+
+  @Override
+  public int endPosition() throws IOException {
+    return NO_MORE_POSITIONS;
+  }
+
+  @Override
+  public int startOffset() throws IOException {
+    return -1;
+  }
+
+  @Override
+  public int endOffset() throws IOException {
+    return -1;
+  }
+
+  @Override
+  public BytesRef getPayload() throws IOException {
+    throw new UnsupportedOperationException();
+  }
 }
diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/TermAutomatonQuery.java b/lucene/sandbox/src/java/org/apache/lucene/search/TermAutomatonQuery.java
index 7120dc2..d430c21 100644
--- a/lucene/sandbox/src/java/org/apache/lucene/search/TermAutomatonQuery.java
+++ b/lucene/sandbox/src/java/org/apache/lucene/search/TermAutomatonQuery.java
@@ -17,14 +17,7 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.lucene.index.DocsAndPositionsEnum;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexReaderContext;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.ReaderUtil;
@@ -40,6 +33,13 @@
 import org.apache.lucene.util.automaton.Operations;
 import org.apache.lucene.util.automaton.Transition;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 import static org.apache.lucene.util.automaton.Operations.DEFAULT_MAX_DETERMINIZED_STATES;
 
 // TODO
@@ -325,7 +325,7 @@
 
   static class EnumAndScorer {
     public final int termID;
-    public final DocsAndPositionsEnum posEnum;
+    public final DocsEnum posEnum;
 
     // How many positions left in the current document:
     public int posLeft;
@@ -333,7 +333,7 @@
     // Current position
     public int pos;
 
-    public EnumAndScorer(int termID, DocsAndPositionsEnum posEnum) {
+    public EnumAndScorer(int termID, DocsEnum posEnum) {
       this.termID = termID;
       this.posEnum = posEnum;
     }
@@ -385,7 +385,7 @@
     }
 
     @Override
-    public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
+    public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
 
       // Initialize the enums; null for a given slot means that term didn't appear in this reader
       EnumAndScorer[] enums = new EnumAndScorer[idToTerm.size()];
diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/TermAutomatonScorer.java b/lucene/sandbox/src/java/org/apache/lucene/search/TermAutomatonScorer.java
index 106c307..e30efd7 100644
--- a/lucene/sandbox/src/java/org/apache/lucene/search/TermAutomatonScorer.java
+++ b/lucene/sandbox/src/java/org/apache/lucene/search/TermAutomatonScorer.java
@@ -326,6 +326,36 @@
   }
 
   @Override
+  public int nextPosition() throws IOException {
+    return -1; // TODO can we get positional information out of this Scorer?
+  }
+
+  @Override
+  public int startPosition() throws IOException {
+    return 0;
+  }
+
+  @Override
+  public int endPosition() throws IOException {
+    return 0;
+  }
+
+  @Override
+  public int startOffset() throws IOException {
+    return 0;
+  }
+
+  @Override
+  public int endOffset() throws IOException {
+    return 0;
+  }
+
+  @Override
+  public BytesRef getPayload() throws IOException {
+    return null;
+  }
+
+  @Override
   public int docID() {
     return docID;
   }
diff --git a/lucene/suggest/src/java/org/apache/lucene/search/suggest/analyzing/BlendedInfixSuggester.java b/lucene/suggest/src/java/org/apache/lucene/search/suggest/analyzing/BlendedInfixSuggester.java
index 0ccaed3..2ed2d1b 100644
--- a/lucene/suggest/src/java/org/apache/lucene/search/suggest/analyzing/BlendedInfixSuggester.java
+++ b/lucene/suggest/src/java/org/apache/lucene/search/suggest/analyzing/BlendedInfixSuggester.java
@@ -29,7 +29,7 @@
 import org.apache.lucene.document.FieldType;
 import org.apache.lucene.document.TextField;
 import org.apache.lucene.index.BinaryDocValues;
-import org.apache.lucene.index.DocsAndPositionsEnum;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexOptions;
 import org.apache.lucene.index.MultiDocValues;
 import org.apache.lucene.index.Terms;
@@ -243,7 +243,7 @@
 
       if (matchedTokens.contains(docTerm) || docTerm.startsWith(prefixToken)) {
 
-        DocsAndPositionsEnum docPosEnum = it.docsAndPositions(null, null, DocsAndPositionsEnum.FLAG_OFFSETS);
+        DocsEnum docPosEnum = it.docsAndPositions(null, null, DocsEnum.FLAG_OFFSETS);
         docPosEnum.nextDoc();
 
         // use the first occurrence of the term
diff --git a/lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/AssertingPostingsFormat.java b/lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/AssertingPostingsFormat.java
index ae02253..77763e6 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/AssertingPostingsFormat.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/AssertingPostingsFormat.java
@@ -17,14 +17,10 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.util.Iterator;
-
 import org.apache.lucene.codecs.FieldsConsumer;
 import org.apache.lucene.codecs.FieldsProducer;
 import org.apache.lucene.codecs.PostingsFormat;
 import org.apache.lucene.index.AssertingLeafReader;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.Fields;
@@ -38,6 +34,9 @@
 import org.apache.lucene.util.BytesRefBuilder;
 import org.apache.lucene.util.TestUtil;
 
+import java.io.IOException;
+import java.util.Iterator;
+
 /**
  * Just like the default postings format but with additional asserts.
  */
@@ -159,7 +158,6 @@
         termsEnum = terms.iterator(termsEnum);
         BytesRefBuilder lastTerm = null;
         DocsEnum docsEnum = null;
-        DocsAndPositionsEnum posEnum = null;
 
         boolean hasFreqs = fieldInfo.getIndexOptions().compareTo(IndexOptions.DOCS_AND_FREQS) >= 0;
         boolean hasPositions = fieldInfo.getIndexOptions().compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) >= 0;
@@ -190,13 +188,12 @@
             docsEnum = termsEnum.docs(null, docsEnum, flags);
           } else {
             if (hasPayloads) {
-              flags |= DocsAndPositionsEnum.FLAG_PAYLOADS;
+              flags |= DocsEnum.FLAG_PAYLOADS;
             }
             if (hasOffsets) {
-              flags = flags | DocsAndPositionsEnum.FLAG_OFFSETS;
+              flags = flags | DocsEnum.FLAG_OFFSETS;
             }
-            posEnum = termsEnum.docsAndPositions(null, posEnum, flags);
-            docsEnum = posEnum;
+            docsEnum = termsEnum.docsAndPositions(null, docsEnum, flags);
           }
 
           assert docsEnum != null : "termsEnum=" + termsEnum + " hasPositions=" + hasPositions;
@@ -218,13 +215,13 @@
                 int lastPos = -1;
                 int lastStartOffset = -1;
                 for(int i=0;i<freq;i++) {
-                  int pos = posEnum.nextPosition();
+                  int pos = docsEnum.nextPosition();
                   assert pos >= lastPos: "pos=" + pos + " vs lastPos=" + lastPos + " i=" + i + " freq=" + freq;
                   lastPos = pos;
 
                   if (hasOffsets) {
-                    int startOffset = posEnum.startOffset();
-                    int endOffset = posEnum.endOffset();
+                    int startOffset = docsEnum.startOffset();
+                    int endOffset = docsEnum.endOffset();
                     assert endOffset >= startOffset;
                     assert startOffset >= lastStartOffset;
                     lastStartOffset = startOffset;
diff --git a/lucene/test-framework/src/java/org/apache/lucene/codecs/ramonly/RAMOnlyPostingsFormat.java b/lucene/test-framework/src/java/org/apache/lucene/codecs/ramonly/RAMOnlyPostingsFormat.java
index e5a2a38..8a76fda 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/codecs/ramonly/RAMOnlyPostingsFormat.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/codecs/ramonly/RAMOnlyPostingsFormat.java
@@ -33,7 +33,6 @@
 import org.apache.lucene.codecs.FieldsProducer;
 import org.apache.lucene.codecs.PostingsFormat;
 import org.apache.lucene.codecs.TermStats;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.Fields;
@@ -252,7 +251,6 @@
         long sumTotalTermFreq = 0;
         long sumDocFreq = 0;
         DocsEnum docsEnum = null;
-        DocsAndPositionsEnum posEnum = null;
         int enumFlags;
 
         IndexOptions indexOptions = fieldInfo.getIndexOptions();
@@ -267,15 +265,15 @@
           enumFlags = DocsEnum.FLAG_FREQS;
         } else if (writeOffsets == false) {
           if (writePayloads) {
-            enumFlags = DocsAndPositionsEnum.FLAG_PAYLOADS;
+            enumFlags = DocsEnum.FLAG_PAYLOADS;
           } else {
             enumFlags = 0;
           }
         } else {
           if (writePayloads) {
-            enumFlags = DocsAndPositionsEnum.FLAG_PAYLOADS | DocsAndPositionsEnum.FLAG_OFFSETS;
+            enumFlags = DocsEnum.FLAG_PAYLOADS | DocsEnum.FLAG_OFFSETS;
           } else {
-            enumFlags = DocsAndPositionsEnum.FLAG_OFFSETS;
+            enumFlags = DocsEnum.FLAG_OFFSETS;
           }
         }
 
@@ -285,14 +283,7 @@
             break;
           }
           RAMPostingsWriterImpl postingsWriter = termsConsumer.startTerm(term);
-
-          if (writePositions) {
-            posEnum = termsEnum.docsAndPositions(null, posEnum, enumFlags);
-            docsEnum = posEnum;
-          } else {
-            docsEnum = termsEnum.docs(null, docsEnum, enumFlags);
-            posEnum = null;
-          }
+          docsEnum = termsEnum.docs(null, docsEnum, enumFlags);
 
           int docFreq = 0;
           long totalTermFreq = 0;
@@ -315,13 +306,13 @@
             postingsWriter.startDoc(docID, freq);
             if (writePositions) {
               for (int i=0;i<freq;i++) {
-                int pos = posEnum.nextPosition();
-                BytesRef payload = writePayloads ? posEnum.getPayload() : null;
+                int pos = docsEnum.nextPosition();
+                BytesRef payload = writePayloads ? docsEnum.getPayload() : null;
                 int startOffset;
                 int endOffset;
                 if (writeOffsets) {
-                  startOffset = posEnum.startOffset();
-                  endOffset = posEnum.endOffset();
+                  startOffset = docsEnum.startOffset();
+                  endOffset = docsEnum.endOffset();
                 } else {
                   startOffset = -1;
                   endOffset = -1;
@@ -483,8 +474,8 @@
     }
 
     @Override
-    public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) {
-      return new RAMDocsAndPositionsEnum(ramField.termToDocs.get(current), liveDocs);
+    public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) {
+      return new RAMDocsEnum(ramField.termToDocs.get(current), liveDocs);
     }
   }
 
@@ -531,63 +522,25 @@
     public int docID() {
       return current.docID;
     }
-    
-    @Override
-    public long cost() {
-      return ramTerm.docs.size();
-    } 
-  }
-
-  private static class RAMDocsAndPositionsEnum extends DocsAndPositionsEnum {
-    private final RAMTerm ramTerm;
-    private final Bits liveDocs;
-    private RAMDoc current;
-    int upto = -1;
-    int posUpto = 0;
-
-    public RAMDocsAndPositionsEnum(RAMTerm ramTerm, Bits liveDocs) {
-      this.ramTerm = ramTerm;
-      this.liveDocs = liveDocs;
-    }
-
-    @Override
-    public int advance(int targetDocID) throws IOException {
-      return slowAdvance(targetDocID);
-    }
-
-    // TODO: override bulk read, for better perf
-    @Override
-    public int nextDoc() {
-      while(true) {
-        upto++;
-        if (upto < ramTerm.docs.size()) {
-          current = ramTerm.docs.get(upto);
-          if (liveDocs == null || liveDocs.get(current.docID)) {
-            posUpto = 0;
-            return current.docID;
-          }
-        } else {
-          return NO_MORE_DOCS;
-        }
-      }
-    }
-
-    @Override
-    public int freq() throws IOException {
-      return current.positions.length;
-    }
-
-    @Override
-    public int docID() {
-      return current.docID;
-    }
 
     @Override
     public int nextPosition() {
+      if (posUpto >= current.positions.length)
+        return NO_MORE_POSITIONS;
       return current.positions[posUpto++];
     }
 
     @Override
+    public int startPosition() throws IOException {
+      return current.positions[posUpto-1];
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return current.positions[posUpto-1];
+    }
+
+    @Override
     public int startOffset() {
       return -1;
     }
diff --git a/lucene/test-framework/src/java/org/apache/lucene/index/AssertingLeafReader.java b/lucene/test-framework/src/java/org/apache/lucene/index/AssertingLeafReader.java
index 81e9548..cdb5ec7 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/index/AssertingLeafReader.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/index/AssertingLeafReader.java
@@ -143,16 +143,16 @@
     }
 
     @Override
-    public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
+    public DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
       assert state == State.POSITIONED: "docsAndPositions(...) called on unpositioned TermsEnum";
 
       // TODO: should we give this thing a random to be super-evil,
       // and randomly *not* unwrap?
-      if (reuse instanceof AssertingDocsAndPositionsEnum) {
-        reuse = ((AssertingDocsAndPositionsEnum) reuse).in;
+      if (reuse instanceof AssertingDocsEnum) {
+        reuse = ((AssertingDocsEnum) reuse).in;
       }
-      DocsAndPositionsEnum docs = super.docsAndPositions(liveDocs, reuse, flags);
-      return docs == null ? null : new AssertingDocsAndPositionsEnum(docs);
+      DocsEnum docs = super.docsAndPositions(liveDocs, reuse, flags);
+      return docs == null ? null : new AssertingDocsEnum(docs);
     }
 
     // TODO: we should separately track if we are 'at the end' ?
@@ -255,8 +255,10 @@
   /** Wraps a docsenum with additional checks */
   public static class AssertingDocsEnum extends FilterDocsEnum {
     private DocsEnumState state = DocsEnumState.START;
+    int positionCount = 0;
+    int positionMax = 0;
     private int doc;
-    
+
     public AssertingDocsEnum(DocsEnum in) {
       this(in, true);
     }
@@ -281,9 +283,12 @@
       assert nextDoc > doc : "backwards nextDoc from " + doc + " to " + nextDoc + " " + in;
       if (nextDoc == DocIdSetIterator.NO_MORE_DOCS) {
         state = DocsEnumState.FINISHED;
+        positionMax = 0;
       } else {
         state = DocsEnumState.ITERATING;
+        positionMax = super.freq();
       }
+      positionCount = 0;
       assert super.docID() == nextDoc;
       return doc = nextDoc;
     }
@@ -296,9 +301,12 @@
       assert advanced >= target : "backwards advance from: " + target + " to: " + advanced;
       if (advanced == DocIdSetIterator.NO_MORE_DOCS) {
         state = DocsEnumState.FINISHED;
+        positionMax = 0;
       } else {
         state = DocsEnumState.ITERATING;
+        positionMax = super.freq();
       }
+      positionCount = 0;
       assert super.docID() == advanced;
       return doc = advanced;
     }
@@ -317,15 +325,72 @@
       assert freq > 0;
       return freq;
     }
+
+    @Override
+    public int nextPosition() throws IOException {
+      assert state != DocsEnumState.START : "nextPosition() called before nextDoc()/advance()";
+      assert state != DocsEnumState.FINISHED : "nextPosition() called after NO_MORE_DOCS";
+      int position = super.nextPosition();
+      assert position >= 0 || position == -1 : "invalid position: " + position;
+      if (positionCount++ >= positionMax)
+        assert position == NO_MORE_POSITIONS : "nextPosition() does not return NO_MORE_POSITIONS when exhausted";
+      return position;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      assert state != DocsEnumState.START : "startOffset() called before nextDoc()/advance()";
+      assert state != DocsEnumState.FINISHED : "startOffset() called after NO_MORE_DOCS";
+      assert positionCount > 0 : "startOffset() called before nextPosition()!";
+      assert positionCount <= positionMax : "startOffset() called after NO_MORE_POSITIONS";
+      return super.startOffset();
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      assert state != DocsEnumState.START : "endOffset() called before nextDoc()/advance()";
+      assert state != DocsEnumState.FINISHED : "endOffset() called after NO_MORE_DOCS";
+      assert positionCount > 0 : "endOffset() called before nextPosition()!";
+      assert positionCount <= positionMax : "endOffset() called after NO_MORE_POSITIONS";
+      return super.endOffset();
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      assert state != DocsEnumState.START : "startPosition() called before nextDoc()/advance()";
+      assert state != DocsEnumState.FINISHED : "startPosition() called after NO_MORE_DOCS";
+      assert positionCount > 0 : "startPosition() called before nextPosition()!";
+      assert positionCount <= positionMax : "startPosition() called after NO_MORE_POSITIONS";
+      return super.startPosition();
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      assert state != DocsEnumState.START : "endPosition() called before nextDoc()/advance()";
+      assert state != DocsEnumState.FINISHED : "endPosition() called after NO_MORE_DOCS";
+      assert positionCount > 0 : "endPosition() called before nextPosition()!";
+      assert positionCount <= positionMax : "endPosition() called after NO_MORE_POSITIONS";
+      return super.endPosition();
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      assert state != DocsEnumState.START : "getPayload() called before nextDoc()/advance()";
+      assert state != DocsEnumState.FINISHED : "getPayload() called after NO_MORE_DOCS";
+      assert positionCount > 0 : "getPayload() called before nextPosition()!";
+      BytesRef payload = super.getPayload();
+      assert payload == null || payload.length > 0 : "getPayload() returned payload with invalid length!";
+      return payload;
+    }
   }
   
-  static class AssertingDocsAndPositionsEnum extends FilterDocsAndPositionsEnum {
+  static class AssertingDocsAndPositionsEnum extends FilterDocsEnum {
     private DocsEnumState state = DocsEnumState.START;
     private int positionMax = 0;
     private int positionCount = 0;
     private int doc;
 
-    public AssertingDocsAndPositionsEnum(DocsAndPositionsEnum in) {
+    public AssertingDocsAndPositionsEnum(DocsEnum in) {
       super(in);
       int docid = in.docID();
       assert docid == -1 : "invalid initial doc id: " + docid;
diff --git a/lucene/test-framework/src/java/org/apache/lucene/index/BaseDocValuesFormatTestCase.java b/lucene/test-framework/src/java/org/apache/lucene/index/BaseDocValuesFormatTestCase.java
index 16845f9..dcf127a 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/index/BaseDocValuesFormatTestCase.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/index/BaseDocValuesFormatTestCase.java
@@ -17,8 +17,6 @@
  * limitations under the License.
  */
 
-import static org.apache.lucene.index.SortedSetDocValues.NO_MORE_ORDS;
-
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.PrintStream;
@@ -61,6 +59,8 @@
 import org.apache.lucene.util.IOUtils;
 import org.apache.lucene.util.TestUtil;
 
+import static org.apache.lucene.index.SortedSetDocValues.NO_MORE_ORDS;
+
 /**
  * Abstract class to do basic tests for a docvalues format.
  * NOTE: This test focuses on the docvalues impl, nothing else.
@@ -2083,7 +2083,7 @@
       );
     }
   }
-  
+
   public void testSortedNumericsMultipleValuesVsStoredFields() throws Exception {
     assumeTrue("Codec does not support SORTED_NUMERIC", codecSupportsSortedNumeric());
     int numIterations = atLeast(1);
diff --git a/lucene/test-framework/src/java/org/apache/lucene/index/BasePostingsFormatTestCase.java b/lucene/test-framework/src/java/org/apache/lucene/index/BasePostingsFormatTestCase.java
index 6024c7e..c6c8265 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/index/BasePostingsFormatTestCase.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/index/BasePostingsFormatTestCase.java
@@ -121,7 +121,7 @@
 
   /** Given the same random seed this always enumerates the
    *  same random postings */
-  private static class SeedPostings extends DocsAndPositionsEnum {
+  private static class SeedPostings extends DocsEnum {
     // Used only to generate docIDs; this way if you pull w/
     // or w/o positions you get the same docID sequence:
     private final Random docRandom;
@@ -233,7 +233,9 @@
         posUpto = freq;
         return 0;
       }
-      assert posUpto < freq;
+      //assert posUpto < freq;
+      if (posUpto >= freq)
+        return NO_MORE_POSITIONS;
 
       if (posUpto == 0 && random.nextBoolean()) {
         // Sometimes index pos = 0
@@ -270,7 +272,17 @@
       posUpto++;
       return pos;
     }
-  
+
+    @Override
+    public int startPosition() throws IOException {
+      return pos;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return pos;
+    }
+
     @Override
     public int startOffset() {
       return startOffset;
@@ -650,17 +662,17 @@
     }
 
     @Override
-    public final DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
+    public final DocsEnum docsAndPositions(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
       if (liveDocs != null) {
         throw new IllegalArgumentException("liveDocs must be null");
       }
       if (maxAllowed.compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) < 0) {
         return null;
       }
-      if ((flags & DocsAndPositionsEnum.FLAG_OFFSETS) != 0 && maxAllowed.compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS) < 0) {
+      if ((flags & DocsEnum.FLAG_OFFSETS) == DocsEnum.FLAG_OFFSETS && maxAllowed.compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS) < 0) {
         return null;
       }
-      if ((flags & DocsAndPositionsEnum.FLAG_PAYLOADS) != 0 && allowPayloads == false) {
+      if ((flags & DocsEnum.FLAG_PAYLOADS) == DocsEnum.FLAG_PAYLOADS && allowPayloads == false) {
         return null;
       }
       return getSeedPostings(current.getKey().utf8ToString(), current.getValue().seed, false, maxAllowed, allowPayloads);
@@ -746,7 +758,6 @@
   private static class ThreadState {
     // Only used with REUSE option:
     public DocsEnum reuseDocsEnum;
-    public DocsAndPositionsEnum reuseDocsAndPositionsEnum;
   }
 
   private void verifyEnum(ThreadState threadState,
@@ -812,31 +823,29 @@
     DocsEnum prevDocsEnum = null;
 
     DocsEnum docsEnum;
-    DocsAndPositionsEnum docsAndPositionsEnum;
 
     if (!doCheckPositions) {
       if (allowPositions && random().nextInt(10) == 7) {
         // 10% of the time, even though we will not check positions, pull a DocsAndPositions enum
         
         if (options.contains(Option.REUSE_ENUMS) && random().nextInt(10) < 9) {
-          prevDocsEnum = threadState.reuseDocsAndPositionsEnum;
+          prevDocsEnum = threadState.reuseDocsEnum;
         }
 
-        int flags = 0;
+        int flags = DocsEnum.FLAG_POSITIONS;
         if (alwaysTestMax || random().nextBoolean()) {
-          flags |= DocsAndPositionsEnum.FLAG_OFFSETS;
+          flags |= DocsEnum.FLAG_OFFSETS;
         }
         if (alwaysTestMax || random().nextBoolean()) {
-          flags |= DocsAndPositionsEnum.FLAG_PAYLOADS;
+          flags |= DocsEnum.FLAG_PAYLOADS;
         }
 
         if (VERBOSE) {
-          System.out.println("  get DocsAndPositionsEnum (but we won't check positions) flags=" + flags);
+          System.out.println("  get DocsEnum (but we won't check positions) flags=" + flags);
         }
 
-        threadState.reuseDocsAndPositionsEnum = termsEnum.docsAndPositions(liveDocs, (DocsAndPositionsEnum) prevDocsEnum, flags);
-        docsEnum = threadState.reuseDocsAndPositionsEnum;
-        docsAndPositionsEnum = threadState.reuseDocsAndPositionsEnum;
+        threadState.reuseDocsEnum = termsEnum.docsAndPositions(liveDocs, prevDocsEnum, flags);
+        docsEnum = threadState.reuseDocsEnum;
       } else {
         if (VERBOSE) {
           System.out.println("  get DocsEnum");
@@ -846,28 +855,26 @@
         }
         threadState.reuseDocsEnum = termsEnum.docs(liveDocs, prevDocsEnum, doCheckFreqs ? DocsEnum.FLAG_FREQS : DocsEnum.FLAG_NONE);
         docsEnum = threadState.reuseDocsEnum;
-        docsAndPositionsEnum = null;
       }
     } else {
       if (options.contains(Option.REUSE_ENUMS) && random().nextInt(10) < 9) {
-        prevDocsEnum = threadState.reuseDocsAndPositionsEnum;
+        prevDocsEnum = threadState.reuseDocsEnum;
       }
 
-      int flags = 0;
+      int flags = DocsEnum.FLAG_POSITIONS;
       if (alwaysTestMax || doCheckOffsets || random().nextInt(3) == 1) {
-        flags |= DocsAndPositionsEnum.FLAG_OFFSETS;
+        flags |= DocsEnum.FLAG_OFFSETS;
       }
       if (alwaysTestMax || doCheckPayloads|| random().nextInt(3) == 1) {
-        flags |= DocsAndPositionsEnum.FLAG_PAYLOADS;
+        flags |= DocsEnum.FLAG_PAYLOADS;
       }
 
       if (VERBOSE) {
-        System.out.println("  get DocsAndPositionsEnum flags=" + flags);
+        System.out.println("  get DocsEnum flags=" + flags);
       }
 
-      threadState.reuseDocsAndPositionsEnum = termsEnum.docsAndPositions(liveDocs, (DocsAndPositionsEnum) prevDocsEnum, flags);
-      docsEnum = threadState.reuseDocsAndPositionsEnum;
-      docsAndPositionsEnum = threadState.reuseDocsAndPositionsEnum;
+      threadState.reuseDocsEnum = termsEnum.docsAndPositions(liveDocs, prevDocsEnum, flags);
+      docsEnum = threadState.reuseDocsEnum;
     }
 
     assertNotNull("null DocsEnum", docsEnum);
@@ -1009,7 +1016,7 @@
           if (VERBOSE) {
             System.out.println("    now nextPosition to " + pos);
           }
-          assertEquals("position is wrong", pos, docsAndPositionsEnum.nextPosition());
+          assertEquals("position is wrong", pos, docsEnum.nextPosition());
 
           if (doCheckPayloads) {
             BytesRef expectedPayload = expected.getPayload();
@@ -1018,9 +1025,9 @@
                 System.out.println("      now check expectedPayload length=" + (expectedPayload == null ? 0 : expectedPayload.length));
               }
               if (expectedPayload == null || expectedPayload.length == 0) {
-                assertNull("should not have payload", docsAndPositionsEnum.getPayload());
+                assertNull("should not have payload", docsEnum.getPayload());
               } else {
-                BytesRef payload = docsAndPositionsEnum.getPayload();
+                BytesRef payload = docsEnum.getPayload();
                 assertNotNull("should have payload but doesn't", payload);
 
                 assertEquals("payload length is wrong", expectedPayload.length, payload.length);
@@ -1032,7 +1039,7 @@
                 
                 // make a deep copy
                 payload = BytesRef.deepCopyOf(payload);
-                assertEquals("2nd call to getPayload returns something different!", payload, docsAndPositionsEnum.getPayload());
+                assertEquals("2nd call to getPayload returns something different!", payload, docsEnum.getPayload());
               }
             } else {
               if (VERBOSE) {
@@ -1046,8 +1053,8 @@
               if (VERBOSE) {
                 System.out.println("      now check offsets: startOff=" + expected.startOffset() + " endOffset=" + expected.endOffset());
               }
-              assertEquals("startOffset is wrong", expected.startOffset(), docsAndPositionsEnum.startOffset());
-              assertEquals("endOffset is wrong", expected.endOffset(), docsAndPositionsEnum.endOffset());
+              assertEquals("startOffset is wrong", expected.startOffset(), docsEnum.startOffset());
+              assertEquals("endOffset is wrong", expected.endOffset(), docsEnum.endOffset());
             } else {
               if (VERBOSE) {
                 System.out.println("      skip check offsets");
@@ -1057,8 +1064,8 @@
             if (VERBOSE) {
               System.out.println("      now check offsets are -1");
             }
-            assertEquals("startOffset isn't -1", -1, docsAndPositionsEnum.startOffset());
-            assertEquals("endOffset isn't -1", -1, docsAndPositionsEnum.endOffset());
+            assertEquals("startOffset isn't -1", -1, docsEnum.startOffset());
+            assertEquals("endOffset isn't -1", -1, docsEnum.endOffset());
           }
         }
       }
@@ -1575,22 +1582,17 @@
 
                       if (random().nextBoolean()) {
                         docs = termsEnum.docs(null, docs, DocsEnum.FLAG_FREQS);
-                      } else if (docs instanceof DocsAndPositionsEnum) {
-                        docs = termsEnum.docsAndPositions(null, (DocsAndPositionsEnum) docs, 0);
                       } else {
-                        docs = termsEnum.docsAndPositions(null, null, 0);
+                        docs = termsEnum.docsAndPositions(null, null, DocsEnum.FLAG_POSITIONS);
                       }
                       int docFreq = 0;
                       long totalTermFreq = 0;
                       while (docs.nextDoc() != DocsEnum.NO_MORE_DOCS) {
                         docFreq++;
                         totalTermFreq += docs.freq();
-                        if (docs instanceof DocsAndPositionsEnum) {
-                          DocsAndPositionsEnum posEnum = (DocsAndPositionsEnum) docs;
-                          int limit = TestUtil.nextInt(random(), 1, docs.freq());
-                          for(int i=0;i<limit;i++) {
-                            posEnum.nextPosition();
-                          }
+                        int limit = TestUtil.nextInt(random(), 1, docs.freq());
+                        for(int i=0;i<limit;i++) {
+                          docs.nextPosition();
                         }
                       }
 
@@ -1625,8 +1627,6 @@
                       if (termsEnum.seekExact(new BytesRef(term))) {
                         if (random().nextBoolean()) {
                           docs = termsEnum.docs(null, docs, DocsEnum.FLAG_FREQS);
-                        } else if (docs instanceof DocsAndPositionsEnum) {
-                          docs = termsEnum.docsAndPositions(null, (DocsAndPositionsEnum) docs, 0);
                         } else {
                           docs = termsEnum.docsAndPositions(null, null, 0);
                         }
@@ -1636,12 +1636,9 @@
                         while (docs.nextDoc() != DocsEnum.NO_MORE_DOCS) {
                           docFreq++;
                           totalTermFreq += docs.freq();
-                          if (docs instanceof DocsAndPositionsEnum) {
-                            DocsAndPositionsEnum posEnum = (DocsAndPositionsEnum) docs;
-                            int limit = TestUtil.nextInt(random(), 1, docs.freq());
-                            for(int i=0;i<limit;i++) {
-                              posEnum.nextPosition();
-                            }
+                          int limit = TestUtil.nextInt(random(), 1, docs.freq());
+                          for(int i=0;i<limit;i++) {
+                            docs.nextPosition();
                           }
                         }
 
diff --git a/lucene/test-framework/src/java/org/apache/lucene/index/BaseTermVectorsFormatTestCase.java b/lucene/test-framework/src/java/org/apache/lucene/index/BaseTermVectorsFormatTestCase.java
index 16655ea..72bf4df 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/index/BaseTermVectorsFormatTestCase.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/index/BaseTermVectorsFormatTestCase.java
@@ -17,16 +17,7 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicReference;
-
+import com.carrotsearch.randomizedtesting.generators.RandomPicks;
 import org.apache.lucene.analysis.TokenStream;
 import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
 import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
@@ -49,7 +40,15 @@
 import org.apache.lucene.util.FixedBitSet;
 import org.apache.lucene.util.TestUtil;
 
-import com.carrotsearch.randomizedtesting.generators.RandomPicks;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Base class aiming at testing {@link TermVectorsFormat term vectors formats}.
@@ -415,7 +414,7 @@
   // to test reuse
   private final ThreadLocal<TermsEnum> termsEnum = new ThreadLocal<>();
   private final ThreadLocal<DocsEnum> docsEnum = new ThreadLocal<>();
-  private final ThreadLocal<DocsAndPositionsEnum> docsAndPositionsEnum = new ThreadLocal<>();
+  private final ThreadLocal<DocsEnum> docsAndPositionsEnum = new ThreadLocal<>();
 
   protected void assertEquals(RandomTokenStream tk, FieldType ft, Terms terms) throws IOException {
     assertEquals(1, terms.getDocCount());
@@ -453,7 +452,7 @@
       this.docsEnum.set(docsEnum);
 
       bits.clear(0);
-      DocsAndPositionsEnum docsAndPositionsEnum = termsEnum.docsAndPositions(bits, random().nextBoolean() ? null : this.docsAndPositionsEnum.get());
+      DocsEnum docsAndPositionsEnum = termsEnum.docsAndPositions(bits, random().nextBoolean() ? null : this.docsEnum.get());
       assertEquals(ft.storeTermVectorOffsets() || ft.storeTermVectorPositions(), docsAndPositionsEnum != null);
       if (docsAndPositionsEnum != null) {
         assertEquals(DocsEnum.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
@@ -519,7 +518,7 @@
         }
         assertEquals(DocsEnum.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
       }
-      this.docsAndPositionsEnum.set(docsAndPositionsEnum);
+      this.docsEnum.set(docsAndPositionsEnum);
     }
     assertNull(termsEnum.next());
     for (int i = 0; i < 5; ++i) {
diff --git a/lucene/test-framework/src/java/org/apache/lucene/search/AssertingIndexSearcher.java b/lucene/test-framework/src/java/org/apache/lucene/search/AssertingIndexSearcher.java
index e2eefa0..050c279 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/search/AssertingIndexSearcher.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/search/AssertingIndexSearcher.java
@@ -17,16 +17,17 @@
  * limitations under the License.
  */
 
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexReaderContext;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.TestUtil;
+
 import java.io.IOException;
 import java.util.List;
 import java.util.Random;
 import java.util.concurrent.ExecutorService;
 
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.IndexReaderContext;
-import org.apache.lucene.util.TestUtil;
-
 /**
  * Helper class that adds some extra checks to ensure correct
  * usage of {@code IndexSearcher} and {@code Weight}.
@@ -65,6 +66,21 @@
       }
 
       @Override
+      public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
+        Scorer scorer = w.scorer(context, flags, acceptDocs);
+        if (scorer != null) {
+          // check that scorer obeys disi contract for docID() before next()/advance
+          try {
+            int docid = scorer.docID();
+            assert docid == -1 || docid == DocIdSetIterator.NO_MORE_DOCS;
+          } catch (UnsupportedOperationException ignored) {
+            // from a top-level BS1
+          }
+        }
+        return scorer;
+      }
+
+      @Override
       public float getValueForNormalization() {
         throw new IllegalStateException("Weight already normalized.");
       }
diff --git a/lucene/test-framework/src/java/org/apache/lucene/search/AssertingScorer.java b/lucene/test-framework/src/java/org/apache/lucene/search/AssertingScorer.java
index 1abda77..bd760ae 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/search/AssertingScorer.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/search/AssertingScorer.java
@@ -26,6 +26,7 @@
 import java.util.WeakHashMap;
 
 import org.apache.lucene.index.AssertingLeafReader;
+import org.apache.lucene.util.BytesRef;
 
 /** Wraps a Scorer with additional checks */
 public class AssertingScorer extends Scorer {
@@ -111,18 +112,58 @@
   }
 
   @Override
+  public int nextPosition() throws IOException {
+    assert iterating();
+    return in.nextPosition();
+  }
+
+  @Override
+  public int startPosition() throws IOException {
+    assert iterating();
+    return in.startPosition();
+  }
+
+  @Override
+  public int endPosition() throws IOException {
+    assert iterating();
+    return in.endPosition();
+  }
+
+  @Override
+  public int startOffset() throws IOException {
+    assert iterating();
+    return in.startOffset();
+  }
+
+  @Override
+  public int endOffset() throws IOException {
+    assert iterating();
+    return in.endOffset();
+  }
+
+  @Override
+  public BytesRef getPayload() throws IOException {
+    assert iterating();
+    return in.getPayload();
+  }
+
+  @Override
   public int docID() {
     return in.docID();
   }
 
   @Override
   public int nextDoc() throws IOException {
-    return docsEnumIn.nextDoc();
+    int doc = docsEnumIn.nextDoc();
+    assert in.startPosition() == -1;
+    return doc;
   }
 
   @Override
   public int advance(int target) throws IOException {
-    return docsEnumIn.advance(target);
+    int doc = docsEnumIn.advance(target);
+    assert in.startPosition() == -1;
+    return doc;
   }
 
   @Override
diff --git a/lucene/test-framework/src/java/org/apache/lucene/search/AssertingWeight.java b/lucene/test-framework/src/java/org/apache/lucene/search/AssertingWeight.java
index eb08c2d..1aed3f9 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/search/AssertingWeight.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/search/AssertingWeight.java
@@ -17,12 +17,12 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.util.Random;
-
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.util.Bits;
 
+import java.io.IOException;
+import java.util.Random;
+
 class AssertingWeight extends Weight {
 
   static Weight wrap(Random random, Weight other) {
@@ -60,18 +60,18 @@
   }
 
   @Override
-  public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
+  public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
     // if the caller asks for in-order scoring or if the weight does not support
     // out-of order scoring then collection will have to happen in-order.
-    final Scorer inScorer = in.scorer(context, acceptDocs);
+    final Scorer inScorer = in.scorer(context, flags, acceptDocs);
     return AssertingScorer.wrap(new Random(random.nextLong()), inScorer);
   }
 
   @Override
-  public BulkScorer bulkScorer(LeafReaderContext context, boolean scoreDocsInOrder, Bits acceptDocs) throws IOException {
+  public BulkScorer bulkScorer(LeafReaderContext context, boolean scoreDocsInOrder, int flags, Bits acceptDocs) throws IOException {
     // if the caller asks for in-order scoring or if the weight does not support
     // out-of order scoring then collection will have to happen in-order.
-    BulkScorer inScorer = in.bulkScorer(context, scoreDocsInOrder, acceptDocs);
+    BulkScorer inScorer = in.bulkScorer(context, scoreDocsInOrder, flags, acceptDocs);
     if (inScorer == null) {
       return null;
     }
@@ -83,7 +83,7 @@
     } else if (random.nextBoolean()) {
       // Let super wrap this.scorer instead, so we use
       // AssertingScorer:
-      inScorer = super.bulkScorer(context, scoreDocsInOrder, acceptDocs);
+      inScorer = super.bulkScorer(context, scoreDocsInOrder, flags, acceptDocs);
     }
 
     if (scoreDocsInOrder == false && random.nextBoolean()) {
diff --git a/lucene/test-framework/src/java/org/apache/lucene/search/QueryUtils.java b/lucene/test-framework/src/java/org/apache/lucene/search/QueryUtils.java
index f8ae4ca..459b21e 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/search/QueryUtils.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/search/QueryUtils.java
@@ -17,21 +17,17 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.util.List;
-import java.util.Random;
-
 import junit.framework.Assert;
-
 import org.apache.lucene.analysis.MockAnalyzer;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.index.AllDeletedFilterReader;
-import org.apache.lucene.index.LeafReader;
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.index.LeafReader;
+import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.MultiReader;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.MockDirectoryWrapper;
@@ -39,6 +35,10 @@
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.LuceneTestCase;
 
+import java.io.IOException;
+import java.util.List;
+import java.util.Random;
+
 /**
  * Utility class for sanity-checking queries.
  */
@@ -264,7 +264,7 @@
               if (scorer == null) {
                 Weight w = s.createNormalizedWeight(q);
                 LeafReaderContext context = readerContextArray.get(leafPtr);
-                scorer = w.scorer(context, context.reader().getLiveDocs());
+                scorer = w.scorer(context, DocsEnum.FLAG_FREQS, context.reader().getLiveDocs());
               }
               
               int op = order[(opidx[0]++) % order.length];
@@ -311,7 +311,7 @@
               indexSearcher.setSimilarity(s.getSimilarity());
               Weight w = indexSearcher.createNormalizedWeight(q);
               LeafReaderContext ctx = (LeafReaderContext)indexSearcher.getTopReaderContext();
-              Scorer scorer = w.scorer(ctx, ctx.reader().getLiveDocs());
+              Scorer scorer = w.scorer(ctx, DocsEnum.FLAG_FREQS, ctx.reader().getLiveDocs());
               if (scorer != null) {
                 boolean more = scorer.advance(lastDoc[0] + 1) != DocIdSetIterator.NO_MORE_DOCS;
                 Assert.assertFalse("query's last doc was "+ lastDoc[0] +" but skipTo("+(lastDoc[0]+1)+") got to "+scorer.docID(),more);
@@ -338,7 +338,7 @@
           indexSearcher.setSimilarity(s.getSimilarity());
           Weight w = indexSearcher.createNormalizedWeight(q);
           LeafReaderContext ctx = previousReader.getContext();
-          Scorer scorer = w.scorer(ctx, ctx.reader().getLiveDocs());
+          Scorer scorer = w.scorer(ctx, DocsEnum.FLAG_FREQS, ctx.reader().getLiveDocs());
           if (scorer != null) {
             boolean more = scorer.advance(lastDoc[0] + 1) != DocIdSetIterator.NO_MORE_DOCS;
             Assert.assertFalse("query's last doc was "+ lastDoc[0] +" but skipTo("+(lastDoc[0]+1)+") got to "+scorer.docID(),more);
@@ -369,7 +369,7 @@
           long startMS = System.currentTimeMillis();
           for (int i=lastDoc[0]+1; i<=doc; i++) {
             Weight w = s.createNormalizedWeight(q);
-            Scorer scorer = w.scorer(context.get(leafPtr), liveDocs);
+            Scorer scorer = w.scorer(context.get(leafPtr), DocsEnum.FLAG_FREQS, liveDocs);
             Assert.assertTrue("query collected "+doc+" but skipTo("+i+") says no more docs!",scorer.advance(i) != DocIdSetIterator.NO_MORE_DOCS);
             Assert.assertEquals("query collected "+doc+" but skipTo("+i+") got to "+scorer.docID(),doc,scorer.docID());
             float skipToScore = scorer.score();
@@ -397,7 +397,7 @@
           IndexSearcher indexSearcher = LuceneTestCase.newSearcher(previousReader);
           indexSearcher.setSimilarity(s.getSimilarity());
           Weight w = indexSearcher.createNormalizedWeight(q);
-          Scorer scorer = w.scorer((LeafReaderContext)indexSearcher.getTopReaderContext(), previousReader.getLiveDocs());
+          Scorer scorer = w.scorer((LeafReaderContext)indexSearcher.getTopReaderContext(), DocsEnum.FLAG_FREQS, previousReader.getLiveDocs());
           if (scorer != null) {
             boolean more = scorer.advance(lastDoc[0] + 1) != DocIdSetIterator.NO_MORE_DOCS;
             Assert.assertFalse("query's last doc was "+ lastDoc[0] +" but skipTo("+(lastDoc[0]+1)+") got to "+scorer.docID(),more);
@@ -422,7 +422,7 @@
       IndexSearcher indexSearcher = LuceneTestCase.newSearcher(previousReader);
       indexSearcher.setSimilarity(s.getSimilarity());
       Weight w = indexSearcher.createNormalizedWeight(q);
-      Scorer scorer = w.scorer((LeafReaderContext)indexSearcher.getTopReaderContext(), previousReader.getLiveDocs());
+      Scorer scorer = w.scorer((LeafReaderContext)indexSearcher.getTopReaderContext(), DocsEnum.FLAG_FREQS, previousReader.getLiveDocs());
       if (scorer != null) {
         boolean more = scorer.advance(lastDoc[0] + 1) != DocIdSetIterator.NO_MORE_DOCS;
         Assert.assertFalse("query's last doc was "+ lastDoc[0] +" but skipTo("+(lastDoc[0]+1)+") got to "+scorer.docID(),more);
diff --git a/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java b/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java
index b877aaf..3b07922 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java
@@ -55,59 +55,42 @@
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.logging.Logger;
 
+import com.carrotsearch.randomizedtesting.JUnit4MethodProvider;
+import com.carrotsearch.randomizedtesting.LifecycleScope;
+import com.carrotsearch.randomizedtesting.MixWithSuiteName;
+import com.carrotsearch.randomizedtesting.RandomizedContext;
+import com.carrotsearch.randomizedtesting.RandomizedRunner;
+import com.carrotsearch.randomizedtesting.RandomizedTest;
+import com.carrotsearch.randomizedtesting.annotations.Listeners;
+import com.carrotsearch.randomizedtesting.annotations.SeedDecorators;
+import com.carrotsearch.randomizedtesting.annotations.TestGroup;
+import com.carrotsearch.randomizedtesting.annotations.TestMethodProviders;
+import com.carrotsearch.randomizedtesting.annotations.ThreadLeakAction;
+import com.carrotsearch.randomizedtesting.annotations.ThreadLeakAction.Action;
+import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters;
+import com.carrotsearch.randomizedtesting.annotations.ThreadLeakGroup;
+import com.carrotsearch.randomizedtesting.annotations.ThreadLeakGroup.Group;
+import com.carrotsearch.randomizedtesting.annotations.ThreadLeakLingering;
+import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope;
+import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope.Scope;
+import com.carrotsearch.randomizedtesting.annotations.ThreadLeakZombies;
+import com.carrotsearch.randomizedtesting.annotations.ThreadLeakZombies.Consequence;
+import com.carrotsearch.randomizedtesting.annotations.TimeoutSuite;
+import com.carrotsearch.randomizedtesting.generators.RandomPicks;
+import com.carrotsearch.randomizedtesting.rules.NoClassHooksShadowingRule;
+import com.carrotsearch.randomizedtesting.rules.NoInstanceHooksOverridesRule;
+import com.carrotsearch.randomizedtesting.rules.StaticFieldsInvariantRule;
+import com.carrotsearch.randomizedtesting.rules.SystemPropertiesInvariantRule;
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.MockAnalyzer;
-import org.apache.lucene.document.Field.Store;
 import org.apache.lucene.document.Field;
+import org.apache.lucene.document.Field.Store;
 import org.apache.lucene.document.FieldType;
 import org.apache.lucene.document.StringField;
 import org.apache.lucene.document.TextField;
-import org.apache.lucene.index.AlcoholicMergePolicy;
-import org.apache.lucene.index.AssertingDirectoryReader;
-import org.apache.lucene.index.AssertingLeafReader;
-import org.apache.lucene.index.BinaryDocValues;
-import org.apache.lucene.index.CompositeReader;
-import org.apache.lucene.index.ConcurrentMergeScheduler;
-import org.apache.lucene.index.DirectoryReader;
-import org.apache.lucene.index.DocValuesType;
-import org.apache.lucene.index.DocsAndPositionsEnum;
-import org.apache.lucene.index.DocsEnum;
-import org.apache.lucene.index.FieldFilterLeafReader;
-import org.apache.lucene.index.FieldInfo;
-import org.apache.lucene.index.FieldInfos;
-import org.apache.lucene.index.Fields;
-import org.apache.lucene.index.IndexOptions;
+import org.apache.lucene.index.*;
 import org.apache.lucene.index.IndexReader.ReaderClosedListener;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.IndexWriter;
-import org.apache.lucene.index.IndexWriterConfig;
-import org.apache.lucene.index.LeafReader;
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.LiveIndexWriterConfig;
-import org.apache.lucene.index.LogByteSizeMergePolicy;
-import org.apache.lucene.index.LogDocMergePolicy;
-import org.apache.lucene.index.LogMergePolicy;
-import org.apache.lucene.index.MergePolicy;
-import org.apache.lucene.index.MergeScheduler;
-import org.apache.lucene.index.MockRandomMergePolicy;
-import org.apache.lucene.index.MultiDocValues;
-import org.apache.lucene.index.MultiFields;
-import org.apache.lucene.index.NumericDocValues;
-import org.apache.lucene.index.ParallelCompositeReader;
-import org.apache.lucene.index.ParallelLeafReader;
-import org.apache.lucene.index.SegmentReader;
-import org.apache.lucene.index.SerialMergeScheduler;
-import org.apache.lucene.index.SimpleMergedSegmentWarmer;
-import org.apache.lucene.index.SlowCompositeReaderWrapper;
-import org.apache.lucene.index.SortedDocValues;
-import org.apache.lucene.index.SortedNumericDocValues;
-import org.apache.lucene.index.SortedSetDocValues;
-import org.apache.lucene.index.StorableField;
-import org.apache.lucene.index.StoredDocument;
-import org.apache.lucene.index.Terms;
 import org.apache.lucene.index.TermsEnum.SeekStatus;
-import org.apache.lucene.index.TermsEnum;
-import org.apache.lucene.index.TieredMergePolicy;
 import org.apache.lucene.search.AssertingIndexSearcher;
 import org.apache.lucene.search.DocIdSet;
 import org.apache.lucene.search.DocIdSetIterator;
@@ -120,12 +103,12 @@
 import org.apache.lucene.store.FSDirectory;
 import org.apache.lucene.store.FSLockFactory;
 import org.apache.lucene.store.FlushInfo;
-import org.apache.lucene.store.IOContext.Context;
 import org.apache.lucene.store.IOContext;
+import org.apache.lucene.store.IOContext.Context;
 import org.apache.lucene.store.LockFactory;
 import org.apache.lucene.store.MergeInfo;
-import org.apache.lucene.store.MockDirectoryWrapper.Throttling;
 import org.apache.lucene.store.MockDirectoryWrapper;
+import org.apache.lucene.store.MockDirectoryWrapper.Throttling;
 import org.apache.lucene.store.NRTCachingDirectory;
 import org.apache.lucene.store.RateLimitedDirectoryWrapper;
 import org.apache.lucene.util.automaton.AutomatonTestUtil;
@@ -143,33 +126,6 @@
 import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 
-import com.carrotsearch.randomizedtesting.JUnit4MethodProvider;
-import com.carrotsearch.randomizedtesting.LifecycleScope;
-import com.carrotsearch.randomizedtesting.MixWithSuiteName;
-import com.carrotsearch.randomizedtesting.RandomizedContext;
-import com.carrotsearch.randomizedtesting.RandomizedRunner;
-import com.carrotsearch.randomizedtesting.RandomizedTest;
-import com.carrotsearch.randomizedtesting.annotations.Listeners;
-import com.carrotsearch.randomizedtesting.annotations.SeedDecorators;
-import com.carrotsearch.randomizedtesting.annotations.TestGroup;
-import com.carrotsearch.randomizedtesting.annotations.TestMethodProviders;
-import com.carrotsearch.randomizedtesting.annotations.ThreadLeakAction.Action;
-import com.carrotsearch.randomizedtesting.annotations.ThreadLeakAction;
-import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters;
-import com.carrotsearch.randomizedtesting.annotations.ThreadLeakGroup.Group;
-import com.carrotsearch.randomizedtesting.annotations.ThreadLeakGroup;
-import com.carrotsearch.randomizedtesting.annotations.ThreadLeakLingering;
-import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope.Scope;
-import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope;
-import com.carrotsearch.randomizedtesting.annotations.ThreadLeakZombies.Consequence;
-import com.carrotsearch.randomizedtesting.annotations.ThreadLeakZombies;
-import com.carrotsearch.randomizedtesting.annotations.TimeoutSuite;
-import com.carrotsearch.randomizedtesting.generators.RandomPicks;
-import com.carrotsearch.randomizedtesting.rules.NoClassHooksShadowingRule;
-import com.carrotsearch.randomizedtesting.rules.NoInstanceHooksOverridesRule;
-import com.carrotsearch.randomizedtesting.rules.StaticFieldsInvariantRule;
-import com.carrotsearch.randomizedtesting.rules.SystemPropertiesInvariantRule;
-
 import static com.carrotsearch.randomizedtesting.RandomizedTest.systemPropertyAsBoolean;
 import static com.carrotsearch.randomizedtesting.RandomizedTest.systemPropertyAsInt;
 
@@ -468,9 +424,9 @@
   static {
     CORE_DIRECTORIES = new ArrayList<>(FS_DIRECTORIES);
     CORE_DIRECTORIES.add("RAMDirectory");
-  };
+  }
 
-  /** A {@link FilterCachingPolicy} that randomly caches. */
+  /** A {@link org.apache.lucene.search.FilterCachingPolicy} that randomly caches. */
   public static final FilterCachingPolicy MAYBE_CACHE_POLICY = new FilterCachingPolicy() {
 
     @Override
@@ -482,7 +438,7 @@
     }
 
   };
-
+  
   // -----------------------------------------------------------------
   // Fields initialized in class or instance rules.
   // -----------------------------------------------------------------
@@ -1291,10 +1247,6 @@
     String fsdirClass = TEST_DIRECTORY;
     if (fsdirClass.equals("random")) {
       fsdirClass = RandomPicks.randomFrom(random(), FS_DIRECTORIES); 
-      if (fsdirClass.equals("SimpleFSDirectory")) {
-        // pick again
-        fsdirClass = RandomPicks.randomFrom(random(), FS_DIRECTORIES); 
-      }
     }
 
     Class<? extends FSDirectory> clazz;
@@ -1334,7 +1286,7 @@
       directory = new NRTCachingDirectory(directory, random.nextDouble(), random.nextDouble());
     }
     
-    if (TEST_NIGHTLY && rarely(random) && !bare) { 
+    if (rarely(random) && !bare) { 
       final double maxMBPerSec = TestUtil.nextInt(random, 20, 40);
       if (LuceneTestCase.VERBOSE) {
         System.out.println("LuceneTestCase: will rate limit output IndexOutput to " + maxMBPerSec + " MB/sec");
@@ -1475,7 +1427,7 @@
 
   /** 
    * Return a random Locale from the available locales on the system.
-   * @see <a href="https://issues.apache.org/jira/browse/LUCENE-4020">LUCENE-4020</a>
+   * @see "https://issues.apache.org/jira/browse/LUCENE-4020"
    */
   public static Locale randomLocale(Random random) {
     Locale locales[] = Locale.getAvailableLocales();
@@ -1484,7 +1436,7 @@
 
   /** 
    * Return a random TimeZone from the available timezones on the system
-   * @see <a href="https://issues.apache.org/jira/browse/LUCENE-4020">LUCENE-4020</a>
+   * @see "https://issues.apache.org/jira/browse/LUCENE-4020" 
    */
   public static TimeZone randomTimeZone(Random random) {
     String tzIds[] = TimeZone.getAvailableIDs();
@@ -1521,10 +1473,6 @@
     if (clazzName.equals("random")) {
       if (rarely(random)) {
         clazzName = RandomPicks.randomFrom(random, CORE_DIRECTORIES);
-        if (clazzName.equals("SimpleFSDirectory")) {
-          // pick again
-          clazzName = RandomPicks.randomFrom(random, CORE_DIRECTORIES);
-        }
       } else {
         clazzName = "RAMDirectory";
       }
@@ -1930,8 +1878,8 @@
   public void assertTermsEnumEquals(String info, IndexReader leftReader, TermsEnum leftTermsEnum, TermsEnum rightTermsEnum, boolean deep) throws IOException {
     BytesRef term;
     Bits randomBits = new RandomBits(leftReader.maxDoc(), random().nextDouble(), random());
-    DocsAndPositionsEnum leftPositions = null;
-    DocsAndPositionsEnum rightPositions = null;
+    DocsEnum leftPositions = null;
+    DocsEnum rightPositions = null;
     DocsEnum leftDocs = null;
     DocsEnum rightDocs = null;
     
@@ -1995,7 +1943,7 @@
   /**
    * checks docs + freqs + positions + payloads, sequentially
    */
-  public void assertDocsAndPositionsEnumEquals(String info, DocsAndPositionsEnum leftDocs, DocsAndPositionsEnum rightDocs) throws IOException {
+  public void assertDocsAndPositionsEnumEquals(String info, DocsEnum leftDocs, DocsEnum rightDocs) throws IOException {
     if (leftDocs == null || rightDocs == null) {
       assertNull(leftDocs);
       assertNull(rightDocs);
@@ -2074,7 +2022,7 @@
   /**
    * checks advancing docs + positions
    */
-  public void assertPositionsSkippingEquals(String info, IndexReader leftReader, int docFreq, DocsAndPositionsEnum leftDocs, DocsAndPositionsEnum rightDocs) throws IOException {
+  public void assertPositionsSkippingEquals(String info, IndexReader leftReader, int docFreq, DocsEnum leftDocs, DocsEnum rightDocs) throws IOException {
     if (leftDocs == null || rightDocs == null) {
       assertNull(leftDocs);
       assertNull(rightDocs);
diff --git a/lucene/test-framework/src/java/org/apache/lucene/util/TestUtil.java b/lucene/test-framework/src/java/org/apache/lucene/util/TestUtil.java
index ad18f00..602e207 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/util/TestUtil.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/util/TestUtil.java
@@ -43,6 +43,8 @@
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 
+import com.carrotsearch.randomizedtesting.generators.RandomInts;
+import com.carrotsearch.randomizedtesting.generators.RandomPicks;
 import org.apache.lucene.codecs.Codec;
 import org.apache.lucene.codecs.DocValuesFormat;
 import org.apache.lucene.codecs.PostingsFormat;
@@ -67,7 +69,6 @@
 import org.apache.lucene.index.CheckIndex;
 import org.apache.lucene.index.ConcurrentMergeScheduler;
 import org.apache.lucene.index.DocValuesType;
-import org.apache.lucene.index.DocsAndPositionsEnum;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FilterLeafReader;
 import org.apache.lucene.index.IndexReader;
@@ -86,8 +87,8 @@
 import org.apache.lucene.mockfile.FilterFileSystem;
 import org.apache.lucene.mockfile.WindowsFS;
 import org.apache.lucene.search.FieldDoc;
-import org.apache.lucene.search.FilteredQuery.FilterStrategy;
 import org.apache.lucene.search.FilteredQuery;
+import org.apache.lucene.search.FilteredQuery.FilterStrategy;
 import org.apache.lucene.search.ScoreDoc;
 import org.apache.lucene.search.TopDocs;
 import org.apache.lucene.store.Directory;
@@ -96,8 +97,6 @@
 import org.apache.lucene.store.NoLockFactory;
 import org.junit.Assert;
 
-import com.carrotsearch.randomizedtesting.generators.RandomInts;
-import com.carrotsearch.randomizedtesting.generators.RandomPicks;
 
 /**
  * General utility methods for Lucene unit tests. 
@@ -959,13 +958,13 @@
       if (random.nextBoolean()) {
         final int posFlags;
         switch (random.nextInt(4)) {
-          case 0: posFlags = 0; break;
-          case 1: posFlags = DocsAndPositionsEnum.FLAG_OFFSETS; break;
-          case 2: posFlags = DocsAndPositionsEnum.FLAG_PAYLOADS; break;
-          default: posFlags = DocsAndPositionsEnum.FLAG_OFFSETS | DocsAndPositionsEnum.FLAG_PAYLOADS; break;
+          case 0: posFlags = DocsEnum.FLAG_POSITIONS; break;
+          case 1: posFlags = DocsEnum.FLAG_OFFSETS; break;
+          case 2: posFlags = DocsEnum.FLAG_PAYLOADS; break;
+          default: posFlags = DocsEnum.FLAG_OFFSETS | DocsEnum.FLAG_PAYLOADS; break;
         }
         // TODO: cast to DocsAndPositionsEnum?
-        DocsAndPositionsEnum docsAndPositions = termsEnum.docsAndPositions(liveDocs, null, posFlags);
+        DocsEnum docsAndPositions = termsEnum.docsAndPositions(liveDocs, null, posFlags);
         if (docsAndPositions != null) {
           return docsAndPositions;
         }
diff --git a/solr/core/src/java/org/apache/solr/handler/component/ExpandComponent.java b/solr/core/src/java/org/apache/solr/handler/component/ExpandComponent.java
index 6b39a01..22c69f7 100644
--- a/solr/core/src/java/org/apache/solr/handler/component/ExpandComponent.java
+++ b/solr/core/src/java/org/apache/solr/handler/component/ExpandComponent.java
@@ -25,6 +25,11 @@
 import java.util.List;
 import java.util.Map;
 
+import com.carrotsearch.hppc.IntObjectMap;
+import com.carrotsearch.hppc.IntObjectOpenHashMap;
+import com.carrotsearch.hppc.IntOpenHashSet;
+import com.carrotsearch.hppc.cursors.IntObjectCursor;
+import com.carrotsearch.hppc.cursors.ObjectCursor;
 import org.apache.lucene.index.DocValues;
 import org.apache.lucene.index.LeafReader;
 import org.apache.lucene.index.LeafReaderContext;
@@ -63,12 +68,6 @@
 import org.apache.solr.util.plugin.PluginInfoInitialized;
 import org.apache.solr.util.plugin.SolrCoreAware;
 
-import com.carrotsearch.hppc.IntObjectMap;
-import com.carrotsearch.hppc.IntObjectOpenHashMap;
-import com.carrotsearch.hppc.IntOpenHashSet;
-import com.carrotsearch.hppc.cursors.IntObjectCursor;
-import com.carrotsearch.hppc.cursors.ObjectCursor;
-
 /**
  * The ExpandComponent is designed to work with the CollapsingPostFilter.
  * The CollapsingPostFilter collapses a result set on a field.
diff --git a/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java b/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java
index 9fb4c76..25d0f64 100644
--- a/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java
+++ b/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java
@@ -24,6 +24,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -31,8 +32,8 @@
 import java.util.Locale;
 import java.util.Map;
 
-import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.IndexReaderContext;
+import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.ReaderUtil;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.BooleanQuery;
@@ -52,7 +53,13 @@
 import org.apache.solr.common.SolrDocument;
 import org.apache.solr.common.SolrDocumentList;
 import org.apache.solr.common.SolrException;
-import org.apache.solr.common.params.*;
+import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.common.params.CursorMarkParams;
+import org.apache.solr.common.params.GroupParams;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.params.MoreLikeThisParams;
+import org.apache.solr.common.params.ShardParams;
+import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.SimpleOrderedMap;
 import org.apache.solr.common.util.StrUtils;
@@ -71,11 +78,11 @@
 import org.apache.solr.search.QParser;
 import org.apache.solr.search.QParserPlugin;
 import org.apache.solr.search.QueryParsing;
+import org.apache.solr.search.RankQuery;
 import org.apache.solr.search.ReturnFields;
 import org.apache.solr.search.SolrIndexSearcher;
 import org.apache.solr.search.SolrReturnFields;
 import org.apache.solr.search.SortSpec;
-import org.apache.solr.search.RankQuery;
 import org.apache.solr.search.SyntaxError;
 import org.apache.solr.search.grouping.CommandHandler;
 import org.apache.solr.search.grouping.GroupingSpecification;
@@ -97,7 +104,6 @@
 import org.apache.solr.search.grouping.endresulttransformer.MainEndResultTransformer;
 import org.apache.solr.search.grouping.endresulttransformer.SimpleEndResultTransformer;
 import org.apache.solr.util.SolrPluginUtils;
-import java.util.Collections;
 
 /**
  * TODO!
@@ -1298,6 +1304,36 @@
     }
 
     @Override
+    public int nextPosition() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
     public int nextDoc() throws IOException {
       throw new UnsupportedOperationException();
     }
diff --git a/solr/core/src/java/org/apache/solr/handler/component/TermVectorComponent.java b/solr/core/src/java/org/apache/solr/handler/component/TermVectorComponent.java
index e807daa..602282e 100644
--- a/solr/core/src/java/org/apache/solr/handler/component/TermVectorComponent.java
+++ b/solr/core/src/java/org/apache/solr/handler/component/TermVectorComponent.java
@@ -13,7 +13,7 @@
 import java.util.Map;
 import java.util.Map.Entry;
 
-import org.apache.lucene.index.DocsAndPositionsEnum;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.Fields;
 import org.apache.lucene.index.IndexReader;
@@ -335,7 +335,7 @@
     docNL.add(field, fieldNL);
 
     BytesRef text;
-    DocsAndPositionsEnum dpEnum = null;
+    DocsEnum dpEnum = null;
     while((text = termsEnum.next()) != null) {
       String term = text.utf8ToString();
       NamedList<Object> termInfo = new NamedList<>();
diff --git a/solr/core/src/java/org/apache/solr/schema/LatLonType.java b/solr/core/src/java/org/apache/solr/schema/LatLonType.java
index d29122a..ba5735f 100644
--- a/solr/core/src/java/org/apache/solr/schema/LatLonType.java
+++ b/solr/core/src/java/org/apache/solr/schema/LatLonType.java
@@ -22,11 +22,14 @@
 import java.util.Map;
 import java.util.Set;
 
+import com.spatial4j.core.context.SpatialContext;
+import com.spatial4j.core.distance.DistanceUtils;
 import com.spatial4j.core.shape.Point;
-
+import com.spatial4j.core.shape.Rectangle;
 import org.apache.lucene.document.FieldType;
-import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.StorableField;
 import org.apache.lucene.queries.function.FunctionValues;
 import org.apache.lucene.queries.function.ValueSource;
@@ -42,6 +45,7 @@
 import org.apache.lucene.search.Weight;
 import org.apache.lucene.uninverting.UninvertingReader.Type;
 import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.response.TextResponseWriter;
 import org.apache.solr.search.DelegatingCollector;
@@ -49,11 +53,6 @@
 import org.apache.solr.search.PostFilter;
 import org.apache.solr.search.QParser;
 import org.apache.solr.search.SpatialOptions;
-
-import com.spatial4j.core.context.SpatialContext;
-import com.spatial4j.core.distance.DistanceUtils;
-import com.spatial4j.core.shape.Rectangle;
-
 import org.apache.solr.util.SpatialUtils;
 
 
@@ -341,13 +340,13 @@
     }
 
     @Override
-    public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
+    public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
       return new SpatialScorer(context, acceptDocs, this, queryWeight);
     }
 
     @Override
     public Explanation explain(LeafReaderContext context, int doc) throws IOException {
-      return ((SpatialScorer)scorer(context, context.reader().getLiveDocs())).explain(doc);
+      return ((SpatialScorer)scorer(context, DocsEnum.FLAG_FREQS, context.reader().getLiveDocs())).explain(doc);
     }
   }
 
@@ -484,6 +483,36 @@
     }
 
     @Override
+    public int nextPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
+    @Override
     public long cost() {
       return maxDoc;
     }
@@ -506,6 +535,7 @@
       result.addDetail(new Explanation(weight.queryNorm,"queryNorm"));
       return result;
     }
+
   }
 
   @Override
diff --git a/solr/core/src/java/org/apache/solr/search/CollapsingQParserPlugin.java b/solr/core/src/java/org/apache/solr/search/CollapsingQParserPlugin.java
index 9e84445..12670b1 100644
--- a/solr/core/src/java/org/apache/solr/search/CollapsingQParserPlugin.java
+++ b/solr/core/src/java/org/apache/solr/search/CollapsingQParserPlugin.java
@@ -22,6 +22,10 @@
 import java.util.Iterator;
 import java.util.Map;
 
+import com.carrotsearch.hppc.FloatArrayList;
+import com.carrotsearch.hppc.IntIntOpenHashMap;
+import com.carrotsearch.hppc.IntOpenHashSet;
+import com.carrotsearch.hppc.cursors.IntIntCursor;
 import org.apache.lucene.index.DocValues;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.NumericDocValues;
@@ -49,11 +53,6 @@
 import org.apache.solr.schema.TrieIntField;
 import org.apache.solr.schema.TrieLongField;
 
-import com.carrotsearch.hppc.FloatArrayList;
-import com.carrotsearch.hppc.IntIntOpenHashMap;
-import com.carrotsearch.hppc.IntOpenHashSet;
-import com.carrotsearch.hppc.cursors.IntIntCursor;
-
 /**
 
  The <b>CollapsingQParserPlugin</b> is a PostFilter that performs field collapsing.
@@ -354,6 +353,36 @@
       return 0;
     }
 
+    @Override
+    public int nextPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
     public int advance(int i) {
       return -1;
     }
diff --git a/solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java b/solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java
index c371ec1..f2acb6f 100644
--- a/solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java
+++ b/solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java
@@ -23,10 +23,10 @@
 import java.util.List;
 import java.util.Set;
 
-import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.Fields;
 import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.MultiDocsEnum;
 import org.apache.lucene.index.Terms;
 import org.apache.lucene.index.TermsEnum;
@@ -57,7 +57,6 @@
 import org.apache.solr.schema.TrieField;
 import org.apache.solr.util.RefCounted;
 
-
 public class JoinQParserPlugin extends QParserPlugin {
   public static final String NAME = "join";
 
@@ -232,7 +231,7 @@
 
 
     @Override
-    public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
+    public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
       if (filter == null) {
         boolean debug = rb != null && rb.isDebug();
         long start = debug ? System.currentTimeMillis() : 0;
@@ -501,7 +500,7 @@
 
     @Override
     public Explanation explain(LeafReaderContext context, int doc) throws IOException {
-      Scorer scorer = scorer(context, context.reader().getLiveDocs());
+      Scorer scorer = scorer(context, DocsEnum.FLAG_FREQS, context.reader().getLiveDocs());
       boolean exists = scorer.advance(doc) == doc;
 
       ComplexExplanation result = new ComplexExplanation();
@@ -556,6 +555,36 @@
     }
 
     @Override
+    public int nextPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
+    @Override
     public int advance(int target) throws IOException {
       return iter.advance(target);
     }
diff --git a/solr/core/src/java/org/apache/solr/search/ReRankQParserPlugin.java b/solr/core/src/java/org/apache/solr/search/ReRankQParserPlugin.java
index 5e744be..12dc8ed 100644
--- a/solr/core/src/java/org/apache/solr/search/ReRankQParserPlugin.java
+++ b/solr/core/src/java/org/apache/solr/search/ReRankQParserPlugin.java
@@ -194,8 +194,8 @@
       return mainWeight.getValueForNormalization();
     }
 
-    public Scorer scorer(LeafReaderContext context, Bits bits) throws IOException {
-      return mainWeight.scorer(context, bits);
+    public Scorer scorer(LeafReaderContext context, int flags, Bits bits) throws IOException {
+      return mainWeight.scorer(context, flags, bits);
     }
 
     public Query getQuery() {
diff --git a/solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java b/solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java
index fa37caf..4e28b4f 100644
--- a/solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java
+++ b/solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java
@@ -1,16 +1,26 @@
 package org.apache.solr.search;
 
+import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.search.*;
+import org.apache.lucene.search.ComplexExplanation;
+import org.apache.lucene.search.ConstantScoreQuery;
+import org.apache.lucene.search.DocIdSet;
+import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.Filter;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Weight;
 import org.apache.lucene.util.Bits;
-import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.util.BytesRef;
 import org.apache.solr.common.SolrException;
 
-import java.io.IOException;
-import java.util.Set;
-import java.util.Map;
-
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -119,7 +129,7 @@
     }
 
     @Override
-    public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
+    public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
       return new ConstantScorer(context, this, queryWeight, acceptDocs);
     }
 
@@ -192,6 +202,36 @@
     }
 
     @Override
+    public int nextPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int endPosition() throws IOException {
+      return NO_MORE_POSITIONS;
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return -1;
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return null;
+    }
+
+    @Override
     public int advance(int target) throws IOException {
       return docIdSetIterator.advance(target);
     }
diff --git a/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java b/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
index e2d1f07..dad5358 100644
--- a/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
+++ b/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
@@ -17,23 +17,6 @@
 
 package org.apache.solr.search;
 
-import java.io.Closeable;
-import java.io.IOException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicLong;
-
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.DoubleField;
 import org.apache.lucene.document.Field;
@@ -61,34 +44,7 @@
 import org.apache.lucene.index.Term;
 import org.apache.lucene.index.Terms;
 import org.apache.lucene.index.TermsEnum;
-import org.apache.lucene.search.LeafCollector;
-import org.apache.lucene.search.BooleanClause;
-import org.apache.lucene.search.BooleanQuery;
-import org.apache.lucene.search.Collector;
-import org.apache.lucene.search.ConstantScoreQuery;
-import org.apache.lucene.search.DocIdSet;
-import org.apache.lucene.search.DocIdSetIterator;
-import org.apache.lucene.search.Explanation;
-import org.apache.lucene.search.FieldDoc;
-import org.apache.lucene.search.Filter;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.MatchAllDocsQuery;
-import org.apache.lucene.search.MultiCollector;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.ScoreDoc;
-import org.apache.lucene.search.Scorer;
-import org.apache.lucene.search.SimpleCollector;
-import org.apache.lucene.search.Sort;
-import org.apache.lucene.search.SortField;
-import org.apache.lucene.search.TermQuery;
-import org.apache.lucene.search.TimeLimitingCollector;
-import org.apache.lucene.search.TopDocs;
-import org.apache.lucene.search.TopDocsCollector;
-import org.apache.lucene.search.TopFieldCollector;
-import org.apache.lucene.search.TopFieldDocs;
-import org.apache.lucene.search.TopScoreDocCollector;
-import org.apache.lucene.search.TotalHitCountCollector;
-import org.apache.lucene.search.Weight;
+import org.apache.lucene.search.*;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.uninverting.UninvertingReader;
 import org.apache.lucene.util.Bits;
@@ -115,6 +71,23 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
 
 /**
  * SolrIndexSearcher adds schema awareness and caching functionality
@@ -2461,7 +2434,7 @@
         iterators.add(iter);
       }
       for (Weight w : weights) {
-        Scorer scorer = w.scorer(context, context.reader().getLiveDocs());
+        Scorer scorer = w.scorer(context, DocsEnum.FLAG_FREQS, context.reader().getLiveDocs());
         if (scorer == null) return null;
         iterators.add(scorer);
       }
diff --git a/solr/core/src/java/org/apache/solr/search/join/IgnoreAcceptDocsQuery.java b/solr/core/src/java/org/apache/solr/search/join/IgnoreAcceptDocsQuery.java
index d550889..37e66c2 100644
--- a/solr/core/src/java/org/apache/solr/search/join/IgnoreAcceptDocsQuery.java
+++ b/solr/core/src/java/org/apache/solr/search/join/IgnoreAcceptDocsQuery.java
@@ -86,8 +86,8 @@
     }
 
     @Override
-    public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
-      return w.scorer(context, null);
+    public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
+      return w.scorer(context, flags, null);
     }
   }
 
diff --git a/solr/core/src/java/org/apache/solr/update/DeleteByQueryWrapper.java b/solr/core/src/java/org/apache/solr/update/DeleteByQueryWrapper.java
index 68d888a..fe644ab 100644
--- a/solr/core/src/java/org/apache/solr/update/DeleteByQueryWrapper.java
+++ b/solr/core/src/java/org/apache/solr/update/DeleteByQueryWrapper.java
@@ -17,11 +17,9 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-
+import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.LeafReader;
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.search.Explanation;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Query;
@@ -31,6 +29,8 @@
 import org.apache.lucene.util.Bits;
 import org.apache.solr.schema.IndexSchema;
 
+import java.io.IOException;
+
 /** 
  * Allows access to uninverted docvalues by delete-by-queries.
  * this is used e.g. to implement versioning constraints in solr.
@@ -82,8 +82,8 @@
       public void normalize(float norm, float topLevelBoost) { inner.normalize(norm, topLevelBoost); }
 
       @Override
-      public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
-        return inner.scorer(privateContext.getIndexReader().leaves().get(0), acceptDocs);
+      public Scorer scorer(LeafReaderContext context, int flags, Bits acceptDocs) throws IOException {
+        return inner.scorer(privateContext.getIndexReader().leaves().get(0), flags, acceptDocs);
       }
     };
   }
diff --git a/solr/core/src/test/org/apache/solr/search/TestRankQueryPlugin.java b/solr/core/src/test/org/apache/solr/search/TestRankQueryPlugin.java
index 2e9a11a..995e383 100644
--- a/solr/core/src/test/org/apache/solr/search/TestRankQueryPlugin.java
+++ b/solr/core/src/test/org/apache/solr/search/TestRankQueryPlugin.java
@@ -17,21 +17,33 @@
 
 package org.apache.solr.search;
 
-import org.apache.lucene.index.LeafReaderContext;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import org.apache.lucene.index.DocValues;
 import org.apache.lucene.index.IndexReaderContext;
+import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.NumericDocValues;
 import org.apache.lucene.index.ReaderUtil;
 import org.apache.lucene.search.FieldComparator;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
 import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.Sort;
 import org.apache.lucene.search.SortField;
 import org.apache.lucene.search.TopDocs;
 import org.apache.lucene.search.TopDocsCollector;
-import org.apache.lucene.search.Query;
 import org.apache.lucene.search.Weight;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.Scorer;
+import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.InPlaceMergeSorter;
 import org.apache.lucene.util.PriorityQueue;
 import org.apache.solr.client.solrj.SolrServerException;
@@ -46,25 +58,13 @@
 import org.apache.solr.handler.component.ShardDoc;
 import org.apache.solr.handler.component.ShardRequest;
 import org.apache.solr.handler.component.ShardResponse;
+import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.SolrQueryResponse;
 import org.apache.solr.schema.FieldType;
 import org.apache.solr.schema.IndexSchema;
 import org.apache.solr.schema.SchemaField;
-import org.apache.solr.request.SolrQueryRequest;
-
 import org.junit.Ignore;
 
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
 
 @Ignore
 public class TestRankQueryPlugin extends QParserPlugin {
@@ -451,57 +451,88 @@
       }
     }
 
-   private class FakeScorer extends Scorer {
-        final int docid;
-        final float score;
+    private class FakeScorer extends Scorer {
 
-        FakeScorer(int docid, float score) {
-          super(null);
-          this.docid = docid;
-          this.score = score;
-        }
+      final int docid;
+      final float score;
 
-        @Override
-        public int docID() {
-          return docid;
-        }
-
-        @Override
-        public float score() throws IOException {
-          return score;
-        }
-
-        @Override
-        public int freq() throws IOException {
-          throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public int nextDoc() throws IOException {
-          throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public int advance(int target) throws IOException {
-          throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public long cost() {
-          return 1;
-        }
-
-        @Override
-        public Weight getWeight() {
-          throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public Collection<ChildScorer> getChildren() {
-          throw new UnsupportedOperationException();
-        }
+      FakeScorer(int docid, float score) {
+        super(null);
+        this.docid = docid;
+        this.score = score;
       }
 
+      @Override
+      public int docID() {
+        return docid;
+      }
+
+      @Override
+      public float score() throws IOException {
+        return score;
+      }
+
+      @Override
+      public int freq() throws IOException {
+        throw new UnsupportedOperationException();
+      }
+
+      @Override
+      public int nextPosition() throws IOException {
+        return NO_MORE_POSITIONS;
+      }
+
+      @Override
+      public int startPosition() throws IOException {
+        return NO_MORE_POSITIONS;
+      }
+
+      @Override
+      public int endPosition() throws IOException {
+        return NO_MORE_POSITIONS;
+      }
+
+      @Override
+      public int startOffset() throws IOException {
+        return -1;
+      }
+
+      @Override
+      public int endOffset() throws IOException {
+        return -1;
+      }
+
+      @Override
+      public BytesRef getPayload() throws IOException {
+        return null;
+      }
+
+      @Override
+      public int nextDoc() throws IOException {
+        throw new UnsupportedOperationException();
+      }
+
+      @Override
+      public int advance(int target) throws IOException {
+        throw new UnsupportedOperationException();
+      }
+
+      @Override
+      public long cost() {
+        return 1;
+      }
+
+      @Override
+      public Weight getWeight() {
+        throw new UnsupportedOperationException();
+      }
+
+      @Override
+      public Collection<ChildScorer> getChildren() {
+        throw new UnsupportedOperationException();
+      }
+    }
+
     public void merge(ResponseBuilder rb, ShardRequest sreq) {
 
       // id to shard mapping, to eliminate any accidental dups