Index: solr/cloud-dev
===================================================================
--- solr/cloud-dev	(.../trunk)	(revision 1645925)
+++ solr/cloud-dev	(.../branches/lucene2878)	(revision 1645925)

Property changes on: solr/cloud-dev
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/solr/cloud-dev:r1638800-1643324
Index: solr/common-build.xml
===================================================================
--- solr/common-build.xml	(.../trunk)	(revision 1645925)
+++ solr/common-build.xml	(.../branches/lucene2878)	(revision 1645925)

Property changes on: solr/common-build.xml
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/solr/common-build.xml:r1638800-1642714
Index: solr/CHANGES.txt
===================================================================
--- solr/CHANGES.txt	(.../trunk)	(revision 1645925)
+++ solr/CHANGES.txt	(.../branches/lucene2878)	(revision 1645925)

Property changes on: solr/CHANGES.txt
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/solr/CHANGES.txt:r1638800-1645916
Index: solr/solrj
===================================================================
--- solr/solrj	(.../trunk)	(revision 1645925)
+++ solr/solrj	(.../branches/lucene2878)	(revision 1645925)

Property changes on: solr/solrj
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/solr/solrj:r1638800-1645916
Index: solr/example
===================================================================
--- solr/example	(.../trunk)	(revision 1645925)
+++ solr/example	(.../branches/lucene2878)	(revision 1645925)

Property changes on: solr/example
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/solr/example:r1638800-1645535
Index: solr/build.xml
===================================================================
--- solr/build.xml	(.../trunk)	(revision 1645925)
+++ solr/build.xml	(.../branches/lucene2878)	(revision 1645925)

Property changes on: solr/build.xml
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/solr/build.xml:r1638800-1645916
Index: solr/contrib
===================================================================
--- solr/contrib	(.../trunk)	(revision 1645925)
+++ solr/contrib	(.../branches/lucene2878)	(revision 1645925)

Property changes on: solr/contrib
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/solr/contrib:r1638800-1645535
Index: solr/site
===================================================================
--- solr/site	(.../trunk)	(revision 1645925)
+++ solr/site	(.../branches/lucene2878)	(revision 1645925)

Property changes on: solr/site
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/solr/site:r1638800-1642144
Index: solr/SYSTEM_REQUIREMENTS.txt
===================================================================
--- solr/SYSTEM_REQUIREMENTS.txt	(.../trunk)	(revision 1645925)
+++ solr/SYSTEM_REQUIREMENTS.txt	(.../branches/lucene2878)	(revision 1645925)

Property changes on: solr/SYSTEM_REQUIREMENTS.txt
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/solr/SYSTEM_REQUIREMENTS.txt:r1638800-1642144
Index: solr/licenses
===================================================================
--- solr/licenses	(.../trunk)	(revision 1645925)
+++ solr/licenses	(.../branches/lucene2878)	(revision 1645925)

Property changes on: solr/licenses
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/solr/licenses:r1638800-1642144
Index: solr/bin
===================================================================
--- solr/bin	(.../trunk)	(revision 1645925)
+++ solr/bin	(.../branches/lucene2878)	(revision 1645925)

Property changes on: solr/bin
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/solr/bin:r1638800-1645535
Index: solr/test-framework
===================================================================
--- solr/test-framework	(.../trunk)	(revision 1645925)
+++ solr/test-framework	(.../branches/lucene2878)	(revision 1645925)

Property changes on: solr/test-framework
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/solr/test-framework:r1638800-1645535
Index: solr/README.txt
===================================================================
--- solr/README.txt	(.../trunk)	(revision 1645925)
+++ solr/README.txt	(.../branches/lucene2878)	(revision 1645925)

Property changes on: solr/README.txt
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/solr/README.txt:r1638800-1645916
Index: solr/webapp
===================================================================
--- solr/webapp	(.../trunk)	(revision 1645925)
+++ solr/webapp	(.../branches/lucene2878)	(revision 1645925)

Property changes on: solr/webapp
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/solr/webapp:r1638800-1643324
Index: lucene/benchmark
===================================================================
--- lucene/benchmark	(.../trunk)	(revision 1645925)
+++ lucene/benchmark	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/benchmark
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/benchmark:r1638800-1645535
Index: lucene/spatial
===================================================================
--- lucene/spatial	(.../trunk)	(revision 1645925)
+++ lucene/spatial	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/spatial
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/spatial:r1638800-1645535
Index: lucene/build.xml
===================================================================
--- lucene/build.xml	(.../trunk)	(revision 1645925)
+++ lucene/build.xml	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/build.xml
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/build.xml:r1638800-1642714
Index: lucene/tools
===================================================================
--- lucene/tools	(.../trunk)	(revision 1645925)
+++ lucene/tools	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/tools
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/tools:r1638800-1642144
Index: lucene/BUILD.txt
===================================================================
--- lucene/BUILD.txt	(.../trunk)	(revision 1645925)
+++ lucene/BUILD.txt	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/BUILD.txt
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/BUILD.txt:r1638800-1642144
Index: lucene/common-build.xml
===================================================================
--- lucene/common-build.xml	(.../trunk)	(revision 1645925)
+++ lucene/common-build.xml	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/common-build.xml
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/common-build.xml:r1638800-1642714
Index: lucene/CHANGES.txt
===================================================================
--- lucene/CHANGES.txt	(.../trunk)	(revision 1645925)
+++ lucene/CHANGES.txt	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/CHANGES.txt
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/CHANGES.txt:r1638800-1645916
Index: dev-tools/idea/lucene/highlighter/highlighter.iml
===================================================================
--- dev-tools/idea/lucene/highlighter/highlighter.iml	(.../trunk)	(revision 1645925)
+++ dev-tools/idea/lucene/highlighter/highlighter.iml	(.../branches/lucene2878)	(revision 1645925)
@@ -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" />
Index: dev-tools
===================================================================
--- dev-tools	(.../trunk)	(revision 1645925)
+++ dev-tools	(.../branches/lucene2878)	(revision 1645925)

Property changes on: dev-tools
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/dev-tools:r1638800-1642714
Index: solr/core/src/test/org/apache/solr/search/TestRankQueryPlugin.java
===================================================================
--- solr/core/src/test/org/apache/solr/search/TestRankQueryPlugin.java	(.../trunk)	(revision 1645925)
+++ solr/core/src/test/org/apache/solr/search/TestRankQueryPlugin.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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,26 +58,14 @@
 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;
-        }
+      FakeScorer(int docid, float score) {
+        super(null);
+        this.docid = docid;
+        this.score = score;
+      }
 
-        @Override
-        public float score() throws IOException {
-          return score;
-        }
+      @Override
+      public int docID() {
+        return docid;
+      }
 
-        @Override
-        public int freq() throws IOException {
-          throw new UnsupportedOperationException();
-        }
+      @Override
+      public float score() throws IOException {
+        return score;
+      }
 
-        @Override
-        public int nextDoc() throws IOException {
-          throw new UnsupportedOperationException();
-        }
+      @Override
+      public int freq() throws IOException {
+        throw new UnsupportedOperationException();
+      }
 
-        @Override
-        public int advance(int target) throws IOException {
-          throw new UnsupportedOperationException();
-        }
+      @Override
+      public int nextPosition() throws IOException {
+        return NO_MORE_POSITIONS;
+      }
 
-        @Override
-        public long cost() {
-          return 1;
-        }
+      @Override
+      public int startPosition() throws IOException {
+        return NO_MORE_POSITIONS;
+      }
 
-        @Override
-        public Weight getWeight() {
-          throw new UnsupportedOperationException();
-        }
+      @Override
+      public int endPosition() throws IOException {
+        return NO_MORE_POSITIONS;
+      }
 
-        @Override
-        public Collection<ChildScorer> getChildren() {
-          throw new UnsupportedOperationException();
-        }
+      @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
Index: solr/core/src/java/org/apache/solr/schema/LatLonType.java
===================================================================
--- solr/core/src/java/org/apache/solr/schema/LatLonType.java	(.../trunk)	(revision 1645925)
+++ solr/core/src/java/org/apache/solr/schema/LatLonType.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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.DocsEnum;
+import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.IndexReader;
 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
Index: solr/core/src/java/org/apache/solr/search/ReRankQParserPlugin.java
===================================================================
--- solr/core/src/java/org/apache/solr/search/ReRankQParserPlugin.java	(.../trunk)	(revision 1645925)
+++ solr/core/src/java/org/apache/solr/search/ReRankQParserPlugin.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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() {
Index: solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java
===================================================================
--- solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java	(.../trunk)	(revision 1645925)
+++ solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
     }
Index: solr/core/src/java/org/apache/solr/search/CollapsingQParserPlugin.java
===================================================================
--- solr/core/src/java/org/apache/solr/search/CollapsingQParserPlugin.java	(.../trunk)	(revision 1645925)
+++ solr/core/src/java/org/apache/solr/search/CollapsingQParserPlugin.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
     }
Index: solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java
===================================================================
--- solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java	(.../trunk)	(revision 1645925)
+++ solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
     }
Index: solr/core/src/java/org/apache/solr/search/join/IgnoreAcceptDocsQuery.java
===================================================================
--- solr/core/src/java/org/apache/solr/search/join/IgnoreAcceptDocsQuery.java	(.../trunk)	(revision 1645925)
+++ solr/core/src/java/org/apache/solr/search/join/IgnoreAcceptDocsQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
     }
   }
 
Index: solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
===================================================================
--- solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java	(.../trunk)	(revision 1645925)
+++ solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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,7 +71,24 @@
 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
  * over the lucene IndexSearcher.
@@ -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);
       }
Index: solr/core/src/java/org/apache/solr/handler/component/TermVectorComponent.java
===================================================================
--- solr/core/src/java/org/apache/solr/handler/component/TermVectorComponent.java	(.../trunk)	(revision 1645925)
+++ solr/core/src/java/org/apache/solr/handler/component/TermVectorComponent.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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<>();
Index: solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java
===================================================================
--- solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java	(.../trunk)	(revision 1645925)
+++ solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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.IndexReaderContext;
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.IndexReaderContext;
 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();
     }
