You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by da...@apache.org on 2017/12/14 07:58:11 UTC

[4/5] lucene-solr:jira/solr-11702: merge master

merge master


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/e80554a8
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/e80554a8
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/e80554a8

Branch: refs/heads/jira/solr-11702
Commit: e80554a87a2f27b1edaf486a1fab457631bff295
Parents: 09135b2
Author: Cao Manh Dat <da...@apache.org>
Authored: Thu Dec 14 14:41:15 2017 +0700
Committer: Cao Manh Dat <da...@apache.org>
Committed: Thu Dec 14 14:41:15 2017 +0700

----------------------------------------------------------------------
 lucene/CHANGES.txt                              |  10 +
 .../index/DocumentsWriterFlushControl.java      |   2 +-
 .../search/similarities/AfterEffectB.java       |  12 +-
 .../search/similarities/AfterEffectL.java       |   4 +-
 .../lucene/search/similarities/Axiomatic.java   |  62 ++-
 .../search/similarities/AxiomaticF1EXP.java     |  36 ++
 .../search/similarities/AxiomaticF1LOG.java     |  34 ++
 .../search/similarities/AxiomaticF2EXP.java     |  36 ++
 .../search/similarities/AxiomaticF2LOG.java     |  35 ++
 .../search/similarities/AxiomaticF3EXP.java     |  31 ++
 .../search/similarities/AxiomaticF3LOG.java     |  30 ++
 .../lucene/search/similarities/BasicModel.java  |  16 +-
 .../lucene/search/similarities/BasicModelG.java |  22 +
 .../search/similarities/BasicModelIF.java       |  15 +
 .../search/similarities/BasicModelIn.java       |  10 +-
 .../search/similarities/BasicModelIne.java      |  21 +
 .../search/similarities/BooleanSimilarity.java  |   2 +-
 .../search/similarities/DFISimilarity.java      |  34 ++
 .../search/similarities/DFRSimilarity.java      |  17 +-
 .../search/similarities/IBSimilarity.java       |  19 +-
 .../similarities/LMDirichletSimilarity.java     |  30 +-
 .../similarities/LMJelinekMercerSimilarity.java |  23 +
 .../lucene/search/similarities/LambdaDF.java    |   9 +-
 .../lucene/search/similarities/LambdaTTF.java   |  12 +-
 .../search/similarities/Normalization.java      |  10 +-
 .../search/similarities/NormalizationH1.java    |  17 +
 .../search/similarities/NormalizationH2.java    |  17 +
 .../search/similarities/NormalizationH3.java    |  19 +
 .../search/similarities/NormalizationZ.java     |  16 +
 .../spatial/spatial4j/Geo3dCircleShape.java     |  15 -
 .../spatial4j/Geo3dDistanceCalculator.java      |  60 +--
 .../lucene/spatial/spatial4j/Geo3dShape.java    |   6 +-
 .../spatial/spatial4j/Geo3dShapeFactory.java    |  43 +-
 .../Geo3dShapeRectRelationTestCase.java         | 264 ------------
 .../Geo3dShapeSphereModelRectRelationTest.java  |   8 +-
 .../Geo3dShapeWGS84ModelRectRelationTest.java   |  10 +-
 .../spatial4j/ShapeRectRelationTestCase.java    | 201 +++++++++
 .../lucene/spatial3d/geom/GeoCircleFactory.java |  29 +-
 .../lucene/spatial3d/geom/GeoExactCircle.java   |  55 +--
 .../spatial3d/geom/GeoStandardCircle.java       |   4 +-
 .../lucene/spatial3d/geom/PlanetModel.java      |   7 +
 solr/CHANGES.txt                                |  16 +-
 solr/bin/solr                                   |   4 +-
 .../solr/handler/component/FacetComponent.java  |  27 +-
 .../apache/solr/cloud/TestCloudPivotFacet.java  |   9 -
 .../DistributedFacetPivotLongTailTest.java      | 115 ++---
 ...ibutedFacetSimpleRefinementLongTailTest.java | 428 +++++++++++++++++++
 solr/solr-ref-guide/src/collections-api.adoc    |  22 +-
 solr/solr-ref-guide/src/installing-solr.adoc    |  14 +-
 solr/solr-ref-guide/src/solr-upgrade-notes.adoc |  35 +-
 .../src/solrcloud-autoscaling-api.adoc          |  73 +++-
 .../apache/solr/common/params/FacetParams.java  |   6 +-
 .../UsingSolrJRefGuideExamplesTest.java         |  75 +++-
 53 files changed, 1531 insertions(+), 596 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index e629be0..c08e215 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -37,6 +37,9 @@ Changes in Runtime Behavior
   divide-by-zero hacks.  IndexSearcher.termStatistics/collectionStatistics return null
   instead of returning bogus values for a non-existent term or field. (Robert Muir)
 
+* LUCENE-7996: FunctionQuery and FunctionScoreQuery now return a score of 0
+  when the function produces a negative value. (Adrien Grand)
+
 Improvements
 
 * LUCENE-7997: Add BaseSimilarityTestCase to sanity check similarities.
@@ -44,6 +47,9 @@ Improvements
   Add missing range checks for similarity parameters.
   Improve BM25 and ClassicSimilarity's explanations. (Robert Muir)
 
+* LUCENE-8011: Improved similarity explanations.
+  (Mayya Sharipova via Adrien Grand)
+
 Optimizations
 
 * LUCENE-8040: Optimize IndexSearcher.collectionStatistics, avoiding MultiFields/MultiTerms
@@ -66,6 +72,10 @@ Improvements
   disk. This change adds an expert setting to opt ouf of this behavior unless
   flusing is falling behind. (Simon Willnauer)
 
+* LUCENE-8086: spatial-extras Geo3dFactory: Use GeoExactCircle with
+  configurable precision for non-spherical planet models.
+  (Ignacio Vera via David Smiley)
+
 ======================= Lucene 7.2.0 =======================
 
 API Changes

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/index/DocumentsWriterFlushControl.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/DocumentsWriterFlushControl.java b/lucene/core/src/java/org/apache/lucene/index/DocumentsWriterFlushControl.java
index 761db0e..bf55991 100644
--- a/lucene/core/src/java/org/apache/lucene/index/DocumentsWriterFlushControl.java
+++ b/lucene/core/src/java/org/apache/lucene/index/DocumentsWriterFlushControl.java
@@ -716,7 +716,7 @@ final class DocumentsWriterFlushControl implements Accountable {
     return infoStream;
   }
 
