Index: src/java/org/apache/lucene/search/Similarity.java
===================================================================
--- src/java/org/apache/lucene/search/Similarity.java	(revision 813992)
+++ src/java/org/apache/lucene/search/Similarity.java	(working copy)
@@ -29,17 +29,203 @@
 import java.util.IdentityHashMap;
 import java.util.Iterator;
 
-/** Expert: Scoring API.
- * <p>Subclasses implement search scoring.
- *
- * <p>The score of query <code>q</code> for document <code>d</code> correlates to the
- * cosine-distance or dot-product between document and query vectors in a
+/** 
+ * Expert: Scoring API.
+ * 
+ * <p>Similarity defines the components of Lucene scoring.
+ * Overriding computation of these components is a convenient
+ * way to alter Lucene scoring.
+ * 
+ * <p>Suggested reading:
+ * <a href="http://nlp.stanford.edu/IR-book/html/htmledition/queries-as-vectors-1.html">
+ * Introduction To Information Retrieval, Chapter 6</a>.
+ * 
+ * <p>The following describes how Lucene scoring evolves from
+ * underlying information retrieval models to (efficient) implementation.
+ * 
+ * <p>Lucene combines
+ * <a href="http://en.wikipedia.org/wiki/Standard_Boolean_model">
+ * Boolean model (BM) of Information Retrieval</a>
+ * with
  * <a href="http://en.wikipedia.org/wiki/Vector_Space_Model">
- * Vector Space Model (VSM) of Information Retrieval</a>.
- * A document whose vector is closer to the query vector in that model is scored higher.
- *
- * The score is computed as follows:
- *
+ * Vector Space Model (VSM) of Information Retrieval</a> -
+ * documents "approved" by BM are scored by VSM.
+ * 
+ * <p>In VSM, documents and queries are represented as
+ * weighted vectors in a multi-dimensional space,
+ * where each distinct index term is a dimension,
+ * and weights are
+ * <a href="http://en.wikipedia.org/wiki/Tfidf">Tf-idf</a> values.
+ * 
+ * <p>It should be noted that VSM does not require weights to be Tf-idf values,
+ * but Tf-idf values are believed to produce search results of high quality,
+ * and so Lucene is using Tf-idf.
+ * The implementation of Tf and Idf in Lucene is discussed in more detail below,
+ * but for now, for completion, let's just say that
+ * Tf(t,x) is proportional to the number of occurrences of term t in x
+ * (x is either a document or a query) and
+ * idf(t) is proportional to the inverse of the
+ * number of index documents containing term t.
+ * 
+ * <p>VSM score of document d for query q is the
+ * <a href="http://en.wikipedia.org/wiki/Cosine_similarity">
+ * Cosine Similarity</a>
+ * of the weighted query vectors V(q) and V(d):
+ * 
+ *      <br>&nbsp;<br>
+ *      <table cellpadding="2" cellspacing="2" border="0" align="center">
+ *        <tr>
+ *          <td valign="middle" align="right" rowspan="1">
+ *            cosine-similarity(q,d) &nbsp; = &nbsp;
+ *          </td>
+ *          <td valign="middle" align="center">
+ *            <table>
+ *               <tr><td align="center"><small>V(q)&nbsp;&middot;&nbsp;V(d)</small></td></tr>
+ *               <tr><td align="center">&ndash;&ndash;&ndash;&ndash;&ndash;&ndash;&ndash;&ndash;&ndash;</td></tr>
+ *               <tr><td align="center"><small>|V(q)|&nbsp;|V(d)|</small></td></tr>
+ *            </table>
+ *          </td>
+ *        </tr>
+ *      </table>
+ *      <br>&nbsp;<br>
+ * 
+ * Where V(q) &middot; V(d) is the
+ * <a href="http://en.wikipedia.org/wiki/Dot_product">dot product</a>
+ * of the weighted vectors,
+ * and |V(q)| and |V(d)| are their
+ * <a href="http://en.wikipedia.org/wiki/Euclidean_norm#Euclidean_norm">Euclidean norms</a>.
+ * 
+ * <p>Note: the above equation can be viewed as the dot product of
+ * the normalized weighted vectors, in the sense that dividing
+ * V(q) by its euclidean norm is normalizing it to a unit vector.
+ * 
+ * <p>This is, in principle, the scoring function of Lucene.
+ * However there are a few refinements, for both search quality and usability:
+ * <ul>
+ *  <li>In order to prevent large documents from "taking over" just because
+ *  a very large document may match all terms of the query, it is common
+ *  practice to "punish" very large documents proportional to their length.
+ *  For this, the score of each document is also multiplied by a document
+ *  normalization doc-len-norm(d).
+ *  </li>
+ * 
+ *  <li>At indexing, users can specify that certain documents are more
+ *  important than others, by assigning a document boost.
+ *  For this, the score of each document is also multiplied by its boost value
+ *  doc-boost(d).
+ *  </li>
+ * 
+ *  <li>Lucene is field based, hence each query term applies to a single
+ *  field, document length normalization is by the length of the certain field,
+ *  and document boost actually is a document field boost.
+ *  </li>
+ * 
+ *  <li>The same field can be added to a document during indexing several time,
+ *  and so the boost of that field is the multiplication of the boosts of
+ *  the separate additions (or parts) of that field within the document.
+ *  </li>
+ * 
+ *  <li>At search time users can specify boosts to each query, sub-query, and
+ *  each query, hence the contribution of a query term to the score of
+ *  a document is multiplied by the boost of that query term.
+ *  </li>
+ * 
+ *  <li>A document may match a multi term query without containing all
+ *  the terms of that query (this is correct for some of the queries),
+ *  and users can further reward documents matching more query terms
+ *  through the coord-factor, which is usually larger when
+ *  more terms are matched, and is multiplied by the score as well.
+ *  </li>
+ * </ul>
+ * 
+ * <p>Under the simplifying assumption of a single field in the index,
+ * here is the resulted scoring formula:
+ * 
+ *      <br>&nbsp;<br>
+ *      <table cellpadding="2" cellspacing="2" border="0" align="center">
+ *        <tr>
+ *          <td valign="middle" align="right" rowspan="1">
+ *            score(q,d) &nbsp; = &nbsp;
+ *            <font color="#FF9933">coord-factor(q,d)</font> &middot; &nbsp;
+ *            <font color="#FFCC00">query-boost(q)</font> &middot; &nbsp;
+ *          </td>
+ *          <td valign="middle" align="center">
+ *            <table>
+ *               <tr><td align="center"><small><font color="#993399">V(q)&nbsp;&middot;&nbsp;V(d)</font></small></td></tr>
+ *               <tr><td align="center">&ndash;&ndash;&ndash;&ndash;&ndash;&ndash;&ndash;&ndash;&ndash;</td></tr>
+ *               <tr><td align="center"><small><font color="#FF33CC">|V(q)|</font>&nbsp;<font color="#FF0033">|V(d)|</font></small></td></tr>
+ *            </table>
+ *          </td>
+ *          <td valign="middle" align="right" rowspan="1">
+ *            &nbsp; &middot; &nbsp; <font color="#00CCFF">doc-len-norm(d)</font>
+ *            &nbsp; &middot; &nbsp; <font color="#00CCFF">doc-boost(d)</font>
+ *          </td>
+ *        </tr>
+ *      </table>
+ *      <br>&nbsp;<br>
+ * 
+ * <p>The above formula is a simplification in the sense that (1) terms and documents
+ * are fielded and (2) boosts are usually per query term rather than per query.
+ * 
+ * <p>Lucene evidently omits some of these components from the scoring
+ * computation for performance considerations.
+ * In addition, some scoring components are computed in advance,
+ * and further aggregated in advance for an efficient score computation
+ * for each matching document:
+ * 
+ * <ul>
+ *  <li>Query-boost for the query (actually for each query term)
+ *  is known when search starts.
+ *  </li>
+ * 
+ *  <li>Query Euclidean norm |V(q)| can be computed when search starts,
+ *  as it is independent of the document being scored.
+ *  From search optimization perscoective, it is a valid question
+ *  why bother to normalize the query at all, because all
+ *  scored documents will be multiplied by the same |V(q)|,
+ *  and hence documents ranks (their order by score) will not
+ *  be affected by this normalization.
+ *  There are two good reasons to keep this normalization:
+ *  <ul>
+ *   <li>Recall that
+ *   <a href="http://en.wikipedia.org/wiki/Cosine_similarity">
+ *   Cosine Similarity</a> can be used find how similar
+ *   two documents are. One can use Lucene for e.g.
+ *   clustering, and use a document as a query to compute
+ *   its similarity to other documents.
+ *   In this use case it is important that the score of document d3
+ *   for query d1 is comparable to the score of document d3
+ *   for query d2. In other words, scores of a document for two
+ *   distinct queries should be comparable.
+ *   There are other applications that may require this.
+ *   And this is exactly what normalizing the query vector V(q)
+ *   provides: comparability (to a certain extent) of two or more queries.
+ *   </li>
+ * 
+ *   <li>Applying query normalization on the scores helps to keep the
+ *   scores arround the unit vector, hence preventing loss of score data
+ *   because of floating point precision limitations.
+ *   </li>
+ *  </ul>
+ *  </li>
+ * 
+ *  <li>Document Euclidean norm |V(d)| is excluded by Lucene,
+ *  for indexing performance considerstions (?).
+ *  </li>
+ * 
+ *  <li>Document length norm doc-len-norm(d) and document
+ *  boost doc-bost(d) are known at indexing time.
+ *  They are computed in advance and their multiplication
+ *  is saved as a single value in the index: norm(d).
+ *  (In the equations below, norm(t in d) means norm(field(t) in doc d)
+ *  where field(t) is the field associated with term t.)
+ *  </li>
+ * </ul>
+ * 
+ * <p>Lucene's actual scoring function is derived from the above.
+ * The color codes demonstrate how actual scoring function components
+ * relate to those of the theoretical scoring function:
+ * 
  * <P>
  * <table cellpadding="1" cellspacing="0" border="1" align="center">
  * <tr><td>
@@ -47,18 +233,18 @@
  *  <tr>
  *    <td valign="middle" align="right" rowspan="1">
  *      score(q,d) &nbsp; = &nbsp;
- *      <A HREF="#formula_coord">coord(q,d)</A> &nbsp;&middot;&nbsp;
- *      <A HREF="#formula_queryNorm">queryNorm(q)</A> &nbsp;&middot;&nbsp;
+ *      <A HREF="#formula_coord"><font color="#FF9933">coord(q,d)</font></A> &nbsp;&middot;&nbsp;
+ *      <A HREF="#formula_queryNorm"><font color="#FF33CC">queryNorm(q)</font></A> &nbsp;&middot;&nbsp;
  *    </td>
  *    <td valign="bottom" align="center" rowspan="1">
  *      <big><big><big>&sum;</big></big></big>
  *    </td>
  *    <td valign="middle" align="right" rowspan="1">
  *      <big><big>(</big></big>
- *      <A HREF="#formula_tf">tf(t in d)</A> &nbsp;&middot;&nbsp;
- *      <A HREF="#formula_idf">idf(t)</A><sup>2</sup> &nbsp;&middot;&nbsp;
- *      <A HREF="#formula_termBoost">t.getBoost()</A>&nbsp;&middot;&nbsp;
- *      <A HREF="#formula_norm">norm(t,d)</A>
+ *      <A HREF="#formula_tf"><font color="#993399">tf(t in d)</font></A> &nbsp;&middot;&nbsp;
+ *      <A HREF="#formula_idf"><font color="#993399">idf(t)</font></A><sup>2</sup> &nbsp;&middot;&nbsp;
+ *      <A HREF="#formula_termBoost"><font color="#FFCC00">t.getBoost()</font></A>&nbsp;&middot;&nbsp;
+ *      <A HREF="#formula_norm"><font color="#00CCFF">norm(t,d)</font></A>
  *      <big><big>)</big></big>
  *    </td>
  *  </tr>
@@ -70,7 +256,7 @@
  * </table>
  * </td></tr>
  * </table>
- *
+ * 
  * <p> where
  * <ol>
  *    <li>
@@ -79,9 +265,13 @@
  *      correlates to the term's <i>frequency</i>,
  *      defined as the number of times term <i>t</i> appears in the currently scored document <i>d</i>.
  *      Documents that have more occurrences of a given term receive a higher score.
+ *      Note that tf(t in q) is assumed to be 1 for and there for it not appear in this equation,
+ *      However if a qurey contains twice the same term, there will be 
+ *      two term-queries with that same term and hence the computation would still be correct (although
+ *      not very efficient).
  *      The default computation for <i>tf(t in d)</i> in
  *      {@link org.apache.lucene.search.DefaultSimilarity#tf(float) DefaultSimilarity} is:
- *
+ * 
  *      <br>&nbsp;<br>
  *      <table cellpadding="2" cellspacing="2" border="0" align="center">
  *        <tr>
@@ -95,16 +285,17 @@
  *      </table>
  *      <br>&nbsp;<br>
  *    </li>
- *
+ * 
  *    <li>
  *      <A NAME="formula_idf"></A>
  *      <b>idf(t)</b> stands for Inverse Document Frequency. This value
  *      correlates to the inverse of <i>docFreq</i>
  *      (the number of documents in which the term <i>t</i> appears).
  *      This means rarer terms give higher contribution to the total score.
+ *      idf(t) appears for t in both the query and the document, and there it is squared in the equation. 
  *      The default computation for <i>idf(t)</i> in
  *      {@link org.apache.lucene.search.DefaultSimilarity#idf(int, int) DefaultSimilarity} is:
- *
+ * 
  *      <br>&nbsp;<br>
  *      <table cellpadding="2" cellspacing="2" border="0" align="center">
  *        <tr>
@@ -128,7 +319,7 @@
  *      </table>
  *      <br>&nbsp;<br>
  *    </li>
- *
+ * 
  *    <li>
  *      <A NAME="formula_coord"></A>
  *      <b>coord(q,d)</b>
@@ -140,7 +331,7 @@
  *      by the Similarity in effect at search time.
  *      <br>&nbsp;<br>
  *    </li>
- *
+ * 
  *    <li><b>
  *      <A NAME="formula_queryNorm"></A>
  *      queryNorm(q)
@@ -149,7 +340,7 @@
  *      This factor does not affect document ranking (since all ranked documents are multiplied by the same factor),
  *      but rather just attempts to make scores from different queries (or even different indexes) comparable.
  *      This is a search time factor computed by the Similarity in effect at search time.
- *
+ * 
  *      The default computation in
  *      {@link org.apache.lucene.search.DefaultSimilarity#queryNorm(float) DefaultSimilarity}
  *      is:
@@ -173,12 +364,12 @@
  *        </tr>
  *      </table>
  *      <br>&nbsp;<br>
- *
+ * 
  *      The sum of squared weights (of the query terms) is
  *      computed by the query {@link org.apache.lucene.search.Weight} object.
  *      For example, a {@link org.apache.lucene.search.BooleanQuery boolean query}
  *      computes this value as:
- *
+ * 
  *      <br>&nbsp;<br>
  *      <table cellpadding="1" cellspacing="0" border="0"n align="center">
  *        <tr>
@@ -204,9 +395,9 @@
  *        </tr>
  *      </table>
  *      <br>&nbsp;<br>
- *
+ * 
  *    </li>
- *
+ * 
  *    <li>
  *      <A NAME="formula_termBoost"></A>
  *      <b>t.getBoost()</b>
@@ -222,11 +413,11 @@
  *      {@link org.apache.lucene.search.Query#getBoost() getBoost()}.
  *      <br>&nbsp;<br>
  *    </li>
- *
+ * 
  *    <li>
  *      <A NAME="formula_norm"></A>
  *      <b>norm(t,d)</b> encapsulates a few (indexing time) boost and length factors:
- *
+ * 
  *      <ul>
  *        <li><b>Document boost</b> - set by calling
  *        {@link org.apache.lucene.document.Document#setBoost(float) doc.setBoost()}
@@ -242,11 +433,11 @@
  *        LengthNorm is computed by the Similarity class in effect at indexing.
  *        </li>
  *      </ul>
- *
+ * 
  *      <p>
  *      When a document is added to the index, all the above factors are multiplied.
  *      If the document has multiple fields with the same name, all their boosts are multiplied together:
- *
+ * 
  *      <br>&nbsp;<br>
  *      <table cellpadding="1" cellspacing="0" border="0"n align="center">
  *        <tr>
