// Path: src/main/java/org/apache/solr/request/BidirectionalFacetResponseBuilder.java
// public static abstract class BaseLocalTermEnv<T extends FieldType & FacetPayload, K extends FacetKey<K>> extends LocalEnv<T, K> {
// 
//   public BaseLocalTermEnv(int offset, int limit, int startTermIndex, int adjust, int targetIdx, int nTerms, Predicate<BytesRef> termFilter,
//       int mincount, int[] counts, CharsRefBuilder charsRef, boolean extend, SortedSetDocValues si,
//       SolrIndexSearcher searcher, List<Entry<LeafReader, Bits>> leaves, String fieldName, T ft, NamedList res) {
//     super(offset, limit, startTermIndex, adjust, targetIdx, nTerms, termFilter, mincount, counts,
//         charsRef, extend, si, searcher, leaves, fieldName, ft, res);
//   }
// 
//   protected final int incrementTermIndex(int lastKeyIndex) {
//     for (int i = lastKeyIndex + 1; i < endTermOrd; i++) {
//       if (acceptTerm(i)) {
//         return i;
//       }
//     }
//     return -1;
//   }
// 
//   protected final int decrementTermIndex(int lastKeyIndex) {
//     for (int i = lastKeyIndex - 1; i >= startTermOrd; i--) {
//       if (acceptTerm(i)) {
//         return i;
//       }
//     }
//     return -1;
//   }
// 
//   protected final int getTargetKeyIndex() {
//     return (targetIdx < 0 ? ~targetIdx : targetIdx);
//   }
// 
//   protected final int getTargetKeyIndexInit(boolean ascending) {
//     int index = getTargetKeyIndex();
//     if (index >= startTermOrd && index < endTermOrd && acceptTerm(index)) {
//       return index;
//     } else if (ascending) {
//       return incrementTermIndex(index);
//     } else {
//       return decrementTermIndex(index);
//     }
//   }
// }
// 
// Path: src/main/java/org/apache/solr/request/BidirectionalFacetResponseBuilder.java
// public static class BaseTermIndexKey<K extends BaseTermIndexKey<K>> implements FacetKey<K> {
// 
//   public final int index;
//     
//   public BaseTermIndexKey(int index) {
//     this.index = index;
//   }
//     
//   @Override
//   public int compareTo(BaseTermIndexKey o) {
//     return Integer.compare(index, o.index);
//   }
// 
//   @Override
//   public String toString() {
//     return BaseTermIndexKey.class.getSimpleName() + "(index=" + index + ')';
//   }
// 
// }
// 
// Path: src/main/java/org/apache/solr/request/BidirectionalFacetResponseBuilder.java
// public static interface LimitMinder<T extends FieldType & FacetPayload, K extends FacetKey<K>> {
//   K startKey();
//   K nextKey(K lastKey, Env<T, K> env);
//   void addEntry(Entry<String, Object> entry, Deque<Entry<String, Object>> entryBuilder);
//   boolean updateEntry(String term, String docId, SolrDocument doc, Deque<Entry<String, Object>> entryBuilder);
//   void removeTail(Deque<Entry<String, Object>> entryBuilder);
// }


import java.io.IOException;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Predicate;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.LeafReader;
import org.apache.lucene.index.PostingsEnum;
import org.apache.lucene.index.SortedSetDocValues;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.CharsRefBuilder;
import org.apache.lucene.util.UnicodeUtil;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.request.BidirectionalFacetResponseBuilder.BaseLocalTermEnv;
import org.apache.solr.request.BidirectionalFacetResponseBuilder.BaseTermIndexKey;
import org.apache.solr.request.BidirectionalFacetResponseBuilder.LimitMinder;
import org.apache.solr.response.DocsStreamer;
import org.apache.solr.schema.FieldType;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.search.DocList;
import org.apache.solr.search.DocSet;
import org.apache.solr.search.SolrIndexSearcher;

    static String brToString(BytesRef br) {
      if (br == null) {
        return "null";
      } else if (UnicodeUtil.BIG_TERM.bytesEquals(br)) {
        return "[UnicodeUtil.BIG_TERM]";
      } else {
        return br.utf8ToString();
      }
    }

    @Override
    public TermDocIndexKey decrementKey(TermDocIndexKey previousKey) {
      int termIndex = previousKey.index;
      BytesRef docId = previousKey.docId;
      do {
        while ((docId = decrementDocId(termIndex, docId)) != null) {
          int docIndex = acceptDoc(termIndex, docId);
          if (docIndex >= 0) {
            localDocIndex = docIndex;
            return termDocIndexKey = new TermDocIndexKey(termIndex, docId);
          }
        }
        docId = UnicodeUtil.BIG_TERM;
      } while ((termIndex = decrementTermIndex(termIndex)) >= 0);
      localDocIndex = -1;
      return termDocIndexKey = null;
    }

    @Override