Index: solr/core/src/java/org/apache/solr/handler/component/ExpandComponent.java
===================================================================
--- solr/core/src/java/org/apache/solr/handler/component/ExpandComponent.java	(.../trunk)	(revision 1645925)
+++ solr/core/src/java/org/apache/solr/handler/component/ExpandComponent.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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.
Index: solr/core/src/java/org/apache/solr/update/DeleteByQueryWrapper.java
===================================================================
--- solr/core/src/java/org/apache/solr/update/DeleteByQueryWrapper.java	(.../trunk)	(revision 1645925)
+++ solr/core/src/java/org/apache/solr/update/DeleteByQueryWrapper.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
       }
     };
   }
Index: solr/core
===================================================================
--- solr/core	(.../trunk)	(revision 1645925)
+++ solr/core	(.../branches/lucene2878)	(revision 1645925)

Property changes on: solr/core
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/solr/core:r1638800-1645916
Index: solr
===================================================================
--- solr	(.../trunk)	(revision 1645925)
+++ solr	(.../branches/lucene2878)	(revision 1645925)

Property changes on: solr
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/solr:r1638800-1645916
Index: lucene/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java
===================================================================
--- lucene/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java	(.../trunk)	(revision 1645925)
+++ lucene/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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 {
Index: lucene/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java
===================================================================
--- lucene/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java	(.../trunk)	(revision 1645925)
+++ lucene/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java	(.../branches/lucene2878)	(revision 1645925)
@@ -17,13 +17,22 @@
  * limitations under the License.
  */
 
-
 import java.io.IOException;
-import java.util.Collection;
 
+import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.IndexWriter;
-import org.apache.lucene.search.*;
+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;
Index: lucene/misc/src/test/org/apache/lucene/index/sorter/SorterTestBase.java
===================================================================
--- lucene/misc/src/test/org/apache/lucene/index/sorter/SorterTestBase.java	(.../trunk)	(revision 1645925)
+++ lucene/misc/src/test/org/apache/lucene/index/sorter/SorterTestBase.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
 import org.apache.lucene.document.Field.Store;
-import org.apache.lucene.document.Field;
 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;
 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.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) {
Index: lucene/misc/src/java/org/apache/lucene/uninverting/DocTermOrds.java
===================================================================
--- lucene/misc/src/java/org/apache/lucene/uninverting/DocTermOrds.java	(.../trunk)	(revision 1645925)
+++ lucene/misc/src/java/org/apache/lucene/uninverting/DocTermOrds.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
     }
 
Index: lucene/misc/src/java/org/apache/lucene/uninverting/FieldCacheImpl.java
===================================================================
--- lucene/misc/src/java/org/apache/lucene/uninverting/FieldCacheImpl.java	(.../trunk)	(revision 1645925)
+++ lucene/misc/src/java/org/apache/lucene/uninverting/FieldCacheImpl.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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.
Index: lucene/misc/src/java/org/apache/lucene/index/sorter/Sorter.java
===================================================================
--- lucene/misc/src/java/org/apache/lucene/index/sorter/Sorter.java	(.../trunk)	(revision 1645925)
+++ lucene/misc/src/java/org/apache/lucene/index/sorter/Sorter.java	(.../branches/lucene2878)	(revision 1645925)
@@ -20,8 +20,9 @@
 import java.io.IOException;
 import java.util.Comparator;
 
+import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.LeafReader;
-import org.apache.lucene.index.IndexReader;
+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();
   
 }
Index: lucene/misc/src/java/org/apache/lucene/index/sorter/SortingLeafReader.java
===================================================================
--- lucene/misc/src/java/org/apache/lucene/index/sorter/SortingLeafReader.java	(.../trunk)	(revision 1645925)
+++ lucene/misc/src/java/org/apache/lucene/index/sorter/SortingLeafReader.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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,31 +486,31 @@
       // 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
@@ -514,12 +517,12 @@
      * 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,7 +635,7 @@
     }
 
     // for testing
-    boolean reused(DocsAndPositionsEnum other) {
+    boolean reused(DocsEnum other) {
       if (other == null || !(other instanceof SortingDocsAndPositionsEnum)) {
         return false;
       }
@@ -639,7 +642,7 @@
       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,7 +668,7 @@
         }
       }
     }
-    
+
     @Override
     public int advance(final int target) throws IOException {
       // need to support it for checkIndex, but in practice it won't be called, so
@@ -672,27 +675,27 @@
       // 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));
   }
-  
+
 }
Index: lucene/codecs/src/java/org/apache/lucene/codecs/bloom/BloomFilteringPostingsFormat.java
===================================================================
--- lucene/codecs/src/java/org/apache/lucene/codecs/bloom/BloomFilteringPostingsFormat.java	(.../trunk)	(revision 1645925)
+++ lucene/codecs/src/java/org/apache/lucene/codecs/bloom/BloomFilteringPostingsFormat.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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
Index: lucene/codecs/src/java/org/apache/lucene/codecs/memory/FSTOrdTermsReader.java
===================================================================
--- lucene/codecs/src/java/org/apache/lucene/codecs/memory/FSTOrdTermsReader.java	(.../trunk)	(revision 1645925)
+++ lucene/codecs/src/java/org/apache/lucene/codecs/memory/FSTOrdTermsReader.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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,16 +35,16 @@
 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;
@@ -57,11 +51,16 @@
 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()
Index: lucene/codecs/src/java/org/apache/lucene/codecs/memory/MemoryPostingsFormat.java
===================================================================
--- lucene/codecs/src/java/org/apache/lucene/codecs/memory/MemoryPostingsFormat.java	(.../trunk)	(revision 1645925)
+++ lucene/codecs/src/java/org/apache/lucene/codecs/memory/MemoryPostingsFormat.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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) {
Index: lucene/codecs/src/java/org/apache/lucene/codecs/memory/DirectPostingsFormat.java
===================================================================
--- lucene/codecs/src/java/org/apache/lucene/codecs/memory/DirectPostingsFormat.java	(.../trunk)	(revision 1645925)
+++ lucene/codecs/src/java/org/apache/lucene/codecs/memory/DirectPostingsFormat.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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<Accountable> getChildResources() {
       return Accountables.namedAccountables("field", fields);
@@ -205,9 +204,10 @@
       @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);
       }
+
     }
 
     // TODO: maybe specialize into prx/no-prx/no-frq cases?
@@ -231,31 +231,32 @@
 
       @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;
       }
+
     }
 
     private final byte[] termBytes;
@@ -312,7 +313,7 @@
       }
       terms = new TermAndSkip[numTerms];
       termOffsets = new int[1+numTerms];
-      
+
       byte[] termBytes = new byte[1024];
 
       this.minSkipCount = minSkipCount;
@@ -324,7 +325,7 @@
 
       BytesRef term;
       DocsEnum docsEnum = null;
-      DocsAndPositionsEnum docsAndPositionsEnum = null;
+      DocsEnum docsAndPositionsEnum = null;
       final TermsEnum termsEnum = termsIn.iterator(null);
       int termOffset = 0;
 
@@ -411,7 +412,7 @@
           }
 
           final int[] postings = scratch.get();
-        
+
           ent = new LowFreqTerm(postings, payloads, docFreq, (int) totalTermFreq);
         } else {
           final int[] docs = new int[docFreq];
@@ -523,7 +524,7 @@
       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) {
@@ -530,7 +531,7 @@
           sizeInBytes += (termAndSkip!=null) ? termAndSkip.ramBytesUsed() : 0;
         }
       }
-      
+
       return sizeInBytes;
     }
 
@@ -546,7 +547,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 +555,7 @@
           return diff;
         }
       }
-    
+
       // One is a prefix of the other, or, they are equal:
       return termLen - other.length;
     }
@@ -706,7 +707,7 @@
     public boolean hasPositions() {
       return hasPos;
     }
-    
+
     @Override
     public boolean hasPayloads() {
       return hasPayloads;
@@ -854,10 +855,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 +931,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 +1206,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 +1339,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 +1459,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 +1490,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 +1578,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 +1677,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 +1755,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 +1792,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 +1836,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 +1847,7 @@
     private int docID;
     private int freq;
     private int skipPositions;
+    private int pos;
     private int startOffset;
     private int endOffset;
     private int lastPayloadOffset;
@@ -1772,10 +1872,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 +1887,7 @@
 
     @Override
     public int nextDoc() {
+      pos = -1;
       if (hasPayloads) {
         for(int i=0;i<skipPositions;i++) {
           upto++;
@@ -1842,9 +1944,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 +1961,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 +1996,7 @@
         return null;
       }
     }
-    
+
     @Override
     public long cost() {
       // TODO: could do a better estimate
@@ -1956,6 +2069,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 +2205,7 @@
         return docID = docIDs[upto];
       }
     }
-    
+
     @Override
     public long cost() {
       return docIDs.length;
@@ -2070,7 +2213,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 +2248,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 +2262,7 @@
       upto++;
       if (liveDocs == null) {
         if (upto < docIDs.length) {
-          posUpto = -posJump;   
+          posUpto = -posJump;
           curPositions = positions[upto];
           return docID = docIDs[upto];
         }
@@ -2150,10 +2293,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 +2455,7 @@
         return payload;
       }
     }
-    
+
     @Override
     public long cost() {
       return docIDs.length;
Index: lucene/codecs/src/java/org/apache/lucene/codecs/memory/MemoryDocValuesProducer.java
===================================================================
--- lucene/codecs/src/java/org/apache/lucene/codecs/memory/MemoryDocValuesProducer.java	(.../trunk)	(revision 1645925)
+++ lucene/codecs/src/java/org/apache/lucene/codecs/memory/MemoryDocValuesProducer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
     }
   }
Index: lucene/codecs/src/java/org/apache/lucene/codecs/memory/FSTTermsReader.java
===================================================================
--- lucene/codecs/src/java/org/apache/lucene/codecs/memory/FSTTermsReader.java	(.../trunk)	(revision 1645925)
+++ lucene/codecs/src/java/org/apache/lucene/codecs/memory/FSTTermsReader.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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,26 +34,30 @@
 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
