public Explanation explain(IndexReader reader, int doc) throws IOException {
      ComplexExplanation result = new ComplexExplanation();
      result.setDescription("weight(" + getQuery() + " in " + doc + "), product of:");

      Explanation idfExpl = new Explanation(idf, "idf(docFreq=" + reader.docFreq(term) + ")");

      // explain query weight
      Explanation queryExpl = new Explanation();
      queryExpl.setDescription("queryWeight(" + getQuery() + "), product of:");

      Explanation boostExpl = new Explanation(getBoost(), "boost");
      if (getBoost() != 1.0f) queryExpl.addDetail(boostExpl);
      queryExpl.addDetail(idfExpl);

      Explanation queryNormExpl = new Explanation(queryNorm, "queryNorm");
      queryExpl.addDetail(queryNormExpl);

      queryExpl.setValue(boostExpl.getValue() * idfExpl.getValue() * queryNormExpl.getValue());

      result.addDetail(queryExpl);

      // explain field weight
      String field = term.field();
      ComplexExplanation fieldExpl = new ComplexExplanation();
      fieldExpl.setDescription("fieldWeight(" + term + " in " + doc + "), product of:");

      Explanation tfExpl = scorer(reader).explain(doc);
      fieldExpl.addDetail(tfExpl);
      fieldExpl.addDetail(idfExpl);

      Explanation fieldNormExpl = new Explanation();
      byte[] fieldNorms = reader.norms(field);
      float fieldNorm = fieldNorms != null ? Similarity.decodeNorm(fieldNorms[doc]) : 0.0f;
      fieldNormExpl.setValue(fieldNorm);
      fieldNormExpl.setDescription("fieldNorm(field=" + field + ", doc=" + doc + ")");
      fieldExpl.addDetail(fieldNormExpl);

      fieldExpl.setMatch(Boolean.valueOf(tfExpl.isMatch()));
      fieldExpl.setValue(tfExpl.getValue() * idfExpl.getValue() * fieldNormExpl.getValue());

      result.addDetail(fieldExpl);
      result.setMatch(fieldExpl.getMatch());

      // combine them
      result.setValue(queryExpl.getValue() * fieldExpl.getValue());

      if (queryExpl.getValue() == 1.0f) return fieldExpl;

      return result;
    }
  @Override
  public Explanation explain(AtomicReaderContext context, int doc) throws IOException {
    Scorer scorer = scorer(context, true, false, context.reader().getLiveDocs());
    if (scorer != null) {
      int newDoc = scorer.advance(doc);
      if (newDoc == doc) {
        float freq = scorer.freq();
        SloppySimScorer docScorer = similarity.sloppySimScorer(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");
  }
    @Override
    public Explanation explain(AtomicReaderContext context, int doc) throws IOException {
      final Scorer cs = scorer(context, true, false, context.reader().getLiveDocs());
      final boolean exists = (cs != null && cs.advance(doc) == doc);

      final ComplexExplanation result = new ComplexExplanation();
      if (exists) {
        result.setDescription(ImageFeatureScoreQuery.this.toString() + ", product of:");
        result.setValue(queryWeight);
        result.setMatch(Boolean.TRUE);
        result.addDetail(new Explanation(getBoost(), "boost"));
        result.addDetail(new Explanation(queryNorm, "queryNorm"));
      } else {
        result.setDescription(ImageFeatureScoreQuery.this.toString() + " doesn't match id " + doc);
        result.setValue(0);
        result.setMatch(Boolean.FALSE);
      }
      return result;
    }
    public Explanation explain(IndexReader reader, int doc) throws IOException {

      ConstantScorer cs = (ConstantScorer) scorer(reader);
      boolean exists = cs.docIdSetIterator.skipTo(doc) && (cs.docIdSetIterator.doc() == doc);

      ComplexExplanation result = new ComplexExplanation();

      if (exists) {
        result.setDescription("ConstantScoreQuery(" + filter + "), product of:");
        result.setValue(queryWeight);
        result.setMatch(Boolean.TRUE);
        result.addDetail(new Explanation(getBoost(), "boost"));
        result.addDetail(new Explanation(queryNorm, "queryNorm"));
      } else {
        result.setDescription("ConstantScoreQuery(" + filter + ") doesn't match id " + doc);
        result.setValue(0);
        result.setMatch(Boolean.FALSE);
      }
      return result;
    }
    @Override
    public Explanation explain(IndexReader reader, int doc) throws IOException {

      ConstantScorer cs = new ConstantScorer(similarity, reader, this);
      boolean exists = cs._innerIter.advance(doc) == doc;

      ComplexExplanation result = new ComplexExplanation();

      if (exists) {
        result.setDescription("ConstantScoreQuery(" + filter + "), product of:");
        result.setValue(queryWeight);
        result.setMatch(Boolean.TRUE);
        result.addDetail(new Explanation(getBoost(), "boost"));
        result.addDetail(new Explanation(queryNorm, "queryNorm"));
      } else {
        result.setDescription("ConstantScoreQuery(" + filter + ") doesn't match id " + doc);
        result.setValue(0);
        result.setMatch(Boolean.FALSE);
      }
      return result;
    }
 /** Explain the score we computed for doc */
 @Override
 public Explanation explain(LeafReaderContext context, int doc) throws IOException {
   if (disjuncts.size() == 1) return weights.get(0).explain(context, doc);
   ComplexExplanation result = new ComplexExplanation();
   float max = 0.0f, sum = 0.0f;
   result.setDescription(
       tieBreakerMultiplier == 0.0f
           ? "max of:"
           : "max plus " + tieBreakerMultiplier + " times others of:");
   for (Weight wt : weights) {
     Explanation e = wt.explain(context, doc);
     if (e.isMatch()) {
       result.setMatch(Boolean.TRUE);
       result.addDetail(e);
       sum += e.getValue();
       max = Math.max(max, e.getValue());
     }
   }
   result.setValue(max + (sum - max) * tieBreakerMultiplier);
   return result;
 }
    @Override
    public Explanation explain(AtomicReaderContext context, int doc) throws IOException {
      Scorer scorer = scorer(context, context.reader().getLiveDocs());
      if (scorer != null) {
        int newDoc = scorer.advance(doc);
        if (newDoc == doc) {
          float score = scorer.score();
          ComplexExplanation result = new ComplexExplanation();
          result.setDescription("ImageHashLimitQuery, product of:");
          result.setValue(score);
          if (getBoost() != 1.0f) {
            result.addDetail(new Explanation(getBoost(), "boost"));
            score = score / getBoost();
          }
          result.addDetail(new Explanation(score, "image score (1/distance)"));
          result.setMatch(true);
          return result;
        }
      }

      return new ComplexExplanation(false, 0.0f, "no matching term");
    }
    @Override
    public Explanation explain(AtomicReaderContext context, int doc) throws IOException {
      ComplexExplanation result = new ComplexExplanation();
      result.setDescription("weight(" + getQuery() + " in " + doc + "), product of:");

      Explanation idfExpl = new Explanation(idf, "idf(" + field + ":" + idfExp.explain() + ")");

      // explain query weight
      Explanation queryExpl = new Explanation();
      queryExpl.setDescription("queryWeight(" + getQuery() + "), product of:");

      Explanation boostExpl = new Explanation(getBoost(), "boost");
      if (getBoost() != 1.0f) queryExpl.addDetail(boostExpl);

      queryExpl.addDetail(idfExpl);

      Explanation queryNormExpl = new Explanation(queryNorm, "queryNorm");
      queryExpl.addDetail(queryNormExpl);

      queryExpl.setValue(boostExpl.getValue() * idfExpl.getValue() * queryNormExpl.getValue());

      result.addDetail(queryExpl);

      // explain field weight
      ComplexExplanation fieldExpl = new ComplexExplanation();
      fieldExpl.setDescription("fieldWeight(" + getQuery() + " in " + doc + "), product of:");

      Scorer scorer = scorer(context, ScorerContext.def());
      if (scorer == null) {
        return new Explanation(0.0f, "no matching docs");
      }

      Explanation tfExplanation = new Explanation();
      int d = scorer.advance(doc);
      float phraseFreq;
      if (d == doc) {
        phraseFreq = scorer.freq();
      } else {
        phraseFreq = 0.0f;
      }

      tfExplanation.setValue(similarity.tf(phraseFreq));
      tfExplanation.setDescription("tf(phraseFreq=" + phraseFreq + ")");
      fieldExpl.addDetail(tfExplanation);
      fieldExpl.addDetail(idfExpl);

      Explanation fieldNormExpl = new Explanation();
      byte[] fieldNorms = context.reader.norms(field);
      float fieldNorm = fieldNorms != null ? similarity.decodeNormValue(fieldNorms[doc]) : 1.0f;
      fieldNormExpl.setValue(fieldNorm);
      fieldNormExpl.setDescription("fieldNorm(field=" + field + ", doc=" + doc + ")");
      fieldExpl.addDetail(fieldNormExpl);

      fieldExpl.setMatch(Boolean.valueOf(tfExplanation.isMatch()));
      fieldExpl.setValue(tfExplanation.getValue() * idfExpl.getValue() * fieldNormExpl.getValue());

      result.addDetail(fieldExpl);
      result.setMatch(fieldExpl.getMatch());

      // combine them
      result.setValue(queryExpl.getValue() * fieldExpl.getValue());

      if (queryExpl.getValue() == 1.0f) return fieldExpl;

      return result;
    }