// Path: src/main/java/org/apache/solr/request/BidirectionalFacetResponseBuilder.java
// public static abstract class BaseLocalTermEnv<T extends FieldType & FacetPayload, K extends FacetKey<K>> extends LocalEnv<T, K> {
// 
//   public BaseLocalTermEnv(int offset, int limit, int startTermIndex, int adjust, int targetIdx, int nTerms, Predicate<BytesRef> termFilter,
//       int mincount, int[] counts, CharsRefBuilder charsRef, boolean extend, SortedSetDocValues si,
//       SolrIndexSearcher searcher, List<Entry<LeafReader, Bits>> leaves, String fieldName, T ft, NamedList res) {
//     super(offset, limit, startTermIndex, adjust, targetIdx, nTerms, termFilter, mincount, counts,
//         charsRef, extend, si, searcher, leaves, fieldName, ft, res);
//   }
// 
//   protected final int incrementTermIndex(int lastKeyIndex) {
//     for (int i = lastKeyIndex + 1; i < endTermOrd; i++) {
//       if (acceptTerm(i)) {
//         return i;
//       }
//     }
//     return -1;
//   }
// 
//   protected final int decrementTermIndex(int lastKeyIndex) {
//     for (int i = lastKeyIndex - 1; i >= startTermOrd; i--) {
//       if (acceptTerm(i)) {
//         return i;
//       }
//     }
//     return -1;
//   }
// 
//   protected final int getTargetKeyIndex() {
//     return (targetIdx < 0 ? ~targetIdx : targetIdx);
//   }
// 
//   protected final int getTargetKeyIndexInit(boolean ascending) {
//     int index = getTargetKeyIndex();
//     if (index >= startTermOrd && index < endTermOrd && acceptTerm(index)) {
//       return index;
//     } else if (ascending) {
//       return incrementTermIndex(index);
//     } else {
//       return decrementTermIndex(index);
//     }
//   }
// }
// 
// Path: src/main/java/org/apache/solr/request/BidirectionalFacetResponseBuilder.java
// public static class BaseTermIndexKey<K extends BaseTermIndexKey<K>> implements FacetKey<K> {
// 
//   public final int index;
//     
//   public BaseTermIndexKey(int index) {
//     this.index = index;
//   }
//     
//   @Override
//   public int compareTo(BaseTermIndexKey o) {
//     return Integer.compare(index, o.index);
//   }
// 
//   @Override
//   public String toString() {
//     return BaseTermIndexKey.class.getSimpleName() + "(index=" + index + ')';
//   }
// 
// }
// 
// Path: src/main/java/org/apache/solr/request/BidirectionalFacetResponseBuilder.java
// public static interface LimitMinder<T extends FieldType & FacetPayload, K extends FacetKey<K>> {
//   K startKey();
//   K nextKey(K lastKey, Env<T, K> env);
//   void addEntry(Entry<String, Object> entry, Deque<Entry<String, Object>> entryBuilder);
//   boolean updateEntry(String term, String docId, SolrDocument doc, Deque<Entry<String, Object>> entryBuilder);
//   void removeTail(Deque<Entry<String, Object>> entryBuilder);
// }


// Path: src/main/java/org/apache/solr/request/DocBasedFacetResponseBuilder.java
import java.io.IOException;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Predicate;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.LeafReader;
import org.apache.lucene.index.PostingsEnum;
import org.apache.lucene.index.SortedSetDocValues;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.CharsRefBuilder;
import org.apache.lucene.util.UnicodeUtil;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.request.BidirectionalFacetResponseBuilder.BaseLocalTermEnv;
import org.apache.solr.request.BidirectionalFacetResponseBuilder.BaseTermIndexKey;
import org.apache.solr.request.BidirectionalFacetResponseBuilder.LimitMinder;
import org.apache.solr.response.DocsStreamer;
import org.apache.solr.schema.FieldType;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.search.DocList;
import org.apache.solr.search.DocSet;
import org.apache.solr.search.SolrIndexSearcher;


    static String brToString(BytesRef br) {
      if (br == null) {
        return "null";
      } else if (UnicodeUtil.BIG_TERM.bytesEquals(br)) {
        return "[UnicodeUtil.BIG_TERM]";
      } else {
        return br.utf8ToString();
      }
    }

    @Override
    public TermDocIndexKey decrementKey(TermDocIndexKey previousKey) {
      int termIndex = previousKey.index;
      BytesRef docId = previousKey.docId;
      do {
        while ((docId = decrementDocId(termIndex, docId)) != null) {
          int docIndex = acceptDoc(termIndex, docId);
          if (docIndex >= 0) {
            localDocIndex = docIndex;
            return termDocIndexKey = new TermDocIndexKey(termIndex, docId);
          }
        }
        docId = UnicodeUtil.BIG_TERM;
      } while ((termIndex = decrementTermIndex(termIndex)) >= 0);
      localDocIndex = -1;
      return termDocIndexKey = null;
    }

    @Override
    public void addEntry(LimitMinder<T, TermDocIndexKey> limitMinder, TermDocIndexKey facetKey, Deque<Map.Entry<String, Object>> entryBuilder) throws IOException {