Index: lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextFieldsReader.java
===================================================================
--- lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextFieldsReader.java	(.../trunk)	(revision 1645925)
+++ lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextFieldsReader.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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,7 +361,7 @@
       // Naive -- better to index skip data
       return slowAdvance(target);
     }
-    
+
     @Override
     public long cost() {
       return cost;
@@ -334,7 +368,7 @@
     }
   }
 
-  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();
Index: lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextTermVectorsReader.java
===================================================================
--- lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextTermVectorsReader.java	(.../trunk)	(revision 1645925)
+++ lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextTermVectorsReader.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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,13 +262,13 @@
       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;
@@ -276,7 +275,7 @@
       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,11 +542,13 @@
 
     @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;
       }
@@ -523,6 +555,16 @@
     }
 
     @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();
Index: lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextFieldsWriter.java
===================================================================
--- lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextFieldsWriter.java	(.../trunk)	(revision 1645925)
+++ lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextFieldsWriter.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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,7 +151,7 @@
 
               // 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));
@@ -161,8 +158,8 @@
                 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;
Index: lucene/codecs/src/java/org/apache/lucene/codecs/blocktreeords/OrdsIntersectTermsEnum.java
===================================================================
--- lucene/codecs/src/java/org/apache/lucene/codecs/blocktreeords/OrdsIntersectTermsEnum.java	(.../trunk)	(revision 1645925)
+++ lucene/codecs/src/java/org/apache/lucene/codecs/blocktreeords/OrdsIntersectTermsEnum.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
Index: lucene/codecs/src/java/org/apache/lucene/codecs/blocktreeords/OrdsSegmentTermsEnum.java
===================================================================
--- lucene/codecs/src/java/org/apache/lucene/codecs/blocktreeords/OrdsSegmentTermsEnum.java	(.../trunk)	(revision 1645925)
+++ lucene/codecs/src/java/org/apache/lucene/codecs/blocktreeords/OrdsSegmentTermsEnum.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
Index: lucene/codecs/src/java/org/apache/lucene/codecs/blockterms/BlockTermsReader.java
===================================================================
--- lucene/codecs/src/java/org/apache/lucene/codecs/blockterms/BlockTermsReader.java	(.../trunk)	(revision 1645925)
+++ lucene/codecs/src/java/org/apache/lucene/codecs/blockterms/BlockTermsReader.java	(.../branches/lucene2878)	(revision 1645925)
@@ -29,8 +29,8 @@
 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;
 import org.apache.lucene.index.IndexOptions;
@@ -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;
Index: lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/AssertingPostingsFormat.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/AssertingPostingsFormat.java	(.../trunk)	(revision 1645925)
+++ lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/AssertingPostingsFormat.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
Index: lucene/test-framework/src/java/org/apache/lucene/codecs/ramonly/RAMOnlyPostingsFormat.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/codecs/ramonly/RAMOnlyPostingsFormat.java	(.../trunk)	(revision 1645925)
+++ lucene/test-framework/src/java/org/apache/lucene/codecs/ramonly/RAMOnlyPostingsFormat.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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,15 +283,8 @@
             break;
           }
           RAMPostingsWriterImpl postingsWriter = termsConsumer.startTerm(term);
+          docsEnum = termsEnum.docs(null, docsEnum, enumFlags);
 
-          if (writePositions) {
-            posEnum = termsEnum.docsAndPositions(null, posEnum, enumFlags);
-            docsEnum = posEnum;
-          } else {
-            docsEnum = termsEnum.docs(null, docsEnum, enumFlags);
-            posEnum = null;
-          }
-
           int docFreq = 0;
           long totalTermFreq = 0;
           while (true) {
@@ -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);
+    public int nextPosition() {
+      if (posUpto >= current.positions.length)
+        return NO_MORE_POSITIONS;
+      return current.positions[posUpto++];
     }
 
-    // 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;
-        }
-      }
+    public int startPosition() throws IOException {
+      return current.positions[posUpto-1];
     }
 
     @Override
-    public int freq() throws IOException {
-      return current.positions.length;
+    public int endPosition() throws IOException {
+      return current.positions[posUpto-1];
     }
 
     @Override
-    public int docID() {
-      return current.docID;
-    }
-
-    @Override
-    public int nextPosition() {
-      return current.positions[posUpto++];
-    }
-
-    @Override
     public int startOffset() {
       return -1;
     }
Index: lucene/test-framework/src/java/org/apache/lucene/search/QueryUtils.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/search/QueryUtils.java	(.../trunk)	(revision 1645925)
+++ lucene/test-framework/src/java/org/apache/lucene/search/QueryUtils.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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.DirectoryReader;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.DirectoryReader;
 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);
Index: lucene/test-framework/src/java/org/apache/lucene/search/AssertingWeight.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/search/AssertingWeight.java	(.../trunk)	(revision 1645925)
+++ lucene/test-framework/src/java/org/apache/lucene/search/AssertingWeight.java	(.../branches/lucene2878)	(revision 1645925)
@@ -17,12 +17,12 @@
  * limitations under the License.
  */
 
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.util.Bits;
+
 import java.io.IOException;
 import java.util.Random;
 
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.util.Bits;
-
 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()) {
Index: lucene/codecs
===================================================================
--- lucene/codecs	(.../trunk)	(revision 1645925)
+++ lucene/codecs	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/codecs
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/codecs:r1638800-1645916
Index: lucene/misc
===================================================================
--- lucene/misc	(.../trunk)	(revision 1645925)
+++ lucene/misc	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/misc
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/misc:r1638800-1645916
Index: lucene/grouping
===================================================================
--- lucene/grouping	(.../trunk)	(revision 1645925)
+++ lucene/grouping	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/grouping
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/grouping:r1638800-1642714
Index: lucene/test-framework/src/java/org/apache/lucene/search/AssertingScorer.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/search/AssertingScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/test-framework/src/java/org/apache/lucene/search/AssertingScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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,6 +112,42 @@
   }
 
   @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();
   }
@@ -117,12 +154,16 @@
 
   @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
Index: lucene/test-framework/src/java/org/apache/lucene/search/AssertingIndexSearcher.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/search/AssertingIndexSearcher.java	(.../trunk)	(revision 1645925)
+++ lucene/test-framework/src/java/org/apache/lucene/search/AssertingIndexSearcher.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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.");
       }
Index: lucene/test-framework/src/java/org/apache/lucene/index/BaseTermVectorsFormatTestCase.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/index/BaseTermVectorsFormatTestCase.java	(.../trunk)	(revision 1645925)
+++ lucene/test-framework/src/java/org/apache/lucene/index/BaseTermVectorsFormatTestCase.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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) {
Index: lucene/test-framework/src/java/org/apache/lucene/index/BaseDocValuesFormatTestCase.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/index/BaseDocValuesFormatTestCase.java	(.../trunk)	(revision 1645925)
+++ lucene/test-framework/src/java/org/apache/lucene/index/BaseDocValuesFormatTestCase.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
Index: lucene/test-framework/src/java/org/apache/lucene/index/BasePostingsFormatTestCase.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/index/BasePostingsFormatTestCase.java	(.../trunk)	(revision 1645925)
+++ lucene/test-framework/src/java/org/apache/lucene/index/BasePostingsFormatTestCase.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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,8 +272,18 @@
       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,7 +662,7 @@
     }
 
     @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");
       }
@@ -657,10 +669,10 @@
       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,7 +823,6 @@
     DocsEnum prevDocsEnum = null;
 
     DocsEnum docsEnum;
-    DocsAndPositionsEnum docsAndPositionsEnum;
 
     if (!doCheckPositions) {
       if (allowPositions && random().nextInt(10) == 7) {
@@ -819,24 +829,23 @@
         // 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,10 +1582,8 @@
 
                       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;
@@ -1585,12 +1590,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();
                         }
                       }
 
@@ -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();
                           }
                         }
 
Index: lucene/test-framework/src/java/org/apache/lucene/index/AssertingLeafReader.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/index/AssertingLeafReader.java	(.../trunk)	(revision 1645925)
+++ lucene/test-framework/src/java/org/apache/lucene/index/AssertingLeafReader.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
Index: lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java	(.../trunk)	(revision 1645925)
+++ lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
 import org.apache.lucene.document.Field.Store;
-import org.apache.lucene.document.Field;
 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;
 import org.apache.lucene.store.IOContext.Context;
-import org.apache.lucene.store.IOContext;
 import org.apache.lucene.store.LockFactory;
 import org.apache.lucene.store.MergeInfo;
+import org.apache.lucene.store.MockDirectoryWrapper;
 import org.apache.lucene.store.MockDirectoryWrapper.Throttling;
-import org.apache.lucene.store.MockDirectoryWrapper;
 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);
Index: lucene/test-framework/src/java/org/apache/lucene/util/TestUtil.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/util/TestUtil.java	(.../trunk)	(revision 1645925)
+++ lucene/test-framework/src/java/org/apache/lucene/util/TestUtil.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
 import org.apache.lucene.search.FilteredQuery.FilterStrategy;
-import org.apache.lucene.search.FilteredQuery;
 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;
         }