-  ThreadState findLargestNonPendingWriter() {
+  synchronized ThreadState findLargestNonPendingWriter() {
     ThreadState maxRamUsingThreadState = null;
     long maxRamSoFar = 0;
     Iterator<ThreadState> activePerThreadsIterator = allActiveThreadStates();

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffectB.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffectB.java b/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffectB.java
index 6678cd9..66ee1b9 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffectB.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffectB.java
@@ -39,10 +39,14 @@ public class AfterEffectB extends AfterEffect {
   public final Explanation explain(BasicStats stats, double tfn) {
     return Explanation.match(
         (float) (scoreTimes1pTfn(stats) / (1 + tfn)),
-        getClass().getSimpleName() + ", computed from: ",
-        Explanation.match((float) tfn, "tfn"),
-        Explanation.match(stats.getTotalTermFreq(), "totalTermFreq"),
-        Explanation.match(stats.getDocFreq(), "docFreq"));
+        getClass().getSimpleName()
+            + ", computed as (F + 1) / (n * (tfn + 1)) from:",
+        Explanation.match((float) tfn, "tfn, normalized term frequency"),
+        Explanation.match(stats.getTotalTermFreq(),
+            "F, total number of occurrences of term across all documents + 1"),
+        Explanation.match(stats.getDocFreq(),
+            "n, number of documents containing term + 1"),
+        Explanation.match((float) tfn, "tfn, normalized term frequency"));
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffectL.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffectL.java b/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffectL.java
index 60a1b1d..283c3ac 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffectL.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/AfterEffectL.java
@@ -37,8 +37,8 @@ public class AfterEffectL extends AfterEffect {
   public final Explanation explain(BasicStats stats, double tfn) {
     return Explanation.match(
         (float) (scoreTimes1pTfn(stats) / (1 + tfn)),
-        getClass().getSimpleName() + ", computed from: ",
-        Explanation.match((float) tfn, "tfn"));
+        getClass().getSimpleName() + ", computed as 1 / (tfn + 1) from:",
+        Explanation.match((float) tfn, "tfn, normalized term frequency"));
   }
   
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/Axiomatic.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/Axiomatic.java b/lucene/core/src/java/org/apache/lucene/search/similarities/Axiomatic.java
index 2a7f353..403773e 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/Axiomatic.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/Axiomatic.java
@@ -118,16 +118,19 @@ public abstract class Axiomatic extends SimilarityBase {
   protected void explain(List<Explanation> subs, BasicStats stats, int doc,
                          double freq, double docLen) {
     if (stats.getBoost() != 1.0d) {
-      subs.add(Explanation.match((float) stats.getBoost(), "boost"));
+      subs.add(Explanation.match((float) stats.getBoost(),
+          "boost, query boost"));
     }
 
-    subs.add(Explanation.match(this.k, "k"));
-    subs.add(Explanation.match(this.s, "s"));
-    subs.add(Explanation.match(this.queryLen, "queryLen"));
-    subs.add(Explanation.match((float) tf(stats, freq, docLen), "tf"));
-    subs.add(Explanation.match((float) ln(stats, freq, docLen), "ln"));
-    subs.add(Explanation.match((float) tfln(stats, freq, docLen), "tfln"));
-    subs.add(Explanation.match((float) idf(stats, freq, docLen), "idf"));
+    subs.add(Explanation.match(this.k,
+        "k, hyperparam for the primitive weighting function"));
+    subs.add(Explanation.match(this.s,
+        "s, hyperparam for the growth function"));
+    subs.add(Explanation.match(this.queryLen, "queryLen, query length"));
+    subs.add(tfExplain(stats, freq, docLen));
+    subs.add(lnExplain(stats, freq, docLen));
+    subs.add(tflnExplain(stats, freq, docLen));
+    subs.add(idfExplain(stats, freq, docLen));
     subs.add(Explanation.match((float) gamma(stats, freq, docLen), "gamma"));
     super.explain(subs, stats, doc, freq, docLen);
   }
@@ -162,4 +165,47 @@ public abstract class Axiomatic extends SimilarityBase {
    * compute the gamma component (only for F3EXp and F3LOG)
    */
   protected abstract double gamma(BasicStats stats, double freq, double docLen);
+
+
+  /**
+   * Explain the score of the term frequency component for a single document
+   * @param stats the corpus level statistics
+   * @param freq number of occurrences of term in the document
+   * @param docLen the document length
+   * @return Explanation of how the tf component was computed
+   */
+  protected abstract Explanation tfExplain(BasicStats stats,
+                                           double freq, double docLen);
+
+  /**
+   * Explain the score of the document length component for a single document
+   * @param stats the corpus level statistics
+   * @param freq number of occurrences of term in the document
+   * @param docLen the document length
+   * @return Explanation of how the ln component was computed
+   */
+  protected abstract Explanation lnExplain(BasicStats stats,
+                                           double freq, double docLen);
+
+  /**
+   * Explain the score of the mixed term frequency and
+   * document length component for a single document
+   * @param stats the corpus level statistics
+   * @param freq number of occurrences of term in the document
+   * @param docLen the document length
+   * @return Explanation of how the tfln component was computed
+   */
+  protected abstract Explanation tflnExplain(BasicStats stats,
+                                             double freq, double docLen);
+
+  /**
+   * Explain the score of the inverted document frequency component
+   * for a single document
+   * @param stats the corpus level statistics
+   * @param freq number of occurrences of term in the document
+   * @param docLen the document length
+   * @return Explanation of how the idf component was computed
+   */
+  protected abstract Explanation idfExplain(BasicStats stats, double freq, double docLen);
+
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF1EXP.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF1EXP.java b/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF1EXP.java
index 0619b4e..c026feb 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF1EXP.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF1EXP.java
@@ -16,6 +16,9 @@
  */
 package org.apache.lucene.search.similarities;
 
+
+import org.apache.lucene.search.Explanation;
+
 /**
  * F1EXP is defined as Sum(tf(term_doc_freq)*ln(docLen)*IDF(term))
  * where IDF(t) = pow((N+1)/df(t), k) N=total num of docs, df=doc freq
@@ -92,4 +95,37 @@ public class AxiomaticF1EXP extends Axiomatic {
   protected double gamma(BasicStats stats, double freq, double docLen) {
     return 0.0;
   }
+
+  @Override
+  protected Explanation tfExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) tf(stats, freq, docLen),
+        "tf, term frequency computed as 1 + log(1 + log(freq)) from:",
+        Explanation.match((float) freq,
+            "freq, number of occurrences of term in the document"));
+  };
+
+  @Override
+  protected Explanation lnExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) ln(stats, freq, docLen),
+        "ln, document length computed as (avgdl + s) / (avgdl + dl * s) from:",
+        Explanation.match((float) stats.getAvgFieldLength(),
+            "avgdl, average length of field across all documents"),
+        Explanation.match((float) docLen,
+            "dl, length of field"));
+  };
+
+  protected Explanation tflnExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) tfln(stats, freq, docLen),
+        "tfln, mixed term frequency and document length, equals to 1");
+  };
+
+  protected Explanation idfExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) idf(stats, freq, docLen),
+        "idf, inverted document frequency computed as " +
+            "Math.pow((N + 1) / n, k) from:",
+        Explanation.match((float) stats.getNumberOfDocuments(),
+            "N, total number of documents with field"),
+        Explanation.match((float) stats.getDocFreq(),
+            "n, number of documents containing term"));
+  };
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF1LOG.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF1LOG.java b/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF1LOG.java
index f7a02da..2e19255 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF1LOG.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF1LOG.java
@@ -16,6 +16,8 @@
  */
 package org.apache.lucene.search.similarities;
 
+import org.apache.lucene.search.Explanation;
+
 /**
  * F1LOG is defined as Sum(tf(term_doc_freq)*ln(docLen)*IDF(term))
  * where IDF(t) = ln((N+1)/df(t)) N=total num of docs, df=doc freq
@@ -85,4 +87,36 @@ public class AxiomaticF1LOG extends Axiomatic {
   protected double gamma(BasicStats stats, double freq, double docLen) {
     return 0.0;
   }
+
+  @Override
+  protected Explanation tfExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) tf(stats, freq, docLen),
+        "tf, term frequency computed as 1 + log(1 + log(freq)) from:",
+        Explanation.match((float) freq,
+            "freq, number of occurrences of term in the document"));
+  };
+
+  @Override
+  protected Explanation lnExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) ln(stats, freq, docLen),
+        "ln, document length computed as (avgdl + s) / (avgdl + dl * s) from:",
+        Explanation.match((float) stats.getAvgFieldLength(),
+            "avgdl, average length of field across all documents"),
+        Explanation.match((float) docLen,
+            "dl, length of field"));
+  };
+
+  protected Explanation tflnExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) tfln(stats, freq, docLen),
+        "tfln, mixed term frequency and document length, equals to 1");
+  };
+
+  protected Explanation idfExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) idf(stats, freq, docLen),
+        "idf, inverted document frequency computed as log((N + 1) / n) from:",
+        Explanation.match((float) stats.getNumberOfDocuments(),
+            "N, total number of documents with field"),
+        Explanation.match((float) stats.getDocFreq(),
+            "n, number of documents containing term"));
+  };
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF2EXP.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF2EXP.java b/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF2EXP.java
index 0a3e4ad..bd28048 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF2EXP.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF2EXP.java
@@ -16,6 +16,8 @@
  */
 package org.apache.lucene.search.similarities;
 
+import org.apache.lucene.search.Explanation;
+
 /**
  * F2EXP is defined as Sum(tfln(term_doc_freq, docLen)*IDF(term))
  * where IDF(t) = pow((N+1)/df(t), k) N=total num of docs, df=doc freq
@@ -91,4 +93,38 @@ public class AxiomaticF2EXP extends Axiomatic {
   protected double gamma(BasicStats stats, double freq, double docLen) {
     return 0.0;
   }
+
+  @Override
+  protected Explanation tfExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) tf(stats, freq, docLen),
+        "tf, term frequency, equals to 1");
+  };
+
+  @Override
+  protected Explanation lnExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) ln(stats, freq, docLen),
+        "ln, document length, equals to 1");
+  };
+
+  protected Explanation tflnExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) tfln(stats, freq, docLen),
+        "tfln, mixed term frequency and document length, " +
+        "computed as freq / (freq + s + s * dl / avgdl) from:",
+        Explanation.match((float) freq,
+            "freq, number of occurrences of term in the document"),
+        Explanation.match((float) docLen,
+            "dl, length of field"),
+        Explanation.match((float) stats.getAvgFieldLength(),
+            "avgdl, average length of field across all documents"));
+  };
+
+  protected Explanation idfExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) idf(stats, freq, docLen),
+        "idf, inverted document frequency computed as " +
+            "Math.pow((N + 1) / n, k) from:",
+        Explanation.match((float) stats.getNumberOfDocuments(),
+            "N, total number of documents with field"),
+        Explanation.match((float) stats.getDocFreq(),
+            "n, number of documents containing term"));
+  };
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF2LOG.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF2LOG.java b/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF2LOG.java
index 2fc5e11..4780d1e 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF2LOG.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF2LOG.java
@@ -16,6 +16,8 @@
  */
 package org.apache.lucene.search.similarities;
 
+import org.apache.lucene.search.Explanation;
+
 /**
  * F2EXP is defined as Sum(tfln(term_doc_freq, docLen)*IDF(term))
  * where IDF(t) = ln((N+1)/df(t)) N=total num of docs, df=doc freq
@@ -83,4 +85,37 @@ public class AxiomaticF2LOG extends Axiomatic {
   protected double gamma(BasicStats stats, double freq, double docLen) {
     return 0.0;
   }
+
+  @Override
+  protected Explanation tfExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) tf(stats, freq, docLen),
+        "tf, term frequency, equals to 1");
+  };
+
+  @Override
+  protected Explanation lnExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) ln(stats, freq, docLen),
+        "ln, document length, equals to 1");
+  };
+
+  protected Explanation tflnExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) tfln(stats, freq, docLen),
+        "tfln, mixed term frequency and document length, " +
+            "computed as freq / (freq + s + s * dl / avgdl) from:",
+        Explanation.match((float) freq,
+            "freq, number of occurrences of term in the document"),
+        Explanation.match((float) docLen,
+            "dl, length of field"),
+        Explanation.match((float) stats.getAvgFieldLength(),
+            "avgdl, average length of field across all documents"));
+  };
+
+  protected Explanation idfExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) idf(stats, freq, docLen),
+        "idf, inverted document frequency computed as log((N + 1) / n) from:",
+        Explanation.match((float) stats.getNumberOfDocuments(),
+            "N, total number of documents with field"),
+        Explanation.match((float) stats.getDocFreq(),
+            "n, number of documents containing term"));
+  };
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF3EXP.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF3EXP.java b/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF3EXP.java
index 5c76317..635dc68 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF3EXP.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF3EXP.java
@@ -16,6 +16,8 @@
  */
 package org.apache.lucene.search.similarities;
 
+import org.apache.lucene.search.Explanation;
+
 /**
  * F3EXP is defined as Sum(tf(term_doc_freq)*IDF(term)-gamma(docLen, queryLen))
  * where IDF(t) = pow((N+1)/df(t), k) N=total num of docs, df=doc freq
@@ -91,4 +93,33 @@ public class AxiomaticF3EXP extends Axiomatic {
   protected double gamma(BasicStats stats, double freq, double docLen) {
     return (docLen - this.queryLen) * this.s * this.queryLen / stats.getAvgFieldLength();
   }
+
+  @Override
+  protected Explanation tfExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) tf(stats, freq, docLen),
+        "tf, term frequency computed as 1 + log(1 + log(freq)) from:",
+        Explanation.match((float) freq,
+            "freq, number of occurrences of term in the document"));
+  };
+
+  @Override
+  protected Explanation lnExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) ln(stats, freq, docLen),
+        "ln, document length, equals to 1");
+  };
+
+  protected Explanation tflnExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) tfln(stats, freq, docLen),
+        "tfln, mixed term frequency and document length, equals to 1");
+  };
+
+  protected Explanation idfExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) idf(stats, freq, docLen),
+        "idf, inverted document frequency computed as " +
+            "Math.pow((N + 1) / n, k) from:",
+        Explanation.match((float) stats.getNumberOfDocuments(),
+            "N, total number of documents with field"),
+        Explanation.match((float) stats.getDocFreq(),
+            "n, number of documents containing term"));
+  };
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF3LOG.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF3LOG.java b/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF3LOG.java
index 22a50b1..4753e4e 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF3LOG.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/AxiomaticF3LOG.java
@@ -16,6 +16,8 @@
  */
 package org.apache.lucene.search.similarities;
 
+import org.apache.lucene.search.Explanation;
+
 /**
  * F3EXP is defined as Sum(tf(term_doc_freq)*IDF(term)-gamma(docLen, queryLen))
  * where IDF(t) = ln((N+1)/df(t)) N=total num of docs, df=doc freq
@@ -80,4 +82,32 @@ public class AxiomaticF3LOG extends Axiomatic {
   protected double gamma(BasicStats stats, double freq, double docLen) {
     return (docLen - this.queryLen) * this.s * this.queryLen / stats.getAvgFieldLength();
   }
+
+  @Override
+  protected Explanation tfExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) tf(stats, freq, docLen),
+        "tf, term frequency computed as 1 + log(1 + log(freq)) from:",
+        Explanation.match((float) freq,
+            "freq, number of occurrences of term in the document"));
+  };
+
+  @Override
+  protected Explanation lnExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) ln(stats, freq, docLen),
+        "ln, document length, equals to 1");
+  };
+
+  protected Explanation tflnExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) tfln(stats, freq, docLen),
+        "tfln, mixed term frequency and document length, equals to 1");
+  };
+
+  protected Explanation idfExplain(BasicStats stats, double freq, double docLen){
+    return Explanation.match((float) idf(stats, freq, docLen),
+        "idf, inverted document frequency computed as log((N + 1) / n) from:",
+        Explanation.match((float) stats.getNumberOfDocuments(),
+            "N, total number of documents with field"),
+        Explanation.match((float) stats.getDocFreq(),
+            "n, number of documents containing term"));
+  };
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModel.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModel.java b/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModel.java
index 51d4571..7bcdd24 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModel.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModel.java
@@ -41,21 +41,13 @@ public abstract class BasicModel {
    * non-decreasing with {@code tfn}. */
   public abstract double score(BasicStats stats, double tfn, double aeTimes1pTfn);
   
+
   /**
    * Returns an explanation for the score.
-   * <p>Most basic models use the number of documents and the total term
-   * frequency to compute Inf<sub>1</sub>. This method provides a generic
-   * explanation for such models. Subclasses that use other statistics must
-   * override this method.</p>
+   * Subclasses must override this method.
    */
-  public Explanation explain(BasicStats stats, double tfn, double aeTimes1pTfn) {
-    return Explanation.match(
-        (float) (score(stats, tfn, aeTimes1pTfn) * (1 + tfn) / aeTimes1pTfn),
-        getClass().getSimpleName() + ", computed from: ",
-        Explanation.match(stats.getNumberOfDocuments(), "numberOfDocuments"),
-        Explanation.match(stats.getTotalTermFreq(), "totalTermFreq"));
-  }
-  
+  public abstract Explanation explain (BasicStats stats, double tfn, double aeTimes1pTfn);
+
   /**
    * Subclasses must override this method to return the code of the
    * basic model formula. Refer to the original paper for the list. 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelG.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelG.java b/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelG.java
index ce87196..72c0219 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelG.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelG.java
@@ -17,6 +17,8 @@
 package org.apache.lucene.search.similarities;
 
 
+import org.apache.lucene.search.Explanation;
+
 import static org.apache.lucene.search.similarities.SimilarityBase.log2;
 
 /**
@@ -49,6 +51,26 @@ public class BasicModelG extends BasicModel {
   }
 
   @Override
+  public Explanation explain(BasicStats stats, double tfn, double aeTimes1pTfn) {
+    double F = stats.getTotalTermFreq() + 1;
+    double N = stats.getNumberOfDocuments();
+    double lambda = F / (N + F);
+    Explanation explLambda = Explanation.match((float) lambda,
+        "lambda, computed as F / (N + F) from:",
+        Explanation.match((float) F,
+            "F, total number of occurrences of term across all docs + 1"),
+        Explanation.match((float) N,
+            "N, total number of documents with field"));
+
+    return Explanation.match(
+        (float) (score(stats, tfn, aeTimes1pTfn) * (1 + tfn) / aeTimes1pTfn),
+        getClass().getSimpleName() + ", computed as " +
+            "log2(lambda + 1) + tfn * log2((1 + lambda) / lambda) from:",
+        Explanation.match((float) tfn, "tfn, normalized term frequency"),
+        explLambda);
+  }
+
+  @Override
   public String toString() {
     return "G";
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelIF.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelIF.java b/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelIF.java
index 16781cd..974420e 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelIF.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelIF.java
@@ -17,6 +17,8 @@
 package org.apache.lucene.search.similarities;
 
 
+import org.apache.lucene.search.Explanation;
+
 import static org.apache.lucene.search.similarities.SimilarityBase.log2;
 
 /**
@@ -43,6 +45,19 @@ public class BasicModelIF extends BasicModel {
   }
 
   @Override
+    public Explanation explain(BasicStats stats, double tfn, double aeTimes1pTfn) {
+    return Explanation.match(
+        (float) (score(stats, tfn, aeTimes1pTfn) * (1 + tfn) / aeTimes1pTfn),
+        getClass().getSimpleName() + ", computed as " +
+            "tfn * log2(1 + (N + 1) / (F + 0.5)) from:",
+        Explanation.match((float) tfn, "tfn, normalized term frequency"),
+        Explanation.match(stats.getNumberOfDocuments(),
+            "N, total number of documents with field"),
+        Explanation.match(stats.getTotalTermFreq(),
+            "F, total number of occurrences of term across all documents"));
+  }
+
+  @Override
   public String toString() {
     return "I(F)";
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelIn.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelIn.java b/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelIn.java
index 5f1e181..82b65f4 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelIn.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelIn.java
@@ -47,9 +47,13 @@ public class BasicModelIn extends BasicModel {
   public final Explanation explain(BasicStats stats, double tfn, double aeTimes1pTfn) {
     return Explanation.match(
         (float) (score(stats, tfn, aeTimes1pTfn) * (1 + tfn) / aeTimes1pTfn),
-        getClass().getSimpleName() + ", computed from: ",
-        Explanation.match(stats.getNumberOfDocuments(), "numberOfDocuments"),
-        Explanation.match(stats.getDocFreq(), "docFreq"));
+        getClass().getSimpleName() +
+            ", computed as tfn * log2((N + 1) / (n + 0.5)) from:",
+        Explanation.match((float) tfn, "tfn, normalized term frequency"),
+        Explanation.match(stats.getNumberOfDocuments(),
+            "N, total number of documents with field"),
+        Explanation.match(stats.getDocFreq(),
+            "n, number of documents containing term"));
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelIne.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelIne.java b/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelIne.java
index fb755fa..c846976 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelIne.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/BasicModelIne.java
@@ -17,6 +17,7 @@
 package org.apache.lucene.search.similarities;
 
 
+import org.apache.lucene.search.Explanation;
 import static org.apache.lucene.search.similarities.SimilarityBase.log2;
 
 /**
@@ -45,6 +46,26 @@ public class BasicModelIne extends BasicModel {
   }
 
   @Override
+  public Explanation explain(BasicStats stats, double tfn, double aeTimes1pTfn) {
+    double F = stats.getTotalTermFreq();
+    double N = stats.getNumberOfDocuments();
+    double ne = N * (1 - Math.pow((N - 1) / N, F));
+    Explanation explNe = Explanation.match((float) ne,
+        "ne, computed as N * (1 - Math.pow((N - 1) / N, F)) from:",
+        Explanation.match((float) F,
+            "F, total number of occurrences of term across all docs"),
+        Explanation.match((float) N,
+            "N, total number of documents with field"));
+
+    return Explanation.match(
+        (float) (score(stats, tfn, aeTimes1pTfn) * (1 + tfn) / aeTimes1pTfn),
+        getClass().getSimpleName() + ", computed as " +
+            "tfn * log2((N + 1) / (ne + 0.5)) from:",
+        Explanation.match((float) tfn, "tfn, normalized term frequency"),
+        explNe);
+  }
+
+  @Override
   public String toString() {
     return "I(ne)";
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/BooleanSimilarity.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/BooleanSimilarity.java b/lucene/core/src/java/org/apache/lucene/search/similarities/BooleanSimilarity.java
index 35f7083..7134172 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/BooleanSimilarity.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/BooleanSimilarity.java
@@ -77,7 +77,7 @@ public class BooleanSimilarity extends Similarity {
 
       @Override
       public Explanation explain(int doc, Explanation freq) throws IOException {
-        Explanation queryBoostExpl = Explanation.match(boost, "boost");
+        Explanation queryBoostExpl = Explanation.match(boost, "boost, query boost");
         return Explanation.match(
             queryBoostExpl.getValue(),
             "score(" + getClass().getSimpleName() + ", doc=" + doc + "), computed from:",

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/DFISimilarity.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/DFISimilarity.java b/lucene/core/src/java/org/apache/lucene/search/similarities/DFISimilarity.java
index ca0f4aa..6ebe9cb 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/DFISimilarity.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/DFISimilarity.java
@@ -17,6 +17,8 @@
 package org.apache.lucene.search.similarities;
 
 
+import org.apache.lucene.search.Explanation;
+
 /**
  * Implements the <em>Divergence from Independence (DFI)</em> model based on Chi-square statistics
  * (i.e., standardized Chi-squared distance from independence in term frequency tf).
@@ -76,6 +78,38 @@ public class DFISimilarity extends SimilarityBase {
   }
 
   @Override
+  protected Explanation explain(
+      BasicStats stats, int doc, Explanation freq, double docLen) {
+    final double expected = (stats.getTotalTermFreq() + 1) * docLen /
+        (stats.getNumberOfFieldTokens() + 1);
+    if (freq.getValue() <= expected){
+      return Explanation.match((float) 0, "score(" +
+          getClass().getSimpleName() + ", doc=" + doc + ", freq=" +
+          freq.getValue() +"), equals to 0");
+    }
+    Explanation explExpected = Explanation.match((float) expected,
+        "expected, computed as (F + 1) * dl / (T + 1) from:",
+        Explanation.match(stats.getTotalTermFreq(),
+            "F, total number of occurrences of term across all docs"),
+        Explanation.match((float) docLen, "dl, length of field"),
+        Explanation.match(stats.getNumberOfFieldTokens(),
+            "T, total number of tokens in the field"));
+
+    final double measure = independence.score(freq.getValue(), expected);
+    Explanation explMeasure = Explanation.match((float) measure,
+        "measure, computed as independence.score(freq, expected) from:",
+        freq,
+        explExpected);
+
+    return Explanation.match(
+        (float) score(stats, freq.getValue(), docLen),
+        "score(" + getClass().getSimpleName() + ", doc=" + doc + ", freq=" +
+            freq.getValue() +"), computed as boost * log2(measure + 1) from:",
+        Explanation.match( (float)stats.getBoost(), "boost, query boost"),
+        explMeasure);
+  }
+
+  @Override
   public String toString() {
     return "DFI(" + independence + ")";
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/DFRSimilarity.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/DFRSimilarity.java b/lucene/core/src/java/org/apache/lucene/search/similarities/DFRSimilarity.java
index 788f30a..8c73e6e 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/DFRSimilarity.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/DFRSimilarity.java
@@ -17,6 +17,7 @@
 package org.apache.lucene.search.similarities;
 
 
+import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.lucene.search.Explanation;
@@ -122,7 +123,7 @@ public class DFRSimilarity extends SimilarityBase {
   protected void explain(List<Explanation> subs,
       BasicStats stats, int doc, double freq, double docLen) {
     if (stats.getBoost() != 1.0d) {
-      subs.add(Explanation.match( (float)stats.getBoost(), "boost"));
+      subs.add(Explanation.match( (float)stats.getBoost(), "boost, query boost"));
     }
     
     Explanation normExpl = normalization.explain(stats, freq, docLen);
@@ -134,6 +135,20 @@ public class DFRSimilarity extends SimilarityBase {
   }
 
   @Override
+  protected Explanation explain(
+      BasicStats stats, int doc, Explanation freq, double docLen) {
+    List<Explanation> subs = new ArrayList<>();
+    explain(subs, stats, doc, freq.getValue(), docLen);
+
+    return Explanation.match(
+        (float) score(stats, freq.getValue(), docLen),
+        "score(" + getClass().getSimpleName() + ", doc=" + doc + ", freq=" +
+            freq.getValue() +"), computed as boost * " +
+            "basicModel.score(stats, tfn) * afterEffect.score(stats, tfn) from:",
+        subs);
+  }
+
+  @Override
   public String toString() {
     return "DFR " + basicModel.toString() + afterEffect.toString()
                   + normalization.toString();

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/IBSimilarity.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/IBSimilarity.java b/lucene/core/src/java/org/apache/lucene/search/similarities/IBSimilarity.java
index a71614c..3e9ba0c 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/IBSimilarity.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/IBSimilarity.java
@@ -17,6 +17,7 @@
 package org.apache.lucene.search.similarities;
 
 
+import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.lucene.search.Explanation;
@@ -113,7 +114,7 @@ public class IBSimilarity extends SimilarityBase {
   protected void explain(
       List<Explanation> subs, BasicStats stats, int doc, double freq, double docLen) {
     if (stats.getBoost() != 1.0d) {
-      subs.add(Explanation.match((float)stats.getBoost(), "boost"));
+      subs.add(Explanation.match((float)stats.getBoost(), "boost, query boost"));
     }
     Explanation normExpl = normalization.explain(stats, freq, docLen);
     Explanation lambdaExpl = lambda.explain(stats);
@@ -121,6 +122,22 @@ public class IBSimilarity extends SimilarityBase {
     subs.add(lambdaExpl);
     subs.add(distribution.explain(stats, normExpl.getValue(), lambdaExpl.getValue()));
   }
+
+  @Override
+  protected Explanation explain(
+      BasicStats stats, int doc, Explanation freq, double docLen) {
+    List<Explanation> subs = new ArrayList<>();
+    explain(subs, stats, doc, freq.getValue(), docLen);
+
+    return Explanation.match(
+        (float) score(stats, freq.getValue(), docLen),
+        "score(" + getClass().getSimpleName() + ", doc=" + doc + ", freq=" +
+            freq.getValue() +"), computed as boost * " +
+            "distribution.score(stats, normalization.tfn(stats, freq," +
+            " docLen), lambda.lambda(stats)) from:",
+        subs);
+  }
+
   
   /**
    * The name of IB methods follow the pattern

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/LMDirichletSimilarity.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/LMDirichletSimilarity.java b/lucene/core/src/java/org/apache/lucene/search/similarities/LMDirichletSimilarity.java
index 2a4354e..be7329b 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/LMDirichletSimilarity.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/LMDirichletSimilarity.java
@@ -17,6 +17,7 @@
 package org.apache.lucene.search.similarities;
 
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 
@@ -86,20 +87,43 @@ public class LMDirichletSimilarity extends LMSimilarity {
   protected void explain(List<Explanation> subs, BasicStats stats, int doc,
       double freq, double docLen) {
     if (stats.getBoost() != 1.0d) {
-      subs.add(Explanation.match((float) stats.getBoost(), "boost"));
+      subs.add(Explanation.match((float) stats.getBoost(), "query boost"));
     }
+    double p = ((LMStats)stats).getCollectionProbability();
+    Explanation explP = Explanation.match((float) p,
+        "P, probability that the current term is generated by the collection");
+    Explanation explFreq = Explanation.match((float) freq,
+        "freq, number of occurrences of term in the document");
 
     subs.add(Explanation.match(mu, "mu"));
     Explanation weightExpl = Explanation.match(
         (float)Math.log(1 + freq /
         (mu * ((LMStats)stats).getCollectionProbability())),
-        "term weight");
+        "term weight, computed as log(1 + freq /(mu * P)) from:",
+        explFreq,
+        explP);
     subs.add(weightExpl);
     subs.add(Explanation.match(
-        (float)Math.log(mu / (docLen + mu)), "document norm"));
+        (float)Math.log(mu / (docLen + mu)),
+        "document norm, computed as log(mu / (dl + mu))"));
+    subs.add(Explanation.match((float) docLen,"dl, length of field"));
     super.explain(subs, stats, doc, freq, docLen);
   }
 
+  @Override
+  protected Explanation explain(
+      BasicStats stats, int doc, Explanation freq, double docLen) {
+    List<Explanation> subs = new ArrayList<>();
+    explain(subs, stats, doc, freq.getValue(), docLen);
+
+    return Explanation.match(
+        (float) score(stats, freq.getValue(), docLen),
+        "score(" + getClass().getSimpleName() + ", doc=" + doc + ", freq=" +
+            freq.getValue() +"), computed as boost * " +
+            "(term weight + document norm) from:",
+        subs);
+  }
+
   /** Returns the &mu; parameter. */
   public float getMu() {
     return mu;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/LMJelinekMercerSimilarity.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/LMJelinekMercerSimilarity.java b/lucene/core/src/java/org/apache/lucene/search/similarities/LMJelinekMercerSimilarity.java
index fa0ebcf..f6db238 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/LMJelinekMercerSimilarity.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/LMJelinekMercerSimilarity.java
@@ -17,6 +17,7 @@
 package org.apache.lucene.search.similarities;
 
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 
@@ -79,9 +80,31 @@ public class LMJelinekMercerSimilarity extends LMSimilarity {
       subs.add(Explanation.match((float) stats.getBoost(), "boost"));
     }
     subs.add(Explanation.match(lambda, "lambda"));
+    double p = ((LMStats)stats).getCollectionProbability();
+    Explanation explP = Explanation.match((float) p,
+        "P, probability that the current term is generated by the collection");
+    subs.add(explP);
+    Explanation explFreq = Explanation.match((float) freq,
+        "freq, number of occurrences of term in the document");
+    subs.add(explFreq);
+    subs.add(Explanation.match((float) docLen,"dl, length of field"));
     super.explain(subs, stats, doc, freq, docLen);
   }
 
+  @Override
+  protected Explanation explain(
+      BasicStats stats, int doc, Explanation freq, double docLen) {
+    List<Explanation> subs = new ArrayList<>();
+    explain(subs, stats, doc, freq.getValue(), docLen);
+
+    return Explanation.match(
+        (float) score(stats, freq.getValue(), docLen),
+        "score(" + getClass().getSimpleName() + ", doc=" + doc + ", freq=" +
+            freq.getValue() +"), computed as boost * " +
+            "log(1 + ((1 - lambda) * freq / dl) /(lambda * P)) from:",
+        subs);
+  }
+
   /** Returns the &lambda; parameter. */
   public float getLambda() {
     return lambda;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/LambdaDF.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/LambdaDF.java b/lucene/core/src/java/org/apache/lucene/search/similarities/LambdaDF.java
index ef87c8a..7dc320d 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/LambdaDF.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/LambdaDF.java
@@ -37,9 +37,12 @@ public class LambdaDF extends Lambda {
   public final Explanation explain(BasicStats stats) {
     return Explanation.match(
         lambda(stats),
-        getClass().getSimpleName() + ", computed from: ",
-        Explanation.match(stats.getDocFreq(), "docFreq"),
-        Explanation.match(stats.getNumberOfDocuments(), "numberOfDocuments"));
+        getClass().getSimpleName()
+            + ", computed as (n + 1) / (N + 1) from:",
+        Explanation.match(stats.getDocFreq(),
+            "n, number of documents containing term"),
+        Explanation.match(stats.getNumberOfDocuments(),
+            "N, total number of documents with field"));
   }
   
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/LambdaTTF.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/LambdaTTF.java b/lucene/core/src/java/org/apache/lucene/search/similarities/LambdaTTF.java
index 20fe652..6dc54a3 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/LambdaTTF.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/LambdaTTF.java
@@ -33,15 +33,17 @@ public class LambdaTTF extends Lambda {
     return (stats.getTotalTermFreq()+1F) / (stats.getNumberOfDocuments()+1F);
   }
 
-  @Override
   public final Explanation explain(BasicStats stats) {
     return Explanation.match(
         lambda(stats),
-        getClass().getSimpleName() + ", computed from: ",
-        Explanation.match(stats.getTotalTermFreq(), "totalTermFreq"),
-        Explanation.match(stats.getNumberOfDocuments(), "numberOfDocuments"));
+        getClass().getSimpleName()
+            + ", computed as (F + 1) / (N + 1) from:",
+        Explanation.match(stats.getTotalTermFreq(),
+            "F, total number of occurrences of term across all documents"),
+        Explanation.match(stats.getNumberOfDocuments(),
+            "N, total number of documents with field"));
   }
-  
+
   @Override
   public String toString() {
     return "L";

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/Normalization.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/Normalization.java b/lucene/core/src/java/org/apache/lucene/search/similarities/Normalization.java
index e20ca02..6bde17d 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/Normalization.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/Normalization.java
@@ -47,10 +47,12 @@ public abstract class Normalization {
   public Explanation explain(BasicStats stats, double tf, double len) {
     return Explanation.match(
         (float) tfn(stats, tf, len),
-        getClass().getSimpleName() + ", computed from: ",
-        Explanation.match((float) tf, "tf"),
-        Explanation.match((float) stats.getAvgFieldLength(), "avgFieldLength"),
-        Explanation.match((float) len, "len"));
+        getClass().getSimpleName() + ", computed from:",
+        Explanation.match((float) tf,
+            "tf, number of occurrences of term in the document"),
+        Explanation.match((float) stats.getAvgFieldLength(),
+            "avgfl, average length of field across all documents"),
+        Explanation.match((float) len, "fl, field length of the document"));
   }
 
   /** Implementation used when there is no normalization. */

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/NormalizationH1.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/NormalizationH1.java b/lucene/core/src/java/org/apache/lucene/search/similarities/NormalizationH1.java
index 8e5a28f..5fa4f1f 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/NormalizationH1.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/NormalizationH1.java
@@ -17,6 +17,8 @@
 package org.apache.lucene.search.similarities;
 
 
+import org.apache.lucene.search.Explanation;
+
 /**
  * Normalization model that assumes a uniform distribution of the term frequency.
  * <p>While this model is parameterless in the
@@ -56,6 +58,21 @@ public class NormalizationH1 extends Normalization {
   }
 
   @Override
+  public Explanation explain(BasicStats stats, double tf, double len) {
+    return Explanation.match(
+        (float) tfn(stats, tf, len),
+        getClass().getSimpleName()
+            + ", computed as tf * c * (avgfl / fl) from:",
+        Explanation.match((float) tf,
+            "tf, number of occurrences of term in the document"),
+        Explanation.match(c,
+            "c, hyper-parameter"),
+        Explanation.match((float) stats.getAvgFieldLength(),
+            "avgfl, average length of field across all documents"),
+        Explanation.match((float) len, "fl, field length of the document"));
+  }
+
+  @Override
   public String toString() {
     return "1";
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/NormalizationH2.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/NormalizationH2.java b/lucene/core/src/java/org/apache/lucene/search/similarities/NormalizationH2.java
index 24fb74e..a30bd4b 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/NormalizationH2.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/NormalizationH2.java
@@ -17,6 +17,8 @@
 package org.apache.lucene.search.similarities;
 
 
+import org.apache.lucene.search.Explanation;
+
 import static org.apache.lucene.search.similarities.SimilarityBase.log2;
 
 /**
@@ -58,6 +60,21 @@ public class NormalizationH2 extends Normalization {
   }
 
   @Override
+  public Explanation explain(BasicStats stats, double tf, double len) {
+    return Explanation.match(
+        (float) tfn(stats, tf, len),
+        getClass().getSimpleName()
+            + ", computed as tf * log2(1 + c * avgfl / fl) from:",
+        Explanation.match((float) tf,
+            "tf, number of occurrences of term in the document"),
+        Explanation.match(c,
+            "c, hyper-parameter"),
+        Explanation.match((float) stats.getAvgFieldLength(),
+            "avgfl, average length of field across all documents"),
+        Explanation.match((float) len, "fl, field length of the document"));
+  }
+
+  @Override
   public String toString() {
     return "2";
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/NormalizationH3.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/NormalizationH3.java b/lucene/core/src/java/org/apache/lucene/search/similarities/NormalizationH3.java
index 0bbea49..64c9ec3 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/NormalizationH3.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/NormalizationH3.java
@@ -17,6 +17,8 @@
 package org.apache.lucene.search.similarities;
 
 
+import org.apache.lucene.search.Explanation;
+
 /**
  * Dirichlet Priors normalization
  * @lucene.experimental
@@ -48,6 +50,23 @@ public class NormalizationH3 extends Normalization {
   }
 
   @Override
+  public Explanation explain(BasicStats stats, double tf, double len) {
+    return Explanation.match(
+        (float) tfn(stats, tf, len),
+        getClass().getSimpleName()
+            + ", computed as (tf + mu * ((F+1) / (T+1))) / (fl + mu) * mu from:",
+        Explanation.match((float) tf,
+            "tf, number of occurrences of term in the document"),
+        Explanation.match(mu,
+            "mu, smoothing parameter"),
+        Explanation.match((float) stats.getTotalTermFreq(),
+            "F,  total number of occurrences of term across all documents"),
+        Explanation.match((float) stats.getNumberOfFieldTokens(),
+            "T, total number of tokens of the field across all documents"),
+        Explanation.match((float) len, "fl, field length of the document"));
+  }
+
+  @Override
   public String toString() {
     return "3(" + mu + ")";
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/core/src/java/org/apache/lucene/search/similarities/NormalizationZ.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/NormalizationZ.java b/lucene/core/src/java/org/apache/lucene/search/similarities/NormalizationZ.java
index dabf9c9..ca7108c 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/NormalizationZ.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/NormalizationZ.java
@@ -17,6 +17,8 @@
 package org.apache.lucene.search.similarities;
 
 
+import org.apache.lucene.search.Explanation;
+
 /**
  * Pareto-Zipf Normalization
  * @lucene.experimental
@@ -49,6 +51,20 @@ public class NormalizationZ extends Normalization {
   }
 
   @Override
+  public Explanation explain(BasicStats stats, double tf, double len) {
+    return Explanation.match(
+        (float) tfn(stats, tf, len),
+        getClass().getSimpleName()
+            + ", computed as tf * Math.pow(avgfl / fl, z) from:",
+        Explanation.match((float) tf,
+            "tf, number of occurrences of term in the document"),
+        Explanation.match((float) stats.getAvgFieldLength(),
+            "avgfl, average length of field across all documents"),
+        Explanation.match((float) len, "fl, field length of the document"),
+        Explanation.match(z, "z, relates to specificity of the language"));
+  }
+
+  @Override
   public String toString() {
     return "Z(" + z + ")";
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dCircleShape.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dCircleShape.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dCircleShape.java
index ccef92a..d01e2b8 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dCircleShape.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dCircleShape.java
@@ -20,13 +20,10 @@ package org.apache.lucene.spatial.spatial4j;
 import org.apache.lucene.spatial3d.geom.GeoCircle;
 import org.apache.lucene.spatial3d.geom.GeoCircleFactory;
 import org.apache.lucene.spatial3d.geom.GeoPointShapeFactory;
-import org.apache.lucene.spatial3d.geom.PlanetModel;
 import org.locationtech.spatial4j.context.SpatialContext;
 import org.locationtech.spatial4j.distance.DistanceUtils;
 import org.locationtech.spatial4j.shape.Circle;
 import org.locationtech.spatial4j.shape.Point;
-import org.locationtech.spatial4j.shape.Shape;
-import org.locationtech.spatial4j.shape.SpatialRelation;
 
 /**
  * Specialization of a {@link Geo3dShape} which represents a {@link Circle}.
@@ -67,16 +64,4 @@ public class Geo3dCircleShape extends Geo3dShape<GeoCircle> implements Circle {
     }
     return center;
   }
-
-  //TODO Improve GeoCircle to properly relate a point with WGS84 model -- LUCENE-7970
-  @Override
-  public SpatialRelation relate(Shape other) {
-    if (shape.getPlanetModel() != PlanetModel.SPHERE && other instanceof Point) {
-      if (spatialcontext.getDistCalc().distance((Point) other, getCenter()) <= getRadius()) {
-        return SpatialRelation.CONTAINS;
-      }
-      return SpatialRelation.DISJOINT;
-    }
-    return super.relate(other);
-  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dDistanceCalculator.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dDistanceCalculator.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dDistanceCalculator.java
index 5154de4..8fdb481 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dDistanceCalculator.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dDistanceCalculator.java
@@ -73,62 +73,20 @@ public class Geo3dDistanceCalculator implements DistanceCalculator {
 
   @Override
   public Point pointOnBearing(Point from, double distDEG, double bearingDEG, SpatialContext ctx, Point reuse) {
-    // Algorithm using Vincenty's formulae (https://en.wikipedia.org/wiki/Vincenty%27s_formulae)
-    // which takes into account that planets may not be spherical.
-    //Code adaptation from http://www.movable-type.co.uk/scripts/latlong-vincenty.html
     Geo3dPointShape geoFrom = (Geo3dPointShape) from;
     GeoPoint point = (GeoPoint) geoFrom.shape;
-    double lat = point.getLatitude();
-    double lon = point.getLongitude();
     double dist = DistanceUtils.DEGREES_TO_RADIANS * distDEG;
     double bearing = DistanceUtils.DEGREES_TO_RADIANS * bearingDEG;
-
-    double sinα1 = Math.sin(bearing);
-    double cosα1 = Math.cos(bearing);
-
-    double tanU1 = (1 - planetModel.flattening) * Math.tan(lat);
-    double cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1));
-    double sinU1 = tanU1 * cosU1;
-
-    double σ1 = Math.atan2(tanU1, cosα1);
-    double sinα = cosU1 * sinα1;
-    double cosSqα = 1 - sinα * sinα;
-    double uSq = cosSqα * planetModel.squareRatio;// (planetModel.ab* planetModel.ab - planetModel.c*planetModel.c) / (planetModel.c*planetModel.c);
-    double A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
-    double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
-
-    double cos2σM;
-    double sinσ;
-    double cosσ;
-    double Δσ;
-
-    double σ = dist / (planetModel.c * A);
-    double σʹ;
-    double iterations = 0;
-    do {
-      cos2σM = Math.cos(2 * σ1 + σ);
-      sinσ = Math.sin(σ);
-      cosσ = Math.cos(σ);
-      Δσ = B * sinσ * (cos2σM + B / 4 * (cosσ * (-1 + 2 * cos2σM * cos2σM) -
-          B / 6 * cos2σM * (-3 + 4 * sinσ * sinσ) * (-3 + 4 * cos2σM * cos2σM)));
-      σʹ = σ;
-      σ = dist / (planetModel.c * A) + Δσ;
-    } while (Math.abs(σ - σʹ) > 1e-12 && ++iterations < 200);
-
-    if (iterations >= 200) {
-      throw new RuntimeException("Formula failed to converge");
+    GeoPoint newPoint = planetModel.surfacePointOnBearing(point, dist, bearing);
+    double newLat = newPoint.getLatitude() * DistanceUtils.RADIANS_TO_DEGREES;
+    double newLon = newPoint.getLongitude() * DistanceUtils.RADIANS_TO_DEGREES;
+    if (reuse != null) {
+      reuse.reset(newLon, newLat);
+      return reuse;
+    }
+    else {
+      return ctx.getShapeFactory().pointXY(newLon, newLat);
     }
-
-    double x = sinU1 * sinσ - cosU1 * cosσ * cosα1;
-    double φ2 = Math.atan2(sinU1 * cosσ + cosU1 * sinσ * cosα1, (1 - planetModel.flattening) * Math.sqrt(sinα * sinα + x * x));
-    double λ = Math.atan2(sinσ * sinα1, cosU1 * cosσ - sinU1 * sinσ * cosα1);
-    double C = planetModel.flattening / 16 * cosSqα * (4 + planetModel.flattening * (4 - 3 * cosSqα));
-    double L = λ - (1 - C) * planetModel.flattening * sinα *
-        (σ + C * sinσ * (cos2σM + C * cosσ * (-1 + 2 * cos2σM * cos2σM)));
-    double λ2 = (lon + L + 3 * Math.PI) % (2 * Math.PI) - Math.PI;  // normalise to -180..+180
-
-    return ctx.getShapeFactory().pointXY(λ2 * DistanceUtils.RADIANS_TO_DEGREES,
-        φ2 * DistanceUtils.RADIANS_TO_DEGREES);
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dShape.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dShape.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dShape.java
index eedf7d6..327ac8f 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dShape.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dShape.java
@@ -112,11 +112,7 @@ public class Geo3dShape<T extends GeoAreaShape> implements Shape {
     if (bbox == null) {
       LatLonBounds bounds = new LatLonBounds();
       shape.getBounds(bounds);
-      double leftLon = bounds.checkNoLongitudeBound() ? -Math.PI : bounds.getLeftLongitude();
-      double rightLon = bounds.checkNoLongitudeBound() ? Math.PI : bounds.getRightLongitude();
-      double minLat = bounds.checkNoBottomLatitudeBound() ? -Math.PI * 0.5 : bounds.getMinLatitude();
-      double maxLat = bounds.checkNoTopLatitudeBound() ? Math.PI * 0.5 : bounds.getMaxLatitude();
-      GeoBBox geoBBox = GeoBBoxFactory.makeGeoBBox(shape.getPlanetModel(), maxLat, minLat, leftLon, rightLon);
+      GeoBBox geoBBox = GeoBBoxFactory.makeGeoBBox(shape.getPlanetModel(), bounds);
       bbox = new Geo3dRectangleShape(geoBBox, spatialcontext);
       this.boundingBox = bbox;
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dShapeFactory.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dShapeFactory.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dShapeFactory.java
index a80a043..282d93b 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dShapeFactory.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dShapeFactory.java
@@ -55,6 +55,13 @@ public class Geo3dShapeFactory implements ShapeFactory {
   private SpatialContext context;
   private PlanetModel planetModel;
 
+  /**
+   * Default accuracy for circles when not using the unit sphere.
+   * It is equivalent to ~10m on the surface of the earth.
+   */
+  private static final double DEFAULT_CIRCLE_ACCURACY = 1e-4;
+  private double circleAccuracy = DEFAULT_CIRCLE_ACCURACY;
+
   @SuppressWarnings("unchecked")
   public Geo3dShapeFactory(SpatialContext context, SpatialContextFactory factory) {
     this.context = context;
@@ -67,6 +74,16 @@ public class Geo3dShapeFactory implements ShapeFactory {
     return context;
   }
 
+  /**
+   * Set the accuracy for circles in decimal degrees. Note that accuracy has no effect
+   * when the planet model is a sphere. In that case, circles are always fully precise.
+   *
+   * @param circleAccuracy the provided accuracy in decimal degrees.
+   */
+  public void setCircleAccuracy(double circleAccuracy) {
+    this.circleAccuracy = circleAccuracy;
+  }
+
   @Override
   public boolean isNormWrapLongitude() {
     return normWrapLongitude;
@@ -150,10 +167,23 @@ public class Geo3dShapeFactory implements ShapeFactory {
 
   @Override
   public Circle circle(double x, double y, double distance) {
-    GeoCircle circle = GeoCircleFactory.makeGeoCircle(planetModel,
-        y * DistanceUtils.DEGREES_TO_RADIANS,
-        x * DistanceUtils.DEGREES_TO_RADIANS,
-        distance * DistanceUtils.DEGREES_TO_RADIANS);
+    GeoCircle circle;
+    if (planetModel.isSphere()) {
+      circle = GeoCircleFactory.makeGeoCircle(planetModel,
+          y * DistanceUtils.DEGREES_TO_RADIANS,
+          x * DistanceUtils.DEGREES_TO_RADIANS,
+          distance * DistanceUtils.DEGREES_TO_RADIANS);
+    }
+    else {
+      //accuracy is defined as a linear distance in this class. At tiny distances, linear distance
+      //can be approximated to surface distance in radians.
+      circle = GeoCircleFactory.makeExactGeoCircle(planetModel,
+          y * DistanceUtils.DEGREES_TO_RADIANS,
+          x * DistanceUtils.DEGREES_TO_RADIANS,
+          distance * DistanceUtils.DEGREES_TO_RADIANS,
+          circleAccuracy * DistanceUtils.DEGREES_TO_RADIANS);
+
+    }
     return new Geo3dCircleShape(circle, context);
   }
 
@@ -238,8 +268,7 @@ public class Geo3dShapeFactory implements ShapeFactory {
 
   /**
    * Geo3d implementation of {@link org.locationtech.spatial4j.shape.ShapeFactory.LineStringBuilder} to generate
-   * nine Strings. Note that GeoPath needs a buffer so we set the
-   * buffer to 1e-10.
+   * line strings.
    */
   private class Geo3dLineStringBuilder extends Geo3dPointBuilder<LineStringBuilder> implements LineStringBuilder {
 
@@ -373,7 +402,7 @@ public class Geo3dShapeFactory implements ShapeFactory {
 
   /**
    * Geo3d implementation of {@link org.locationtech.spatial4j.shape.ShapeFactory.MultiShapeBuilder} to generate
-   * geometry collections
+   * geometry collections.
    *
    * @param <T> is the type of shapes.
    */

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTestCase.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTestCase.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTestCase.java
deleted file mode 100644
index 9873012..0000000
--- a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTestCase.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * 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.
- */
-package org.apache.lucene.spatial.spatial4j;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.lucene.spatial3d.geom.GeoPath;
-import org.apache.lucene.spatial3d.geom.GeoPolygon;
-import org.locationtech.spatial4j.TestLog;
-import org.locationtech.spatial4j.context.SpatialContext;
-import org.locationtech.spatial4j.distance.DistanceUtils;
-import org.locationtech.spatial4j.shape.Circle;
-import org.locationtech.spatial4j.shape.Point;
-import org.locationtech.spatial4j.shape.RectIntersectionTestHelper;
-import org.apache.lucene.spatial3d.geom.LatLonBounds;
-import org.apache.lucene.spatial3d.geom.GeoBBox;
-import org.apache.lucene.spatial3d.geom.GeoBBoxFactory;
-import org.apache.lucene.spatial3d.geom.GeoCircle;
-import org.apache.lucene.spatial3d.geom.GeoCircleFactory;
-import org.apache.lucene.spatial3d.geom.GeoPathFactory;
-import org.apache.lucene.spatial3d.geom.GeoPoint;
-import org.apache.lucene.spatial3d.geom.GeoPolygonFactory;
-import org.apache.lucene.spatial3d.geom.GeoShape;
-import org.apache.lucene.spatial3d.geom.PlanetModel;
-import org.junit.Rule;
-import org.junit.Test;
-
-import static org.locationtech.spatial4j.distance.DistanceUtils.DEGREES_TO_RADIANS;
-
-public abstract class Geo3dShapeRectRelationTestCase extends RandomizedShapeTestCase {
-  protected final static double RADIANS_PER_DEGREE = Math.PI/180.0;
-
-  @Rule
-  public final TestLog testLog = TestLog.instance;
-
-  protected final PlanetModel planetModel;
-
-  public Geo3dShapeRectRelationTestCase(PlanetModel planetModel) {
-    super(SpatialContext.GEO);
-    this.planetModel = planetModel;
-  }
-
-  protected GeoBBox getBoundingBox(final GeoShape path) {
-    LatLonBounds bounds = new LatLonBounds();
-    path.getBounds(bounds);
-
-    double leftLon;
-    double rightLon;
-    if (bounds.checkNoLongitudeBound()) {
-      leftLon = -Math.PI;
-      rightLon = Math.PI;
-    } else {
-      leftLon = bounds.getLeftLongitude().doubleValue();
-      rightLon = bounds.getRightLongitude().doubleValue();
-    }
-    double minLat;
-    if (bounds.checkNoBottomLatitudeBound()) {
-      minLat = -Math.PI * 0.5;
-    } else {
-      minLat = bounds.getMinLatitude().doubleValue();
-    }
-    double maxLat;
-    if (bounds.checkNoTopLatitudeBound()) {
-      maxLat = Math.PI * 0.5;
-    } else {
-      maxLat = bounds.getMaxLatitude().doubleValue();
-    }
-    return GeoBBoxFactory.makeGeoBBox(planetModel, maxLat, minLat, leftLon, rightLon);
-  }
-
-  public abstract class Geo3dRectIntersectionTestHelper extends RectIntersectionTestHelper<Geo3dShape> {
-
-    public Geo3dRectIntersectionTestHelper(SpatialContext ctx) {
-      super(ctx);
-    }
-
-    //20 times each -- should be plenty
-
-    protected int getContainsMinimum(int laps) {
-      return 20;
-    }
-
-    protected int getIntersectsMinimum(int laps) {
-      return 20;
-    }
-
-    // producing "within" cases in Geo3D based on our random shapes doesn't happen often. It'd be nice to increase this.
-    protected int getWithinMinimum(int laps) {
-      return 2;
-    }
-
-    protected int getDisjointMinimum(int laps) {
-      return 20;
-    }
-
-    protected int getBoundingMinimum(int laps) {
-      return 20;
-    }
-  }
-
-  @Test
-  public void testGeoCircleRect() {
-    new Geo3dRectIntersectionTestHelper(ctx) {
-
-      @Override
-      protected Geo3dShape generateRandomShape(Point nearP) {
-        final int circleRadius = 180 - random().nextInt(180);//no 0-radius
-        final Point point = nearP;
-        final GeoCircle shape = GeoCircleFactory.makeGeoCircle(planetModel, point.getY() * DEGREES_TO_RADIANS, point.getX() * DEGREES_TO_RADIANS,
-            circleRadius * DEGREES_TO_RADIANS);
-        return new Geo3dShape(shape, ctx);
-      }
-
-      @Override
-      protected Point randomPointInEmptyShape(Geo3dShape shape) {
-        GeoPoint geoPoint = ((GeoCircle)shape.shape).getCenter();
-        return geoPointToSpatial4jPoint(geoPoint);
-      }
-
-    }.testRelateWithRectangle();
-  }
-
-  @Test
-  public void testGeoBBoxRect() {
-    new Geo3dRectIntersectionTestHelper(ctx) {
-
-      @Override
-      protected boolean isRandomShapeRectangular() {
-        return true;
-      }
-
-      @Override
-      protected Geo3dShape generateRandomShape(Point nearP) {
-        // (ignoring nearP)
-        Point ulhcPoint = randomPoint();
-        Point lrhcPoint = randomPoint();
-        if (ulhcPoint.getY() < lrhcPoint.getY()) {
-          //swap
-          Point temp = ulhcPoint;
-          ulhcPoint = lrhcPoint;
-          lrhcPoint = temp;
-        }
-        final GeoBBox shape = GeoBBoxFactory.makeGeoBBox(planetModel, ulhcPoint.getY() * DEGREES_TO_RADIANS,
-            lrhcPoint.getY() * DEGREES_TO_RADIANS,
-            ulhcPoint.getX() * DEGREES_TO_RADIANS,
-            lrhcPoint.getX() * DEGREES_TO_RADIANS);
-        return new Geo3dShape(shape, ctx);
-      }
-
-      @Override
-      protected Point randomPointInEmptyShape(Geo3dShape shape) {
-        return shape.getBoundingBox().getCenter();
-      }
-    }.testRelateWithRectangle();
-  }
-
-  @Test
-  public void testGeoPolygonRect() {
-    new Geo3dRectIntersectionTestHelper(ctx) {
-
-      @Override
-      protected Geo3dShape generateRandomShape(Point nearP) {
-        final Point centerPoint = randomPoint();
-        final int maxDistance = random().nextInt(160) + 20;
-        final Circle pointZone = ctx.makeCircle(centerPoint, maxDistance);
-        final int vertexCount = random().nextInt(3) + 3;
-        while (true) {
-          final List<GeoPoint> geoPoints = new ArrayList<>();
-          while (geoPoints.size() < vertexCount) {
-            final Point point = randomPointIn(pointZone);
-            final GeoPoint gPt = new GeoPoint(planetModel, point.getY() * DEGREES_TO_RADIANS, point.getX() * DEGREES_TO_RADIANS);
-            geoPoints.add(gPt);
-          }
-          try {
-            final GeoPolygon shape = GeoPolygonFactory.makeGeoPolygon(planetModel, geoPoints);
-            if (shape == null) {
-              continue;
-            }
-            return new Geo3dShape(shape, ctx);
-          } catch (IllegalArgumentException e) {
-            // This is what happens when we create a shape that is invalid.  Although it is conceivable that there are cases where
-            // the exception is thrown incorrectly, we aren't going to be able to do that in this random test.
-            continue;
-          }
-        }
-      }
-
-      @Override
-      protected Point randomPointInEmptyShape(Geo3dShape shape) {
-        throw new IllegalStateException("unexpected; need to finish test code");
-      }
-
-      @Override
-      protected int getWithinMinimum(int laps) {
-        // Long/thin so lets just find 1.
-        return 1;
-      }
-
-    }.testRelateWithRectangle();
-  }
-
-  @Test
-  public void testGeoPathRect() {
-    new Geo3dRectIntersectionTestHelper(ctx) {
-
-      @Override
-      protected Geo3dShape generateRandomShape(Point nearP) {
-        final Point centerPoint = randomPoint();
-        final int maxDistance = random().nextInt(160) + 20;
-        final Circle pointZone = ctx.makeCircle(centerPoint, maxDistance);
-        final int pointCount = random().nextInt(5) + 1;
-        final double width = (random().nextInt(89)+1) * DEGREES_TO_RADIANS;
-        final GeoPoint[] points = new GeoPoint[pointCount];
-        while (true) {
-          for (int i = 0; i < pointCount; i++) {
-            final Point nextPoint = randomPointIn(pointZone);
-            points[i] = new GeoPoint(planetModel, nextPoint.getY() * DEGREES_TO_RADIANS, nextPoint.getX() * DEGREES_TO_RADIANS);
-          }
-          
-          try {
-            final GeoPath path = GeoPathFactory.makeGeoPath(planetModel, width, points);
-            return new Geo3dShape(path, ctx);
-          } catch (IllegalArgumentException e) {
-            // This is what happens when we create a shape that is invalid.  Although it is conceivable that there are cases where
-            // the exception is thrown incorrectly, we aren't going to be able to do that in this random test.
-            continue;
-          }
-        }
-      }
-
-      @Override
-      protected Point randomPointInEmptyShape(Geo3dShape shape) {
-        throw new IllegalStateException("unexpected; need to finish test code");
-      }
-
-      @Override
-      protected int getWithinMinimum(int laps) {
-        // Long/thin so lets just find 1.
-        return 1;
-      }
-
-    }.testRelateWithRectangle();
-  }
-
-  private Point geoPointToSpatial4jPoint(GeoPoint geoPoint) {
-    return ctx.makePoint(geoPoint.getLongitude() * DistanceUtils.RADIANS_TO_DEGREES,
-        geoPoint.getLatitude() * DistanceUtils.RADIANS_TO_DEGREES);
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeSphereModelRectRelationTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeSphereModelRectRelationTest.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeSphereModelRectRelationTest.java
index 20db21c..bf152b7 100644
--- a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeSphereModelRectRelationTest.java
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeSphereModelRectRelationTest.java
@@ -34,13 +34,13 @@ import org.locationtech.spatial4j.shape.Point;
 import org.locationtech.spatial4j.shape.Rectangle;
 import org.locationtech.spatial4j.shape.SpatialRelation;
 
-public class Geo3dShapeSphereModelRectRelationTest extends Geo3dShapeRectRelationTestCase {
+public class Geo3dShapeSphereModelRectRelationTest extends ShapeRectRelationTestCase {
+
+  PlanetModel planetModel = PlanetModel.SPHERE;
 
   public Geo3dShapeSphereModelRectRelationTest() {
-    super(PlanetModel.SPHERE);
     Geo3dSpatialContextFactory factory = new Geo3dSpatialContextFactory();
-    factory.planetModel = PlanetModel.SPHERE;
-    //factory.distCalc = new GeodesicSphereDistCalc.Haversine();
+    factory.planetModel = planetModel;
     this.ctx = factory.newSpatialContext();
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e80554a8/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeWGS84ModelRectRelationTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeWGS84ModelRectRelationTest.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeWGS84ModelRectRelationTest.java
index 22d7bd4..5a7b4b5 100644
--- a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeWGS84ModelRectRelationTest.java
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeWGS84ModelRectRelationTest.java
@@ -30,14 +30,16 @@ import org.locationtech.spatial4j.shape.Circle;
 import org.locationtech.spatial4j.shape.Point;
 import org.locationtech.spatial4j.shape.SpatialRelation;
 
-public class Geo3dShapeWGS84ModelRectRelationTest extends Geo3dShapeRectRelationTestCase {
+public class Geo3dShapeWGS84ModelRectRelationTest extends ShapeRectRelationTestCase {
+
+  PlanetModel planetModel = PlanetModel.WGS84;
 
   public Geo3dShapeWGS84ModelRectRelationTest() {
-    super(PlanetModel.WGS84);
     Geo3dSpatialContextFactory factory = new Geo3dSpatialContextFactory();
-    factory.planetModel = PlanetModel.WGS84;
-    //factory.distCalc = new GeodesicSphereDistCalc.Haversine();
+    factory.planetModel = planetModel;
     this.ctx = factory.newSpatialContext();
+    this.maxRadius = 178;
+    ((Geo3dShapeFactory)ctx.getShapeFactory()).setCircleAccuracy(1e-6);
   }
 
   @Test