public UnionDocsAndPositionsEnum(IndexReader indexReader, Term[] terms) throws IOException {
    List<DocsAndPositionsEnum> docsEnums = new LinkedList<DocsAndPositionsEnum>();
    final Bits delDocs = indexReader.getDeletedDocs();
    for (int i = 0; i < terms.length; i++) {
      DocsAndPositionsEnum postings =
          indexReader.termPositionsEnum(delDocs, terms[i].field(), terms[i].bytes());
      if (postings != null) {
        docsEnums.add(postings);
      } else {
        if (indexReader.termDocsEnum(delDocs, terms[i].field(), terms[i].bytes()) != null) {
          // term does exist, but has no positions
          throw new IllegalStateException(
              "field \""
                  + terms[i].field()
                  + "\" was indexed with Field.omitTermFreqAndPositions=true; cannot run PhraseQuery (term="
                  + terms[i].text()
                  + ")");
        }
      }
    }

    _queue = new DocsQueue(docsEnums);
    _posList = new IntQueue();
  }
    @Override
    public Scorer scorer(AtomicReaderContext context, ScorerContext scorerContext)
        throws IOException {
      if (termArrays.size() == 0) // optimize zero-term case
      return null;
      final IndexReader reader = context.reader;
      final Bits delDocs = reader.getDeletedDocs();

      PhraseQuery.PostingsAndFreq[] postingsFreqs =
          new PhraseQuery.PostingsAndFreq[termArrays.size()];

      for (int pos = 0; pos < postingsFreqs.length; pos++) {
        Term[] terms = termArrays.get(pos);

        final DocsAndPositionsEnum postingsEnum;
        int docFreq;

        if (terms.length > 1) {
          postingsEnum = new UnionDocsAndPositionsEnum(reader, terms);

          // coarse -- this overcounts since a given doc can
          // have more than one terms:
          docFreq = 0;
          for (int termIdx = 0; termIdx < terms.length; termIdx++) {
            docFreq += reader.docFreq(terms[termIdx]);
          }
        } else {
          final Term term = terms[0];
          postingsEnum = reader.termPositionsEnum(delDocs, term.field(), term.bytes());

          if (postingsEnum == null) {
            if (reader.termDocsEnum(delDocs, term.field(), term.bytes()) != null) {
              // term does exist, but has no positions
              throw new IllegalStateException(
                  "field \""
                      + term.field()
                      + "\" was indexed with Field.omitTermFreqAndPositions=true; cannot run PhraseQuery (term="
                      + term.text()
                      + ")");
            } else {
              // term does not exist
              return null;
            }
          }

          docFreq = reader.docFreq(term.field(), term.bytes());
        }

        postingsFreqs[pos] =
            new PhraseQuery.PostingsAndFreq(postingsEnum, docFreq, positions.get(pos).intValue());
      }

      // sort by increasing docFreq order
      if (slop == 0) {
        ArrayUtil.quickSort(postingsFreqs);
      }

      if (slop == 0) {
        ExactPhraseScorer s =
            new ExactPhraseScorer(this, postingsFreqs, similarity, reader.norms(field));
        if (s.noDocs) {
          return null;
        } else {
          return s;
        }
      } else {
        return new SloppyPhraseScorer(this, postingsFreqs, similarity, slop, reader.norms(field));
      }
    }