Index: lucene/suggest/src/java/org/apache/lucene/search/suggest/analyzing/BlendedInfixSuggester.java
===================================================================
--- lucene/suggest/src/java/org/apache/lucene/search/suggest/analyzing/BlendedInfixSuggester.java	(.../trunk)	(revision 1645925)
+++ lucene/suggest/src/java/org/apache/lucene/search/suggest/analyzing/BlendedInfixSuggester.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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
Index: lucene/core/src/test/org/apache/lucene/codecs/lucene50/TestBlockPostingsFormat3.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/codecs/lucene50/TestBlockPostingsFormat3.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/codecs/lucene50/TestBlockPostingsFormat3.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
Index: lucene/core/src/test/org/apache/lucene/codecs/perfield/TestPerFieldPostingsFormat2.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/codecs/perfield/TestPerFieldPostingsFormat2.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/codecs/perfield/TestPerFieldPostingsFormat2.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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
Index: lucene/core/src/test/org/apache/lucene/analysis/TestMockAnalyzer.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/analysis/TestMockAnalyzer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/analysis/TestMockAnalyzer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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());
Index: lucene/core/src/test/org/apache/lucene/analysis/TestCachingTokenFilter.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/analysis/TestCachingTokenFilter.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/analysis/TestCachingTokenFilter.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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"));
Index: lucene/core/src/test/org/apache/lucene/search/TestBooleanCoord.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestBooleanCoord.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/TestBooleanCoord.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
Index: lucene/core/src/test/org/apache/lucene/search/TestPhrasePrefixQuery.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestPhrasePrefixQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/TestPhrasePrefixQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -17,20 +17,20 @@
  * limitations under the License.
  */
 
+import java.io.IOException;
+import java.util.LinkedList;
+
+import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
-import org.apache.lucene.util.LuceneTestCase;
-import org.apache.lucene.document.Document;
+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.index.IndexReader;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.index.MultiFields;
+import org.apache.lucene.store.Directory;
 import org.apache.lucene.util.BytesRef;
-import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.LuceneTestCase;
 
-import java.io.IOException;
-import java.util.LinkedList;
-
 /**
  * This class tests PhrasePrefixQuery class.
  */
Index: lucene/core/src/test/org/apache/lucene/search/TestPositiveScoresOnlyCollector.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestPositiveScoresOnlyCollector.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/TestPositiveScoresOnlyCollector.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
Index: lucene/core/src/test/org/apache/lucene/search/TestMinShouldMatch2.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestMinShouldMatch2.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/TestMinShouldMatch2.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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.DirectoryReader;
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.LeafReader;
-import org.apache.lucene.index.DirectoryReader;
 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;
     }
Index: lucene/core/src/test/org/apache/lucene/search/TestPhraseQuery.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestPhraseQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/TestPhraseQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
Index: lucene/core/src/test/org/apache/lucene/search/TestScoreCachingWrappingScorer.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestScoreCachingWrappingScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/TestScoreCachingWrappingScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
Index: lucene/core/src/test/org/apache/lucene/search/TestSimpleExplanations.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestSimpleExplanations.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/TestSimpleExplanations.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
Index: lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
     }
     
Index: lucene/core/src/test/org/apache/lucene/search/TestTermScorer.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestTermScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/TestTermScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
Index: lucene/suggest
===================================================================
--- lucene/suggest	(.../trunk)	(revision 1645925)
+++ lucene/suggest	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/suggest
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/suggest:r1638800-1645916
Index: lucene/test-framework
===================================================================
--- lucene/test-framework	(.../trunk)	(revision 1645925)
+++ lucene/test-framework	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/test-framework
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/test-framework:r1638800-1645916
Index: lucene/core/src/test/org/apache/lucene/search/TestConstantScoreQuery.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestConstantScoreQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/TestConstantScoreQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
Index: lucene/core/src/test/org/apache/lucene/search/posfilter/TestNestedPositionFilterQueries.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/posfilter/TestNestedPositionFilterQueries.java	(.../trunk)	(revision 0)
+++ lucene/core/src/test/org/apache/lucene/search/posfilter/TestNestedPositionFilterQueries.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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[][]{});
+  }
+
+}

Property changes on: lucene/core/src/test/org/apache/lucene/search/posfilter/TestNestedPositionFilterQueries.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/core/src/test/org/apache/lucene/search/posfilter/TestPhraseQueryPositions.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/posfilter/TestPhraseQueryPositions.java	(.../trunk)	(revision 0)
+++ lucene/core/src/test/org/apache/lucene/search/posfilter/TestPhraseQueryPositions.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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[][]{});
+
+  }
+
+
+}

Property changes on: lucene/core/src/test/org/apache/lucene/search/posfilter/TestPhraseQueryPositions.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/core/src/test/org/apache/lucene/search/posfilter/TestRangeFilterQuery.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/posfilter/TestRangeFilterQuery.java	(.../trunk)	(revision 0)
+++ lucene/core/src/test/org/apache/lucene/search/posfilter/TestRangeFilterQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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 }
+    });
+  }
+}

Property changes on: lucene/core/src/test/org/apache/lucene/search/posfilter/TestRangeFilterQuery.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/core/src/test/org/apache/lucene/search/TestBooleanQueryVisitSubscorers.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestBooleanQueryVisitSubscorers.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/TestBooleanQueryVisitSubscorers.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
Index: lucene/core/src/test/org/apache/lucene/search/spans/TestSpans.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/spans/TestSpans.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/spans/TestSpans.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
       }
Index: lucene/core/src/test/org/apache/lucene/search/TestQueryRescorer.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestQueryRescorer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/TestQueryRescorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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.IndexReader;
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.IndexReader;
 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;
             }
Index: lucene/core/src/test/org/apache/lucene/search/TestBooleanOr.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestBooleanOr.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/TestBooleanOr.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
Index: lucene/core/src/test/org/apache/lucene/search/TestCachingCollector.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestCachingCollector.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/TestCachingCollector.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
Index: lucene/core/src/test/org/apache/lucene/index/TestLongPostings.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestLongPostings.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/index/TestLongPostings.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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) {
Index: lucene/core/src/test/org/apache/lucene/index/TestPostingsOffsets.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestPostingsOffsets.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/index/TestPostingsOffsets.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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) {
Index: lucene/core/src/test/org/apache/lucene/index/TestPayloadsOnVectors.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestPayloadsOnVectors.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/index/TestPayloadsOnVectors.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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());
Index: lucene/core/src/test/org/apache/lucene/index/TestTermVectorsReader.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestTermVectorsReader.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/index/TestTermVectorsReader.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
Index: lucene/core/src/test/org/apache/lucene/index/TestIndexableField.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestIndexableField.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/index/TestIndexableField.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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());
Index: lucene/core/src/test/org/apache/lucene/index/TestTermVectorsWriter.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestTermVectorsWriter.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/index/TestTermVectorsWriter.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
Index: lucene/core/src/java/org/apache/lucene/codecs/blocktree/SegmentTermsEnum.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/blocktree/SegmentTermsEnum.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/codecs/blocktree/SegmentTermsEnum.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
Index: lucene/core/src/java/org/apache/lucene/codecs/lucene50/Lucene50PostingsReader.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/lucene50/Lucene50PostingsReader.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/codecs/lucene50/Lucene50PostingsReader.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
     }
Index: lucene/core/src/java/org/apache/lucene/codecs/blocktree/IntersectTermsEnum.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/blocktree/IntersectTermsEnum.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/codecs/blocktree/IntersectTermsEnum.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
Index: lucene/core/src/java/org/apache/lucene/codecs/lucene50/Lucene50DocValuesProducer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/lucene50/Lucene50DocValuesProducer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/codecs/lucene50/Lucene50DocValuesProducer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
       }
     }
Index: lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingTermVectorsReader.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingTermVectorsReader.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingTermVectorsReader.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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) {
Index: lucene/core/src/java/org/apache/lucene/codecs/PushPostingsWriterBase.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/PushPostingsWriterBase.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/codecs/PushPostingsWriterBase.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
Index: lucene/core/src/java/org/apache/lucene/search/payloads/PayloadTermQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/payloads/PayloadTermQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/payloads/PayloadTermQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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) {
Index: lucene/core/src/java/org/apache/lucene/search/FilterScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/FilterScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/FilterScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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,7 +32,7 @@
  * 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) {
@@ -39,6 +39,11 @@
     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();
   }
