1// Copyright (C) 2016 Space Monkey, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//   http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15// WARNING: THE NON-M4 VERSIONS OF THIS FILE ARE GENERATED BY GO GENERATE!
16//          ONLY MAKE CHANGES TO THE M4 FILE
17//
18
19package monkit
20
21import (
22	"sort"
23)
24
25// IntDist keeps statistics about values such as
26// low/high/recent/average/quantiles. Not threadsafe. Construct with
27// NewIntDist(). Fields are expected to be read from but not written to.
28type IntDist struct {
29	// Low and High are the lowest and highest values observed since
30	// construction or the last reset.
31	Low, High int64
32
33	// Recent is the last observed value.
34	Recent int64
35
36	// Count is the number of observed values since construction or the last
37	// reset.
38	Count int64
39
40	// Sum is the sum of all the observed values since construction or the last
41	// reset.
42	Sum int64
43
44	key       SeriesKey
45	reservoir [ReservoirSize]float32
46	rng       xorshift128
47	sorted    bool
48}
49
50func initIntDist(v *IntDist, key SeriesKey) {
51	v.key = key
52	v.rng = newXORShift128()
53}
54
55// NewIntDist creates a distribution of int64s.
56func NewIntDist(key SeriesKey) (d *IntDist) {
57	d = &IntDist{}
58	initIntDist(d, key)
59	return d
60}
61
62// Insert adds a value to the distribution, updating appropriate values.
63func (d *IntDist) Insert(val int64) {
64	if d.Count != 0 {
65		if val < d.Low {
66			d.Low = val
67		}
68		if val > d.High {
69			d.High = val
70		}
71	} else {
72		d.Low = val
73		d.High = val
74	}
75	d.Recent = val
76	d.Sum += val
77
78	index := d.Count
79	d.Count += 1
80
81	if index < ReservoirSize {
82		d.reservoir[index] = float32(val)
83		d.sorted = false
84	} else {
85		window := d.Count
86		// careful, the capitalization of Window is important
87		if Window > 0 && window > Window {
88			window = Window
89		}
90		// fast, but kind of biased. probably okay
91		j := d.rng.Uint64() % uint64(window)
92		if j < ReservoirSize {
93			d.reservoir[int(j)] = float32(val)
94			d.sorted = false
95		}
96	}
97}
98
99// FullAverage calculates and returns the average of all inserted values.
100func (d *IntDist) FullAverage() int64 {
101	if d.Count > 0 {
102		return d.Sum / int64(d.Count)
103	}
104	return 0
105}
106
107// ReservoirAverage calculates the average of the current reservoir.
108func (d *IntDist) ReservoirAverage() int64 {
109	amount := ReservoirSize
110	if d.Count < int64(amount) {
111		amount = int(d.Count)
112	}
113	if amount <= 0 {
114		return 0
115	}
116	var sum float32
117	for i := 0; i < amount; i++ {
118		sum += d.reservoir[i]
119	}
120	return int64(sum / float32(amount))
121}
122
123// Query will return the approximate value at the given quantile from the
124// reservoir, where 0 <= quantile <= 1.
125func (d *IntDist) Query(quantile float64) int64 {
126	rlen := int(ReservoirSize)
127	if int64(rlen) > d.Count {
128		rlen = int(d.Count)
129	}
130
131	if rlen < 2 {
132		return int64(d.reservoir[0])
133	}
134
135	reservoir := d.reservoir[:rlen]
136	if !d.sorted {
137		sort.Sort(float32Slice(reservoir))
138		d.sorted = true
139	}
140
141	if quantile <= 0 {
142		return int64(reservoir[0])
143	}
144	if quantile >= 1 {
145		return int64(reservoir[rlen-1])
146	}
147
148	idx_float := quantile * float64(rlen-1)
149	idx := int(idx_float)
150
151	diff := idx_float - float64(idx)
152	prior := float64(reservoir[idx])
153	return int64(prior + diff*(float64(reservoir[idx+1])-prior))
154}
155
156// Copy returns a full copy of the entire distribution.
157func (d *IntDist) Copy() *IntDist {
158	cp := *d
159	cp.rng = newXORShift128()
160	return &cp
161}
162
163func (d *IntDist) Reset() {
164	d.Low, d.High, d.Recent, d.Count, d.Sum = 0, 0, 0, 0, 0
165	// resetting count will reset the quantile reservoir
166}
167
168func (d *IntDist) Stats(cb func(key SeriesKey, field string, val float64)) {
169	count := d.Count
170	cb(d.key, "count", float64(count))
171	if count > 0 {
172		cb(d.key, "sum", d.toFloat64(d.Sum))
173		cb(d.key, "min", d.toFloat64(d.Low))
174		cb(d.key, "avg", d.toFloat64(d.FullAverage()))
175		cb(d.key, "max", d.toFloat64(d.High))
176		cb(d.key, "rmin", d.toFloat64(d.Query(0)))
177		cb(d.key, "ravg", d.toFloat64(d.ReservoirAverage()))
178		cb(d.key, "r10", d.toFloat64(d.Query(.1)))
179		cb(d.key, "r50", d.toFloat64(d.Query(.5)))
180		cb(d.key, "r90", d.toFloat64(d.Query(.9)))
181		cb(d.key, "rmax", d.toFloat64(d.Query(1)))
182		cb(d.key, "recent", d.toFloat64(d.Recent))
183	}
184}
185