@Override
  public TwoPhaseIterator twoPhaseIterator() {
    final float matchCost = matchCost(reqApproximation, reqTwoPhaseIterator, exclApproximation, exclTwoPhaseIterator);

    if (reqTwoPhaseIterator == null
        || (exclTwoPhaseIterator != null && reqTwoPhaseIterator.matchCost() <= exclTwoPhaseIterator.matchCost())) {
      // reqTwoPhaseIterator is LESS costly than exclTwoPhaseIterator, check it first
      return new TwoPhaseIterator(reqApproximation) {

        @Override
        public boolean matches() throws IOException {
          final int doc = reqApproximation.docID();
          // check if the doc is not excluded
          int exclDoc = exclApproximation.docID();
          if (exclDoc < doc) {
            exclDoc = exclApproximation.advance(doc);
          }
          if (exclDoc != doc) {
            return matchesOrNull(reqTwoPhaseIterator);
          }
          return matchesOrNull(reqTwoPhaseIterator) && !matchesOrNull(exclTwoPhaseIterator);
        }

        @Override
        public float matchCost() {
          return matchCost;
        }
      };
    } else {
      // reqTwoPhaseIterator is MORE costly than exclTwoPhaseIterator, check it first
      return new TwoPhaseIterator(reqApproximation) {

        @Override
        public boolean matches() throws IOException {
          final int doc = reqApproximation.docID();
          // check if the doc is not excluded
          int exclDoc = exclApproximation.docID();
          if (exclDoc < doc) {
            exclDoc = exclApproximation.advance(doc);
          }
          if (exclDoc != doc) {
            return matchesOrNull(reqTwoPhaseIterator);
          }
          return !matchesOrNull(exclTwoPhaseIterator) && matchesOrNull(reqTwoPhaseIterator);
        }

        @Override
        public float matchCost() {
          return matchCost;
        }
      };
    }
  }