Index: lucene/core/src/java/org/apache/lucene/analysis/tokenattributes/PayloadAttribute.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/analysis/tokenattributes/PayloadAttribute.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/analysis/tokenattributes/PayloadAttribute.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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 {
   /**
Index: lucene/core/src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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.IndexSearcher;
 import org.apache.lucene.search.Scorer;
-import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Weight;
 import org.apache.lucene.search.similarities.DefaultSimilarity;
 import org.apache.lucene.search.similarities.Similarity;
@@ -148,7 +149,7 @@
     }
 
     @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));
     }
@@ -155,7 +156,7 @@
     
     @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) {
Index: lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -99,7 +99,7 @@
     }
 
   }
-  
+
   static final class Bucket {
     int doc = -1;            // tells if bucket is valid
     double score;             // incremental score
Index: lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -17,17 +17,19 @@
  * limitations under the License.
  */
 
+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.IndexReader;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.ToStringUtils;
 
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Set;
-
 /**
  * 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,8 +152,7 @@
     }
 
     @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);
@@ -158,16 +159,20 @@
         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();
Index: lucene/core/src/java/org/apache/lucene/search/ReqExclScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/ReqExclScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/ReqExclScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
-  }
 }
Index: lucene/core/src/test/org/apache/lucene/search/TestBooleanQuery.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestBooleanQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/TestBooleanQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -17,14 +17,6 @@
  * 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;
@@ -31,6 +23,7 @@
 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);
Index: lucene/core/src/test/org/apache/lucene/search/posfilter/IntervalTestBase.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/posfilter/IntervalTestBase.java	(.../trunk)	(revision 0)
+++ lucene/core/src/test/org/apache/lucene/search/posfilter/IntervalTestBase.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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" : "");
+    }
+  }
+
+
+}

Property changes on: lucene/core/src/test/org/apache/lucene/search/posfilter/IntervalTestBase.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/core/src/test/org/apache/lucene/search/posfilter/TestPositionsAndOffsets.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/posfilter/TestPositionsAndOffsets.java	(.../trunk)	(revision 0)
+++ lucene/core/src/test/org/apache/lucene/search/posfilter/TestPositionsAndOffsets.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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

Property changes on: lucene/core/src/test/org/apache/lucene/search/posfilter/TestPositionsAndOffsets.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/core/src/test/org/apache/lucene/search/TestTermVectors.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestTermVectors.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/TestTermVectors.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
Index: lucene/core/src/test/org/apache/lucene/search/posfilter/TestPositionFilteredIntervals.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/posfilter/TestPositionFilteredIntervals.java	(.../trunk)	(revision 0)
+++ lucene/core/src/test/org/apache/lucene/search/posfilter/TestPositionFilteredIntervals.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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 }
+    });
+
+  }
+}

Property changes on: lucene/core/src/test/org/apache/lucene/search/posfilter/TestPositionFilteredIntervals.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/core/src/test/org/apache/lucene/search/posfilter/TestBrouwerianQuery.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/posfilter/TestBrouwerianQuery.java	(.../trunk)	(revision 0)
+++ lucene/core/src/test/org/apache/lucene/search/posfilter/TestBrouwerianQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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 }
+    });
+  }
+
+}

Property changes on: lucene/core/src/test/org/apache/lucene/search/posfilter/TestBrouwerianQuery.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/core/src/test/org/apache/lucene/search/posfilter/TestIntervalScoring.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/posfilter/TestIntervalScoring.java	(.../trunk)	(revision 0)
+++ lucene/core/src/test/org/apache/lucene/search/posfilter/TestIntervalScoring.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
+  }
+
+
+}

Property changes on: lucene/core/src/test/org/apache/lucene/search/posfilter/TestIntervalScoring.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/core/src/test/org/apache/lucene/search/TestPositionIncrement.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestPositionIncrement.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/TestPositionIncrement.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
Index: lucene/core/src/test/org/apache/lucene/search/TestSloppyPhraseQuery.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestSloppyPhraseQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/TestSloppyPhraseQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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()));
       }
       
Index: lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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
Index: lucene/core/src/test/org/apache/lucene/index/TestSegmentReader.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestSegmentReader.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/index/TestSegmentReader.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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"));
Index: lucene/core/src/test/org/apache/lucene/index/TestLazyProxSkipping.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestLazyProxSkipping.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/index/TestLazyProxSkipping.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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"));
Index: lucene/core/src/test/org/apache/lucene/index/TestDoc.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestDoc.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/index/TestDoc.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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());
Index: lucene/core/src/test/org/apache/lucene/index/TestMultiLevelSkipList.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestMultiLevelSkipList.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/index/TestMultiLevelSkipList.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
Index: lucene/core/src/test/org/apache/lucene/index/TestDirectoryReader.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestDirectoryReader.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/index/TestDirectoryReader.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
Index: lucene/core/src/test/org/apache/lucene/index/TestDuelingCodecs.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestDuelingCodecs.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/index/TestDuelingCodecs.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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
  */
Index: lucene/core/src/test/org/apache/lucene/index/TestDocumentWriter.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestDocumentWriter.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/index/TestDocumentWriter.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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());
Index: lucene/core/src/java/org/apache/lucene/codecs/TermVectorsWriter.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/TermVectorsWriter.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/codecs/TermVectorsWriter.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
           }
         }
Index: lucene/core/src/java/org/apache/lucene/codecs/PostingsReaderBase.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/PostingsReaderBase.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/codecs/PostingsReaderBase.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
 }
Index: lucene/core/src/java/org/apache/lucene/analysis/Token.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/analysis/Token.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/analysis/Token.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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>
Index: lucene/core/src/java/org/apache/lucene/search/TermQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/TermQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/TermQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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.IndexReaderContext;
 import org.apache.lucene.index.LeafReader;
-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.index.TermContext;
 import org.apache.lucene.index.TermState;
 import org.apache.lucene.index.TermsEnum;
+import org.apache.lucene.search.similarities.Similarity;
 import org.apache.lucene.search.similarities.Similarity.SimScorer;
-import org.apache.lucene.search.similarities.Similarity;
 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,8 +100,10 @@
         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;
     }
@@ -104,13 +110,14 @@
     
     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) {
@@ -117,8 +124,10 @@
           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);
@@ -125,18 +134,19 @@
           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;
@@ -143,9 +153,10 @@
     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;
@@ -152,34 +163,37 @@
     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();
   }
-
+  
 }
Index: lucene/core/src/java/org/apache/lucene/search/TotalHitCountCollector.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/TotalHitCountCollector.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/TotalHitCountCollector.java	(.../branches/lucene2878)	(revision 1645925)
@@ -17,6 +17,7 @@
  * limitations under the License.
  */
 
+import org.apache.lucene.index.DocsEnum;
 
 /**
  * Just counts the total number of hits.
Index: lucene/core/src/java/org/apache/lucene/search/package.html
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/package.html	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/package.html	(.../branches/lucene2878)	(revision 1645925)
@@ -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>
Index: lucene/core/src/java/org/apache/lucene/search/ExactPhraseScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/ExactPhraseScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/ExactPhraseScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -20,22 +20,26 @@
 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;
@@ -42,25 +46,25 @@
     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,6 +311,8 @@
         // petered out for this chunk
         chunkStart += CHUNK;
         chunkEnd += CHUNK;
+        if (exhausted)
+          return false;
         continue;
       }
 
@@ -226,18 +319,20 @@
       // 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,9 +342,12 @@
 
       chunkStart += CHUNK;
       chunkEnd += CHUNK;
+
+      if (any) {
+        posRemaining = positionsInChunk;
+        return true;
+      }
     }
-
-    return freq;
   }
 
   @Override
@@ -256,4 +354,5 @@
   public long cost() {
     return cost;
   }
+
 }
Index: lucene/core/src/java/org/apache/lucene/search/DisjunctionSumScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/DisjunctionSumScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/DisjunctionSumScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -19,6 +19,7 @@
 
 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. 
  */
@@ -25,7 +26,7 @@
 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]; 
   }
+
 }
Index: lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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.IndexReaderContext;
 import org.apache.lucene.index.LeafReader;
-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.index.TermState;
 import org.apache.lucene.index.Terms;
 import org.apache.lucene.index.TermsEnum;
+import org.apache.lucene.search.similarities.Similarity;
 import org.apache.lucene.search.similarities.Similarity.SimScorer;
-import org.apache.lucene.search.similarities.Similarity;
 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);
+    }
+
+  }
 }
Index: lucene/core/src/test/org/apache/lucene/search/posfilter/TestBasicIntervals.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/posfilter/TestBasicIntervals.java	(.../trunk)	(revision 0)
+++ lucene/core/src/test/org/apache/lucene/search/posfilter/TestBasicIntervals.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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[][]{});
+  }
+  
+}

Property changes on: lucene/core/src/test/org/apache/lucene/search/posfilter/TestBasicIntervals.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/core/src/test/org/apache/lucene/search/spans/TestNearSpansOrdered.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/spans/TestNearSpansOrdered.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/spans/TestNearSpansOrdered.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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.IndexReaderContext;
 import org.apache.lucene.index.RandomIndexWriter;
-import org.apache.lucene.index.IndexReaderContext;
 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.Scorer;
 import org.apache.lucene.search.Weight;
-import org.apache.lucene.search.Scorer;
 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));
   }
 
Index: lucene/core/src/test/org/apache/lucene/search/TestSearchWithThreads.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestSearchWithThreads.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/TestSearchWithThreads.java	(.../branches/lucene2878)	(revision 1645925)
@@ -20,6 +20,7 @@
 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;
@@ -26,10 +27,11 @@
 import org.apache.lucene.index.RandomIndexWriter;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.LuceneTestCase;
 import org.apache.lucene.util.LuceneTestCase.SuppressCodecs;
-import org.apache.lucene.util.LuceneTestCase;
 
 @SuppressCodecs({ "SimpleText", "Memory", "Direct" })
+@Seed("12017F5C55C9DD62")
 public class TestSearchWithThreads extends LuceneTestCase {
   int NUM_DOCS;
   final int NUM_SEARCH_THREADS = 5;
Index: lucene/core/src/test/org/apache/lucene/search/TestDisjunctionMaxQuery.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestDisjunctionMaxQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/search/TestDisjunctionMaxQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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.LeafReaderContext;
+import org.apache.lucene.index.RandomIndexWriter;
 import org.apache.lucene.index.SlowCompositeReaderWrapper;
-import org.apache.lucene.index.FieldInvertState;
-import org.apache.lucene.index.RandomIndexWriter;
 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"));
Index: lucene/core/src/test/org/apache/lucene/index/TestPayloads.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestPayloads.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/index/TestPayloads.java	(.../branches/lucene2878)	(revision 1645925)
@@ -17,18 +17,16 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-import java.io.StringReader;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.lucene.analysis.*;
+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.analysis.tokenattributes.CharTermAttribute;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.document.TextField;
@@ -35,11 +33,20 @@
 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.Bits;
 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;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 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());
Index: lucene/core/src/test/org/apache/lucene/index/TestDocsAndPositions.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestDocsAndPositions.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/index/TestDocsAndPositions.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
Index: lucene/core/src/test/org/apache/lucene/index/TestFilterLeafReader.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestFilterLeafReader.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/index/TestFilterLeafReader.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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 {
Index: lucene/core/src/test/org/apache/lucene/index/TestCodecs.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestCodecs.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/index/TestCodecs.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
     }
Index: lucene/core/src/test/org/apache/lucene/index/TestStressIndexing2.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestStressIndexing2.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/index/TestStressIndexing2.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
       
Index: lucene/core/src/test/org/apache/lucene/index/TestIndexWriter.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestIndexWriter.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/index/TestIndexWriter.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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());
Index: lucene/core/src/test/org/apache/lucene/TestSearchForDuplicates.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/TestSearchForDuplicates.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/test/org/apache/lucene/TestSearchForDuplicates.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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";
Index: lucene/core/src/java/org/apache/lucene/codecs/TermVectorsReader.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/TermVectorsReader.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/codecs/TermVectorsReader.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
   
   /** 
Index: lucene/core/src/java/org/apache/lucene/codecs/PostingsWriterBase.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/PostingsWriterBase.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/codecs/PostingsWriterBase.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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
Index: lucene/core/src/java/org/apache/lucene/analysis/tokenattributes/PositionIncrementAttribute.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/analysis/tokenattributes/PositionIncrementAttribute.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/analysis/tokenattributes/PositionIncrementAttribute.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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.
Index: lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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) {
Index: lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java	(.../branches/lucene2878)	(revision 1645925)
@@ -32,11 +32,13 @@
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
+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;
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.DirectoryReader; // javadocs
-import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.MultiFields;
-import org.apache.lucene.index.IndexReaderContext;
 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);
Index: lucene/core/src/java/org/apache/lucene/search/PhrasePositions.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/PhrasePositions.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/PhrasePositions.java	(.../branches/lucene2878)	(revision 1645925)
@@ -18,26 +18,28 @@
  */
 
 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;
   }
