func (ds *DefaultSimilarity) lengthNorm(state *index.FieldInvertState) float32 {
	var numTerms int
	if ds.discountOverlaps {
		numTerms = state.Length() - state.NumOverlap()
	} else {
		numTerms = state.Length()
	}
	return state.Boost() * float32(1.0/math.Sqrt(float64(numTerms)))
}
/*
Implemented as state.boost() * lengthNorm(numTerms), where numTerms
is FieldInvertState.length() if setDiscountOverlaps() is false, else
it's FieldInvertState.length() - FieldInvertState.numOverlap().
package search
import (
	"fmt"
	. "github.com/balzaczyy/golucene/core/codec/spi"
	"github.com/balzaczyy/golucene/core/index"
	"github.com/balzaczyy/golucene/core/util"
	"log"
	"math"
)
type IndexSearcherSPI interface {
	CreateNormalizedWeight(Query) (Weight, error)
	Rewrite(Query) (Query, error)
	WrapFilter(Query, Filter) Query
	SearchLWC([]*index.AtomicReaderContext, Weight, Collector) error
}
type IndexSearcher struct {
	spi           IndexSearcherSPI
	reader        index.IndexReader
	readerContext index.IndexReaderContext
	leafContexts  []*index.AtomicReaderContext
	similarity    Similarity
}
func NewIndexSearcher(r index.IndexReader) *IndexSearcher {
	return NewIndexSearcherFromContext(r.Context())
}
func NewIndexSearcherFromContext(context index.IndexReaderContext) *IndexSearcher {
	defaultSimilarity := NewDefaultSimilarity()
	ss := &IndexSearcher{nil, context.Reader(), context, context.Leaves(), defaultSimilarity}
	ss.spi = ss
	return ss
}
func (ss *IndexSearcher) SetSimilarity(similarity Similarity) {
	ss.similarity = similarity
}
func (ss *IndexSearcher) SearchTop(q Query, n int) (topDocs TopDocs, err error) {
	return ss.Search(q, nil, n)
}
func (ss *IndexSearcher) Search(q Query, f Filter, n int) (topDocs TopDocs, err error) {
	w, err := ss.spi.CreateNormalizedWeight(ss.spi.WrapFilter(q, f))
	if err != nil {
		return TopDocs{}, err
	}
	return ss.searchWSI(w, nil, n), nil
}
func (ss *IndexSearcher) searchWSI(w Weight, after *ScoreDoc, nDocs int) TopDocs {
	return ss.searchLWSI(ss.leafContexts, w, after, nDocs)
}
func (ss *IndexSearcher) searchLWSI(leaves []*index.AtomicReaderContext,
	w Weight, after *ScoreDoc, nDocs int) TopDocs {
	limit := ss.reader.MaxDoc()
	if limit == 0 {
		limit = 1
	}
	if nDocs > limit {
		nDocs = limit
	}
	collector := NewTopScoreDocCollector(nDocs, after, !w.IsScoresDocsOutOfOrder())
	ss.spi.SearchLWC(leaves, w, collector)
	return collector.TopDocs()
}
func (ss *IndexSearcher) SearchLWC(leaves []*index.AtomicReaderContext, w Weight, c Collector) (err error) {
	for _, ctx := range leaves {  
		c.SetNextReader(ctx)
		scorer, err := w.BulkScorer(ctx, !c.AcceptsDocsOutOfOrder(),
			ctx.Reader().(index.AtomicReader).LiveDocs())
		if err != nil {
			return err
		}
		if scorer != nil {
			err = scorer.ScoreAndCollect(c)
		}  
	}
	return
}
func (ss *IndexSearcher) WrapFilter(q Query, f Filter) Query {
	if f == nil {
		return q
	}
	panic("FilteredQuery not supported yet")
}
func (ss *IndexSearcher) Explain(query Query, doc int) (exp Explanation, err error) {
	w, err := ss.spi.CreateNormalizedWeight(query)
	if err == nil {
		return ss.explain(w, doc)
	}
	return
}
func (ss *IndexSearcher) explain(weight Weight, doc int) (exp Explanation, err error) {
	n := index.SubIndex(doc, ss.leafContexts)
	ctx := ss.leafContexts[n]
	deBasedDoc := doc - ctx.DocBase
	return weight.Explain(ctx, deBasedDoc)
}
func (ss *IndexSearcher) CreateNormalizedWeight(q Query) (w Weight, err error) {
	q, err = ss.spi.Rewrite(q)
	if err != nil {
		return nil, err
	}
	log.Printf("After rewrite: %v", q)
	w, err = q.CreateWeight(ss)
	if err != nil {
		return nil, err
	}
	v := w.ValueForNormalization()
	norm := ss.similarity.QueryNorm(v)
	if math.IsInf(float64(norm), 1) || math.IsNaN(float64(norm)) {
		norm = 1.0
	}
	w.Normalize(norm, 1.0)
	return w, nil
}
func (ss *IndexSearcher) Rewrite(q Query) (Query, error) {
	log.Printf("Rewriting '%v'...", q)
	after := q.Rewrite(ss.reader)
	for after != q {
		q = after
		after = q.Rewrite(ss.reader)
	}
	return q, nil
}
func (ss *IndexSearcher) TopReaderContext() index.IndexReaderContext {
	return ss.readerContext
}
func (ss *IndexSearcher) String() string {
	return fmt.Sprintf("IndexSearcher(%v)", ss.reader)
}
func (ss *IndexSearcher) TermStatistics(term *index.Term, context *index.TermContext) TermStatistics {
	return NewTermStatistics(term.Bytes, int64(context.DocFreq), context.TotalTermFreq)
}
func (ss *IndexSearcher) CollectionStatistics(field string) CollectionStatistics {
	terms := index.GetMultiTerms(ss.reader, field)
	if terms == nil {
		return NewCollectionStatistics(field, int64(ss.reader.MaxDoc()), 0, 0, 0)
	}
	return NewCollectionStatistics(field, int64(ss.reader.MaxDoc()), int64(terms.DocCount()), terms.SumTotalTermFreq(), terms.SumDocFreq())
}
type TermStatistics struct {
	Term                   []byte
	DocFreq, TotalTermFreq int64
}
func NewTermStatistics(term []byte, docFreq, totalTermFreq int64) TermStatistics {
	return TermStatistics{term, docFreq, totalTermFreq}
}
type CollectionStatistics struct {
	field                                          string
	maxDoc, docCount, sumTotalTermFreq, sumDocFreq int64
}
func NewCollectionStatistics(field string, maxDoc, docCount, sumTotalTermFreq, sumDocFreq int64) CollectionStatistics {
	return CollectionStatistics{field, maxDoc, docCount, sumTotalTermFreq, sumDocFreq}
}
type SimScorer interface {
	Score(doc int, freq float32) float32
	explain(int, Explanation) Explanation
}
type SimWeight interface {
	ValueForNormalization() float32
	Normalize(norm float32, topLevelBoost float32)
}
type ITFIDFSimilarity interface {
	tf(freq float32) float32
	idf(docFreq int64, numDocs int64) float32
	lengthNorm(*index.FieldInvertState) float32
	decodeNormValue(norm int64) float32
	encodeNormValue(float32) int64
}
type TFIDFSimilarity struct {
	spi ITFIDFSimilarity
}
func newTFIDFSimilarity(spi ITFIDFSimilarity) *TFIDFSimilarity {
	return &TFIDFSimilarity{spi}
}
func (ts *TFIDFSimilarity) idfExplainTerm(collectionStats CollectionStatistics, termStats TermStatistics) Explanation {
	df, max := termStats.DocFreq, collectionStats.maxDoc
	idf := ts.spi.idf(df, max)
	return newExplanation(idf, fmt.Sprintf("idf(docFreq=%v, maxDocs=%v)", df, max))
}
func (ts *TFIDFSimilarity) idfExplainPhrase(collectionStats CollectionStatistics, termStats []TermStatistics) Explanation {
	details := make([]Explanation, len(termStats))
	var idf float32 = 0
	for i, stat := range termStats {
		details[i] = ts.idfExplainTerm(collectionStats, stat)
		idf += details[i].(*ExplanationImpl).value
	}
	ans := newExplanation(idf, fmt.Sprintf("idf(), sum of:"))
	ans.details = details
	return ans
}
func (ts *TFIDFSimilarity) ComputeNorm(state *index.FieldInvertState) int64 {
	return ts.spi.encodeNormValue(ts.spi.lengthNorm(state))
}
func (ts *TFIDFSimilarity) computeWeight(queryBoost float32, collectionStats CollectionStatistics, termStats ...TermStatistics) SimWeight {
	var idf Explanation
	if len(termStats) == 1 {
		idf = ts.idfExplainTerm(collectionStats, termStats[0])
	} else {
		idf = ts.idfExplainPhrase(collectionStats, termStats)
	}
	return newIDFStats(collectionStats.field, idf, queryBoost)
}
func (ts *TFIDFSimilarity) simScorer(stats SimWeight, ctx *index.AtomicReaderContext) (ss SimScorer, err error) {
	idfstats := stats.(*idfStats)
	ndv, err := ctx.Reader().(index.AtomicReader).NormValues(idfstats.field)
	if err != nil {
		return nil, err
	}
	return newTFIDFSimScorer(ts, idfstats, ndv), nil
}
type tfIDFSimScorer struct {
	owner       *TFIDFSimilarity
	stats       *idfStats
	weightValue float32
	norms       NumericDocValues
}
func newTFIDFSimScorer(owner *TFIDFSimilarity, stats *idfStats, norms NumericDocValues) *tfIDFSimScorer {
	return &tfIDFSimScorer{owner, stats, stats.value, norms}
}
func (ss *tfIDFSimScorer) Score(doc int, freq float32) float32 {
	raw := ss.owner.spi.tf(freq) * ss.weightValue  
	if ss.norms == nil {
		return raw
	}
	return raw * ss.owner.spi.decodeNormValue(ss.norms(doc))  
}
func (ss *tfIDFSimScorer) explain(doc int, freq Explanation) Explanation {
	return ss.owner.explainScore(doc, freq, ss.stats, ss.norms)
}
type idfStats struct {
	field string
	idf         Explanation
	queryNorm   float32
	queryWeight float32
	queryBoost  float32
	value       float32
}
func newIDFStats(field string, idf Explanation, queryBoost float32) *idfStats {
	return &idfStats{
		field:       field,
		idf:         idf,
		queryBoost:  queryBoost,
		queryWeight: idf.(*ExplanationImpl).value * queryBoost,  
	}
}
func (stats *idfStats) ValueForNormalization() float32 {
	return stats.queryWeight * stats.queryWeight  
}
func (stats *idfStats) Normalize(queryNorm float32, topLevelBoost float32) {
	stats.queryNorm = queryNorm * topLevelBoost
	stats.queryWeight *= stats.queryNorm                                  
	stats.value = stats.queryWeight * stats.idf.(*ExplanationImpl).value  
}
func (ss *TFIDFSimilarity) explainScore(doc int, freq Explanation,
	stats *idfStats, norms NumericDocValues) Explanation {
	boostExpl := newExplanation(stats.queryBoost, "boost")
	queryNormExpl := newExplanation(stats.queryNorm, "queryNorm")
	queryExpl := newExplanation(
		boostExpl.value*stats.idf.Value()*queryNormExpl.value,
		"queryWeight, product of:")
	if stats.queryBoost != 1 {
		queryExpl.addDetail(boostExpl)
	}
	queryExpl.addDetail(stats.idf)
	queryExpl.addDetail(queryNormExpl)
	tfExplanation := newExplanation(ss.spi.tf(freq.Value()),
		fmt.Sprintf("tf(freq=%v), with freq of:", freq.Value()))
	tfExplanation.addDetail(freq)
	fieldNorm := float32(1)
	if norms != nil {
		fieldNorm = ss.spi.decodeNormValue(norms(doc))
	}
	fieldNormExpl := newExplanation(fieldNorm, fmt.Sprintf("fieldNorm(doc=%v)", doc))
	fieldExpl := newExplanation(
		tfExplanation.value*stats.idf.Value()*fieldNormExpl.value,
		fmt.Sprintf("fieldWeight in %v, product of:", doc))
	fieldExpl.addDetail(tfExplanation)
	fieldExpl.addDetail(stats.idf)
	fieldExpl.addDetail(fieldNormExpl)
	if queryExpl.value == 1 {
		return fieldExpl
	}
	ans := newExplanation(queryExpl.value*fieldExpl.value,
		fmt.Sprintf("score(doc=%v,freq=%v), product of:", doc, freq))
	ans.addDetail(queryExpl)
	ans.addDetail(fieldExpl)
	return ans
}
var NORM_TABLE []float32 = buildNormTable()
func buildNormTable() []float32 {
	table := make([]float32, 256)
	for i, _ := range table {
		table[i] = util.Byte315ToFloat(byte(i))
	}
	return table
}
type DefaultSimilarity struct {
	*TFIDFSimilarity
	discountOverlaps bool
}
func NewDefaultSimilarity() *DefaultSimilarity {
	ans := &DefaultSimilarity{discountOverlaps: true}
	ans.TFIDFSimilarity = newTFIDFSimilarity(ans)
	return ans
}
func (ds *DefaultSimilarity) Coord(overlap, maxOverlap int) float32 {
	return float32(overlap) / float32(maxOverlap)
}
func (ds *DefaultSimilarity) QueryNorm(sumOfSquaredWeights float32) float32 {
	return float32(1.0 / math.Sqrt(float64(sumOfSquaredWeights)))
}
func (ds *DefaultSimilarity) encodeNormValue(f float32) int64 {
	return int64(util.FloatToByte315(f))
}
func (ds *DefaultSimilarity) decodeNormValue(norm int64) float32 {
	return NORM_TABLE[int(norm&0xff)]  
}