// 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)) }