@@ -44,6 +46,7 @@
 
   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;
   }
 }
Index: lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -27,28 +27,35 @@
 
 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,
@@ -55,17 +62,18 @@
       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,70 +85,114 @@
       }
       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();
+          setSpanStart(pos, startoffset);
+          pq.add(top);
+          return startpos;
+        }
+        pq.add(top);
+        top = pq.pop();
         next = pq.top().position;
-        matchLength = end - pp.position;
-      } else {
-        int matchLength2 = end - pp.position;
-        if (matchLength2 < matchLength) {
+        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,9 +295,13 @@
     // 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,7 +417,7 @@
     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()][];
@@ -363,7 +424,7 @@
     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;
   }
 
+  /**
+   * 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;
+  }
+
   @Override
-  public int freq() {
-    return numMatches;
+  public int startOffset() throws IOException {
+    return startoffset;
   }
-  
-  float sloppyFreq() {
-    return sloppyFreq;
+
+  @Override
+  public int endOffset() throws IOException {
+    return spanOffsetEnd;
   }
-  
-//  private void printQueue(PrintStream ps, PhrasePositions ext, String title) {
+
+  @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 + ")"; }
+
 }
Index: lucene/core/src/java/org/apache/lucene/index/DocsAndPositionsEnum.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/DocsAndPositionsEnum.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/index/DocsAndPositionsEnum.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
-}
Index: lucene/core/src/java/org/apache/lucene/search/DisjunctionScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/DisjunctionScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/DisjunctionScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
+
 }
Index: lucene/core/src/java/org/apache/lucene/search/FakeScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/FakeScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/FakeScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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()");
   }
Index: lucene/core/src/java/org/apache/lucene/search/posfilter/Interval.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/posfilter/Interval.java	(.../trunk)	(revision 0)
+++ lucene/core/src/java/org/apache/lucene/search/posfilter/Interval.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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

Property changes on: lucene/core/src/java/org/apache/lucene/search/posfilter/Interval.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/core/src/java/org/apache/lucene/search/ReqOptSumScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/ReqOptSumScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/ReqOptSumScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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"));
Index: lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -17,15 +17,16 @@
  * 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.IndexReader;
 import org.apache.lucene.index.Term;
+import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.ToStringUtils;
-import org.apache.lucene.util.Bits;
 
-import java.util.Set;
-import java.io.IOException;
-
 /**
  * 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);
     }
 
Index: lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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,7 +116,7 @@
     }
     return sum * coord;
   }
-  
+
   @Override
   public int freq() {
     return docsAndFreqs.length;
@@ -116,6 +123,36 @@
   }
 
   @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();
   }
@@ -122,9 +159,9 @@
 
   @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();
Index: lucene/core/src/java/org/apache/lucene/search/PhraseQueue.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/PhraseQueue.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/PhraseQueue.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
+  }
 }
Index: lucene/core/src/java/org/apache/lucene/search/CachingCollector.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/CachingCollector.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/CachingCollector.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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 {
Index: lucene/core/src/java/org/apache/lucene/search/similarities/Similarity.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/similarities/Similarity.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/similarities/Similarity.java	(.../branches/lucene2878)	(revision 1645925)
@@ -17,10 +17,8 @@
  * limitations under the License.
  */
 
-import java.io.IOException;
-
+import org.apache.lucene.index.FieldInvertState;
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.FieldInvertState;
 import org.apache.lucene.search.BooleanQuery;
 import org.apache.lucene.search.CollectionStatistics;
 import org.apache.lucene.search.Explanation;
@@ -28,10 +26,12 @@
 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.
  * <p>
Index: lucene/core/src/java/org/apache/lucene/search/Collector.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/Collector.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/Collector.java	(.../branches/lucene2878)	(revision 1645925)
@@ -72,5 +72,5 @@
    *          next atomic reader context
    */
   LeafCollector getLeafCollector(LeafReaderContext context) throws IOException;
-
+  
 }
Index: lucene/core/src/java/org/apache/lucene/search/posfilter/RangeFilterQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/posfilter/RangeFilterQuery.java	(.../trunk)	(revision 0)
+++ lucene/core/src/java/org/apache/lucene/search/posfilter/RangeFilterQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
+    }
+  }
+
+}

Property changes on: lucene/core/src/java/org/apache/lucene/search/posfilter/RangeFilterQuery.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -17,18 +17,18 @@
  * limitations under the License.
  */
 
+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.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;
 
-
 /**
  * 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,18 +290,8 @@
     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,7 +531,7 @@
 
     @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.
@@ -558,7 +538,7 @@
         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,16 +568,15 @@
     @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
@@ -604,14 +583,14 @@
     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);
     }
   }
Index: lucene/core/src/java/org/apache/lucene/search/posfilter/NonOverlappingQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/posfilter/NonOverlappingQuery.java	(.../trunk)	(revision 0)
+++ lucene/core/src/java/org/apache/lucene/search/posfilter/NonOverlappingQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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

Property changes on: lucene/core/src/java/org/apache/lucene/search/posfilter/NonOverlappingQuery.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/core/src/java/org/apache/lucene/search/posfilter/WithinFilteredScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/posfilter/WithinFilteredScorer.java	(.../trunk)	(revision 0)
+++ lucene/core/src/java/org/apache/lucene/search/posfilter/WithinFilteredScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
+  }
+
+}

Property changes on: lucene/core/src/java/org/apache/lucene/search/posfilter/WithinFilteredScorer.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/core/src/java/org/apache/lucene/search/posfilter/UnorderedNearQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/posfilter/UnorderedNearQuery.java	(.../trunk)	(revision 0)
+++ lucene/core/src/java/org/apache/lucene/search/posfilter/UnorderedNearQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
+      }
+
+    }
+
+  }
+
+}
+

Property changes on: lucene/core/src/java/org/apache/lucene/search/posfilter/UnorderedNearQuery.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/core/src/java/org/apache/lucene/search/MultiCollector.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/MultiCollector.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/MultiCollector.java	(.../branches/lucene2878)	(revision 1645925)
@@ -101,7 +101,6 @@
     return new MultiLeafCollector(leafCollectors);
   }
 
-
   private static class MultiLeafCollector implements LeafCollector {
 
     private final LeafCollector[] collectors;
Index: lucene/core/src/java/org/apache/lucene/search/spans/TermSpans.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/spans/TermSpans.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/spans/TermSpans.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
   }
 
Index: lucene/core/src/java/org/apache/lucene/search/QueryRescorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/QueryRescorer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/QueryRescorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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) {
Index: lucene/core/src/java/org/apache/lucene/search/MinShouldMatchSumScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/MinShouldMatchSumScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/MinShouldMatchSumScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
Index: lucene/core/src/java/org/apache/lucene/search/FuzzyTermsEnum.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/FuzzyTermsEnum.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/FuzzyTermsEnum.java	(.../branches/lucene2878)	(revision 1645925)
@@ -22,8 +22,8 @@
 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;
 import org.apache.lucene.index.TermState;
@@ -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);
   }
   
Index: lucene/core/src/java/org/apache/lucene/index/MultiFields.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/MultiFields.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/index/MultiFields.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
Index: lucene/core/src/java/org/apache/lucene/index/MappingMultiDocsEnum.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/MappingMultiDocsEnum.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/index/MappingMultiDocsEnum.java	(.../branches/lucene2878)	(revision 1645925)
@@ -17,10 +17,11 @@
  * limitations under the License.
  */
 
+import java.io.IOException;
+
 import org.apache.lucene.index.MultiDocsEnum.EnumWithSlice;
+import org.apache.lucene.util.BytesRef;
 
-import java.io.IOException;
-
 /**
  * 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;
   }
Index: lucene/core/src/java/org/apache/lucene/search/posfilter/BlockPhraseScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/posfilter/BlockPhraseScorer.java	(.../trunk)	(revision 0)
+++ lucene/core/src/java/org/apache/lucene/search/posfilter/BlockPhraseScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
+  }
+}

Property changes on: lucene/core/src/java/org/apache/lucene/search/posfilter/BlockPhraseScorer.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/core/src/java/org/apache/lucene/search/posfilter/PositionFilteredScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/posfilter/PositionFilteredScorer.java	(.../trunk)	(revision 0)
+++ lucene/core/src/java/org/apache/lucene/search/posfilter/PositionFilteredScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
+  }
+
+}

Property changes on: lucene/core/src/java/org/apache/lucene/search/posfilter/PositionFilteredScorer.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/core/src/java/org/apache/lucene/search/Scorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/Scorer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/Scorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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 */
Index: lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java	(.../branches/lucene2878)	(revision 1645925)
@@ -17,11 +17,18 @@
  * limitations under the License.
  */
 
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.index.IndexReaderContext;
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.IndexReaderContext;
 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) {
Index: lucene/core/src/java/org/apache/lucene/search/PositionQueue.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/PositionQueue.java	(.../trunk)	(revision 0)
+++ lucene/core/src/java/org/apache/lucene/search/PositionQueue.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
+  }
+
+
+}

Property changes on: lucene/core/src/java/org/apache/lucene/search/PositionQueue.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/core/src/java/org/apache/lucene/search/TermScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/TermScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/TermScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -17,11 +17,12 @@
  * 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>.
  */
 final class TermScorer extends Scorer {
@@ -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;
+  }
+
+
 }
