blob: 937f62a76c94e53adfc5d9713c22d68442019093 [file] [log] [blame]
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package stats
import "math"
// LogHist is a Histogram with logarithmically-spaced bins.
type LogHist struct {
b int
m float64
mOverLogb float64
low, high uint
bins []uint
}
// NewLogHist returns an empty logarithmic histogram with bins for
// integral values of m * log_b(x) up to x = max.
func NewLogHist(b int, m float64, max float64) *LogHist {
// TODO(austin) Minimum value as well? If the samples are
// actually integral, having fractional bin boundaries can
// mess up smoothing.
mOverLogb := m / math.Log(float64(b))
nbins := int(math.Ceil(mOverLogb * math.Log(max)))
return &LogHist{b: b, m: m, mOverLogb: mOverLogb, low: 0, high: 0, bins: make([]uint, nbins)}
}
func (h *LogHist) bin(x float64) int {
return int(h.mOverLogb * math.Log(x))
}
func (h *LogHist) Add(x float64) {
bin := h.bin(x)
if bin < 0 {
h.low++
} else if bin >= len(h.bins) {
h.high++
} else {
h.bins[bin]++
}
}
func (h *LogHist) Counts() (uint, []uint, uint) {
return h.low, h.bins, h.high
}
func (h *LogHist) BinToValue(bin float64) float64 {
return math.Pow(float64(h.b), bin/h.m)
}
func (h *LogHist) At(x float64) float64 {
bin := h.bin(x)
if bin < 0 || bin >= len(h.bins) {
return 0
}
return float64(h.bins[bin])
}
func (h *LogHist) Bounds() (float64, float64) {
// XXX Plot will plot this on a linear axis. Maybe this
// should be able to return the natural axis?
// Maybe then we could also give it the bins for the tics.
lowbin := 0
if h.low == 0 {
for bin, count := range h.bins {
if count > 0 {
lowbin = bin
break
}
}
}
highbin := len(h.bins)
if h.high == 0 {
for bin := range h.bins {
if h.bins[len(h.bins)-bin-1] > 0 {
highbin = len(h.bins) - bin
break
}
}
}
return h.BinToValue(float64(lowbin)), h.BinToValue(float64(highbin))
}