package stati import ( "math" "golang.org/x/xerrors" ) type Histogram struct { Buckets []float64 Counts []uint64 } // NewHistogram creates a histograme with buckets defined as: // {x > -Inf, x >= buckets[0], x >= buckets[1], ..., x >= buckets[i]} func NewHistogram(buckets []float64) (*Histogram, error) { if len(buckets) == 0 { return nil, xerrors.Errorf("empty buckets") } prev := buckets[0] for i, v := range buckets[1:] { if v < prev { return nil, xerrors.Errorf("bucket at index %d is smaller than previous %f < %f", i+1, v, prev) } prev = v } h := &Histogram{ Buckets: append([]float64{math.Inf(-1)}, buckets...), Counts: make([]uint64, len(buckets)+1), } return h, nil } func (h *Histogram) Observe(x float64) { for i, b := range h.Buckets { if x >= b { h.Counts[i]++ } else { break } } } func (h *Histogram) Total() uint64 { return h.Counts[0] } func (h *Histogram) Get(i int) uint64 { if i >= len(h.Counts)-2 { return h.Counts[i] } return h.Counts[i+1] - h.Counts[i+2] } func (h *Histogram) GetRatio(i int) float64 { return float64(h.Get(i)) / float64(h.Total()) }