Index: lucene/core/src/java/org/apache/lucene/search/posfilter/PositionFilterQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/posfilter/PositionFilterQuery.java	(.../trunk)	(revision 0)
+++ lucene/core/src/java/org/apache/lucene/search/posfilter/PositionFilterQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
+
+  }
+}

Property changes on: lucene/core/src/java/org/apache/lucene/search/posfilter/PositionFilterQuery.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/core/src/java/org/apache/lucene/search/spans/SpanTermQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/spans/SpanTermQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/spans/SpanTermQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
Index: lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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; 
   }
+
 }
Index: lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
         }
Index: lucene/core/src/java/org/apache/lucene/index/TermsEnum.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/TermsEnum.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/index/TermsEnum.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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,7 +162,7 @@
    * @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.
@@ -169,13 +169,13 @@
    *  
    *  @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");
+    }
   };
 }
Index: lucene/core/src/java/org/apache/lucene/index/MultiDocsAndPositionsEnum.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/MultiDocsAndPositionsEnum.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/index/MultiDocsAndPositionsEnum.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
+
 /**
  * Exposes flex API, merged from flex API of sub-segments.
  *
@@ -28,13 +28,13 @@
  * @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;
 
@@ -41,7 +41,7 @@
   /** 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. */
Index: lucene/core/src/java/org/apache/lucene/index/CheckIndex.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/CheckIndex.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/index/CheckIndex.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
 
Index: lucene/core/src/java/org/apache/lucene/index/FreqProxFields.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/FreqProxFields.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/index/FreqProxFields.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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");
Index: lucene/core/src/java/org/apache/lucene/index/DocsEnum.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/DocsEnum.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/index/DocsEnum.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
+
 }
Index: lucene/core/src/java/org/apache/lucene/search/posfilter/OrderedNearQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/posfilter/OrderedNearQuery.java	(.../trunk)	(revision 0)
+++ lucene/core/src/java/org/apache/lucene/search/posfilter/OrderedNearQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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]);
+      }
+    }
+  }
+}

Property changes on: lucene/core/src/java/org/apache/lucene/search/posfilter/OrderedNearQuery.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/core/src/java/org/apache/lucene/search/BooleanTopLevelScorers.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/BooleanTopLevelScorers.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/BooleanTopLevelScorers.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
Index: lucene/core/src/java/org/apache/lucene/search/TimeLimitingCollector.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/TimeLimitingCollector.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/TimeLimitingCollector.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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 
Index: lucene/core/src/java/org/apache/lucene/search/spans/SpanScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/spans/SpanScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/spans/SpanScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -19,9 +19,10 @@
 
 import java.io.IOException;
 
+import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.Weight;
-import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.similarities.Similarity;
+import org.apache.lucene.util.BytesRef;
 
 /**
  * Public for extension only.
@@ -96,7 +97,37 @@
   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.
@@ -103,9 +134,10 @@
   public float sloppyFreq() throws IOException {
     return freq;
   }
-  
+
   @Override
   public long cost() {
     return spans.cost();
   }
+
 }
Index: lucene/core/src/java/org/apache/lucene/search/QueryWrapperFilter.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/QueryWrapperFilter.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/QueryWrapperFilter.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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
Index: lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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.PhraseQuery.TermDocsEnumFactory;
+import org.apache.lucene.search.similarities.Similarity;
 import org.apache.lucene.search.similarities.Similarity.SimScorer;
-import org.apache.lucene.search.similarities.Similarity;
 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,6 +624,7 @@
 
     _posList.sort();
     _freq = _posList.size();
+    posPending = _freq;
 
     return _doc;
   }
@@ -533,17 +631,30 @@
 
   @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;
   }
 
Index: lucene/core/src/java/org/apache/lucene/search/Weight.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/Weight.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/search/Weight.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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,7 +198,7 @@
    * 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>
@@ -205,7 +205,6 @@
    * <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; }
+
 }
Index: lucene/join/src/java/org/apache/lucene/search/join/FakeScorer.java
===================================================================
--- lucene/join/src/java/org/apache/lucene/search/join/FakeScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/join/src/java/org/apache/lucene/search/join/FakeScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
-  }
-}
Index: lucene/replicator
===================================================================
--- lucene/replicator	(.../trunk)	(revision 1645925)
+++ lucene/replicator	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/replicator
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/replicator:r1638800-1642144
Index: lucene/core/src/java/org/apache/lucene/index/MultiTermsEnum.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/MultiTermsEnum.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/index/MultiTermsEnum.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
Index: lucene/core/src/java/org/apache/lucene/index/TermContext.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/TermContext.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/index/TermContext.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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
Index: lucene/sandbox/src/java/org/apache/lucene/search/TermAutomatonScorer.java
===================================================================
--- lucene/sandbox/src/java/org/apache/lucene/search/TermAutomatonScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/sandbox/src/java/org/apache/lucene/search/TermAutomatonScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
   }
Index: lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/SingleDocsEnum.java
===================================================================
--- lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/SingleDocsEnum.java	(.../trunk)	(revision 1645925)
+++ lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/SingleDocsEnum.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
+  }
 }
Index: lucene/core/src/java/org/apache/lucene/index/MultiDocsEnum.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/MultiDocsEnum.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/index/MultiDocsEnum.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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,8 +91,38 @@
   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 {
     assert target > doc;
     while(true) {
Index: lucene/core/src/java/org/apache/lucene/index/FilterLeafReader.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/FilterLeafReader.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/index/FilterLeafReader.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
+    public int nextPosition() throws IOException {
+      return in.nextPosition();
     }
-  }
 
-  /** 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();
+    public int startPosition() throws IOException {
+      return in.startPosition();
     }
 
     @Override
-    public int docID() {
-      return in.docID();
+    public int endPosition() throws IOException {
+      return in.endPosition();
     }
 
     @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 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();
Index: lucene/core/src/java/org/apache/lucene/index/MappedMultiFields.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/MappedMultiFields.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/index/MappedMultiFields.java	(.../branches/lucene2878)	(revision 1645925)
@@ -17,10 +17,10 @@
  * limitations under the License.
  */
 
+import org.apache.lucene.util.Bits;
+
 import java.io.IOException;
 
-import org.apache.lucene.util.Bits;
-
 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");
       }
Index: lucene/core/src/java/org/apache/lucene/index/SortedSetDocValuesTermsEnum.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/SortedSetDocValuesTermsEnum.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/index/SortedSetDocValuesTermsEnum.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
   }
 
Index: lucene/core/src/java/org/apache/lucene/index/MappingMultiDocsAndPositionsEnum.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/MappingMultiDocsAndPositionsEnum.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/index/MappingMultiDocsAndPositionsEnum.java	(.../branches/lucene2878)	(revision 1645925)
@@ -17,11 +17,11 @@
  * limitations under the License.
  */
 
+import java.io.IOException;
+
+import org.apache.lucene.index.MultiDocsAndPositionsEnum.EnumWithSlice;
 import org.apache.lucene.util.BytesRef;
-import org.apache.lucene.index.MultiDocsAndPositionsEnum.EnumWithSlice;
 
-import java.io.IOException;
-
 /**
  * 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();
   }
Index: lucene/core/src/java/org/apache/lucene/index/FilteredTermsEnum.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/FilteredTermsEnum.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/index/FilteredTermsEnum.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
   }
   
Index: lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/IDVersionSegmentTermsEnum.java
===================================================================
--- lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/IDVersionSegmentTermsEnum.java	(.../trunk)	(revision 1645925)
+++ lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/IDVersionSegmentTermsEnum.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
Index: lucene/core/src/java/org/apache/lucene/index/SortedDocValuesTermsEnum.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/SortedDocValuesTermsEnum.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/index/SortedDocValuesTermsEnum.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
   }
 
Index: lucene/core/src/java/org/apache/lucene/index/LeafReader.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/LeafReader.java	(.../trunk)	(revision 1645925)
+++ lucene/core/src/java/org/apache/lucene/index/LeafReader.java	(.../branches/lucene2878)	(revision 1645925)
@@ -17,11 +17,10 @@
  * limitations under the License.
  */
 
+import org.apache.lucene.util.Bits;
+
 import java.io.IOException;
 
-import org.apache.lucene.index.IndexReader.ReaderClosedListener;
-import org.apache.lucene.util.Bits;
-
 /** {@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());
Index: lucene/sandbox/src/java/org/apache/lucene/search/TermAutomatonQuery.java
===================================================================
--- lucene/sandbox/src/java/org/apache/lucene/search/TermAutomatonQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/sandbox/src/java/org/apache/lucene/search/TermAutomatonQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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()];
Index: lucene/core
===================================================================
--- lucene/core	(.../trunk)	(revision 1645925)
+++ lucene/core	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/core
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/core:r1638800-1645916
Index: lucene/SYSTEM_REQUIREMENTS.txt
===================================================================
--- lucene/SYSTEM_REQUIREMENTS.txt	(.../trunk)	(revision 1645925)
+++ lucene/SYSTEM_REQUIREMENTS.txt	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/SYSTEM_REQUIREMENTS.txt
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/SYSTEM_REQUIREMENTS.txt:r1638800-1642144
Index: lucene/ivy-versions.properties
===================================================================
--- lucene/ivy-versions.properties	(.../trunk)	(revision 1645925)
+++ lucene/ivy-versions.properties	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/ivy-versions.properties
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/ivy-versions.properties:r1638800-1642144
Index: lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/SingleDocsAndPositionsEnum.java
===================================================================
--- lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/SingleDocsAndPositionsEnum.java	(.../trunk)	(revision 1645925)
+++ lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/SingleDocsAndPositionsEnum.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
   }
Index: lucene/highlighter/src/test/org/apache/lucene/search/highlight/custom/HighlightCustomQueryTest.java
===================================================================
--- lucene/highlighter/src/test/org/apache/lucene/search/highlight/custom/HighlightCustomQueryTest.java	(.../trunk)	(revision 1645925)
+++ lucene/highlighter/src/test/org/apache/lucene/search/highlight/custom/HighlightCustomQueryTest.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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
Index: lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java
===================================================================
--- lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
     }
Index: lucene/highlighter/src/java/org/apache/lucene/search/postingshighlight/MultiTermHighlighting.java
===================================================================
--- lucene/highlighter/src/java/org/apache/lucene/search/postingshighlight/MultiTermHighlighting.java	(.../trunk)	(revision 1645925)
+++ lucene/highlighter/src/java/org/apache/lucene/search/postingshighlight/MultiTermHighlighting.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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,8 +237,20 @@
         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
       }
Index: lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java
===================================================================
--- lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
       
Index: lucene/highlighter/src/java/org/apache/lucene/search/postingshighlight/PostingsHighlighter.java
===================================================================
--- lucene/highlighter/src/java/org/apache/lucene/search/postingshighlight/PostingsHighlighter.java	(.../trunk)	(revision 1645925)
+++ lucene/highlighter/src/java/org/apache/lucene/search/postingshighlight/PostingsHighlighter.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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,12 +724,22 @@
     }
   }
   
-  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; }
 
     @Override
Index: lucene/highlighter/src/java/org/apache/lucene/search/highlight/TokenStreamFromTermVector.java
===================================================================
--- lucene/highlighter/src/java/org/apache/lucene/search/highlight/TokenStreamFromTermVector.java	(.../trunk)	(revision 1645925)
+++ lucene/highlighter/src/java/org/apache/lucene/search/highlight/TokenStreamFromTermVector.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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)
Index: lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java
===================================================================
--- lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java	(.../trunk)	(revision 1645925)
+++ lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java	(.../branches/lucene2878)	(revision 1645925)
@@ -17,18 +17,33 @@
  * 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
  *  specified parent Sort.  Note that this cannot perform
Index: lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/IDVersionPostingsReader.java
===================================================================
--- lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/IDVersionPostingsReader.java	(.../trunk)	(revision 1645925)
+++ lucene/sandbox/src/java/org/apache/lucene/codecs/idversion/IDVersionPostingsReader.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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) {
Index: lucene/highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldTermStack.java
===================================================================
--- lucene/highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldTermStack.java	(.../trunk)	(revision 1645925)
+++ lucene/highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldTermStack.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
Index: lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java
===================================================================
--- lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java	(.../trunk)	(revision 1645925)
+++ lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
Index: lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java
===================================================================
--- lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
Index: lucene/join
===================================================================
--- lucene/join	(.../trunk)	(revision 1645925)
+++ lucene/join	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/join
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/join:r1638800-1645916
Index: lucene/highlighter
===================================================================
--- lucene/highlighter	(.../trunk)	(revision 1645925)
+++ lucene/highlighter	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/highlighter
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/highlighter:r1638800-1643324
Index: lucene/sandbox
===================================================================
--- lucene/sandbox	(.../trunk)	(revision 1645925)
+++ lucene/sandbox	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/sandbox
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/sandbox:r1638800-1645916
Index: lucene/queryparser
===================================================================
--- lucene/queryparser	(.../trunk)	(revision 1645925)
+++ lucene/queryparser	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/queryparser
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/queryparser:r1638800-1642714
Index: lucene/memory/src/java/org/apache/lucene/index/memory/MemoryIndex.java
===================================================================
--- lucene/memory/src/java/org/apache/lucene/index/memory/MemoryIndex.java	(.../trunk)	(revision 1645925)
+++ lucene/memory/src/java/org/apache/lucene/index/memory/MemoryIndex.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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,51 +994,6 @@
     }
     
     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;
@@ -1049,14 +1000,15 @@
       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;
       }
Index: lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java
===================================================================
--- lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -17,17 +17,24 @@
  * limitations under the License.
  */
 
+import java.io.IOException;
+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.IndexReader;
 import org.apache.lucene.index.Term;
-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.Weight;
 import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
 
-import java.io.IOException;
-import java.util.Set;
-import java.util.Map;
 
-
 /**
  * Returns a score for each document based on a ValueSource,
  * often some function of the value of a field.
@@ -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;
     }
+
   }
 
 
Index: lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TermFreqValueSource.java
===================================================================
--- lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TermFreqValueSource.java	(.../trunk)	(revision 1645925)
+++ lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TermFreqValueSource.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
             }
Index: lucene/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java
===================================================================
--- lucene/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -17,19 +17,25 @@
  * limitations under the License.
  */
 
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.lucene.index.IndexReader;
 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.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;
 
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Set;
-import java.util.Map;
-
 /**
  * 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();
-    }
   }
 
 
Index: lucene/memory/src/test/org/apache/lucene/index/memory/TestMemoryIndexAgainstRAMDir.java
===================================================================
--- lucene/memory/src/test/org/apache/lucene/index/memory/TestMemoryIndexAgainstRAMDir.java	(.../trunk)	(revision 1645925)
+++ lucene/memory/src/test/org/apache/lucene/index/memory/TestMemoryIndexAgainstRAMDir.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
 import org.apache.lucene.util.ByteBlockPool.Allocator;
-import org.apache.lucene.util.ByteBlockPool;
 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()));
Index: lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/QueryValueSource.java
===================================================================
--- lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/QueryValueSource.java	(.../trunk)	(revision 1645925)
+++ lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/QueryValueSource.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
Index: lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TFValueSource.java
===================================================================
--- lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TFValueSource.java	(.../trunk)	(revision 1645925)
+++ lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TFValueSource.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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;
             }
Index: lucene/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java
===================================================================
--- lucene/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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.IndexReader;
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.IndexReader;
 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.IndexSearcher;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.Weight;
-import org.apache.lucene.search.Scorer;
-import org.apache.lucene.search.IndexSearcher;
 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"));
     }
Index: lucene/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java
===================================================================
--- lucene/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -17,13 +17,14 @@
  * 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 org.apache.lucene.util.BytesRef;
 
-import java.io.IOException;
-
 /**
  * {@link Scorer} which returns the result of {@link FunctionValues#floatVal(int)} as
  * the score for a document.
@@ -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;
   }
Index: lucene/queries
===================================================================
--- lucene/queries	(.../trunk)	(revision 1645925)
+++ lucene/queries	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/queries
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/queries:r1638800-1643324
Index: lucene/memory
===================================================================
--- lucene/memory	(.../trunk)	(revision 1645925)
+++ lucene/memory	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/memory
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/memory:r1638800-1643324
Index: lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysScorer.java
===================================================================
--- lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysScorer.java	(.../trunk)	(revision 1645925)
+++ lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysScorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -21,15 +21,16 @@
 import java.util.Collection;
 import java.util.Collections;
 
+import org.apache.lucene.index.DocsEnum;
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.DocsEnum;
-import org.apache.lucene.search.LeafCollector;
+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()");
     }
Index: lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyFacetSumValueSource.java
===================================================================
--- lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyFacetSumValueSource.java	(.../trunk)	(revision 1645925)
+++ lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyFacetSumValueSource.java	(.../branches/lucene2878)	(revision 1645925)
@@ -18,13 +18,12 @@
  */
 
 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;
 import org.apache.lucene.facet.FacetsCollector.MatchingDocs;
-import org.apache.lucene.facet.FacetsCollector;
 import org.apache.lucene.facet.FacetsConfig;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.queries.function.FunctionValues;
@@ -31,8 +30,8 @@
 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);
Index: lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysQuery.java
===================================================================
--- lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysQuery.java	(.../trunk)	(revision 1645925)
+++ lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysQuery.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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.IndexReader;
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.IndexReader;
+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;
Index: lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionRescorer.java
===================================================================
--- lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionRescorer.java	(.../trunk)	(revision 1645925)
+++ lucene/expressions/src/java/org/apache/lucene/expressions/ExpressionRescorer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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);
Index: lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/TaxonomyIndexArrays.java
===================================================================
--- lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/TaxonomyIndexArrays.java	(.../trunk)	(revision 1645925)
+++ lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/TaxonomyIndexArrays.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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) {
Index: lucene/expressions
===================================================================
--- lucene/expressions	(.../trunk)	(revision 1645925)
+++ lucene/expressions	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/expressions
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/expressions:r1638800-1642144
Index: lucene/facet
===================================================================
--- lucene/facet	(.../trunk)	(revision 1645925)
+++ lucene/facet	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/facet
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/facet:r1638800-1645916
Index: lucene/analysis/common/src/test/org/apache/lucene/analysis/sinks/TestTeeSinkTokenFilter.java
===================================================================
--- lucene/analysis/common/src/test/org/apache/lucene/analysis/sinks/TestTeeSinkTokenFilter.java	(.../trunk)	(revision 1645925)
+++ lucene/analysis/common/src/test/org/apache/lucene/analysis/sinks/TestTeeSinkTokenFilter.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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();
Index: lucene/analysis/common/src/test/org/apache/lucene/analysis/standard/TestClassicAnalyzer.java
===================================================================
--- lucene/analysis/common/src/test/org/apache/lucene/analysis/standard/TestClassicAnalyzer.java	(.../trunk)	(revision 1645925)
+++ lucene/analysis/common/src/test/org/apache/lucene/analysis/standard/TestClassicAnalyzer.java	(.../branches/lucene2878)	(revision 1645925)
@@ -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"));
Index: lucene/analysis/common
===================================================================
--- lucene/analysis/common	(.../trunk)	(revision 1645925)
+++ lucene/analysis/common	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/analysis/common
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/analysis/common:r1638800-1642714
Index: lucene/analysis
===================================================================
--- lucene/analysis	(.../trunk)	(revision 1645925)
+++ lucene/analysis	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene/analysis
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene/analysis:r1638800-1645916
Index: lucene
===================================================================
--- lucene	(.../trunk)	(revision 1645925)
+++ lucene	(.../branches/lucene2878)	(revision 1645925)

Property changes on: lucene
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk/lucene:r1638800-1645916
Index: .
===================================================================
--- .	(.../trunk)	(revision 1645925)
+++ .	(.../branches/lucene2878)	(revision 1645925)

Property changes on: .
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /lucene/dev/trunk:r1638800-1645921
