/
config.go
181 lines (149 loc) · 3.59 KB
/
config.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package quantile
import (
"fmt"
"math"
)
const (
defaultBinLimit = 4096
defaultEps = 1.0 / 128.0
defaultMin = 1e-9
)
// A Config struct is passed around to many sketches (read-only).
type Config struct {
binLimit int
// TODO: interpolation type enum (e.g. https://github.com/gee-go/util/blob/ec29b7754/vec/quantile.go#L13-L29)
gamma struct {
// ln is the natural log of v, used to speed up calculating log base gamma
v, ln float64
}
norm struct {
// min and max values representable by a sketch with these params.
//
// key(x) =
// 0 : -min > x < min
// 1 : x == min
// -1 : x == -min
// +Inf : x > max
// -Inf : x < -max.
min, max float64
emin int
// bias of the exponent, used to ensure key(x) >= 1
bias int
}
}
// MaxCount returns the max number of values you can insert.
// This is limited by using a uint16 for bin.n
func (c *Config) MaxCount() int {
return c.binLimit * math.MaxUint16
}
// f64 returns the lower bound for the given key: γ^k
func (c *Config) f64(k Key) float64 {
switch {
case k < 0:
return -c.f64(-k)
case k.IsInf():
return math.Inf(int(k))
case k == 0:
return 0
}
exp := float64(int(k) - c.norm.bias)
return c.powGamma(exp)
}
func (c *Config) binLow(k Key) float64 {
switch {
case k < 0:
return -c.f64(-k)
case k.IsInf():
return math.Inf(int(k))
case k == 0:
return 0
}
exp := float64(int(k) - c.norm.bias)
return c.powGamma(exp)
}
// key returns a value k such that:
// γ^k <= v < γ^(k+1)
func (c *Config) key(v float64) Key {
switch {
case v < 0:
return -c.key(-v)
case v == 0, v > 0 && v < c.norm.min, v < 0 && v > -c.norm.min:
return 0
}
// RoundToEven is used so that key(f64(k)) == k.
i := int(math.RoundToEven(c.logGamma(v))) + c.norm.bias
if i > maxKey {
return InfKey(1)
}
if i < 1 {
// this should not happen, but better be safe than sorry
return Key(1)
}
return Key(i)
}
func (c *Config) logGamma(v float64) float64 {
return math.Log(v) / c.gamma.ln
}
func (c *Config) powGamma(y float64) float64 {
return math.Pow(c.gamma.v, y)
}
// Default returns the default config.
func Default() *Config {
c, err := NewConfig(0, 0, 0)
if err != nil {
panic(err)
}
return c
}
func (c *Config) refresh(eps, minValue float64) error {
// (1) gamma
// TODO: Calc via epsilon:
// (1) gamma.v = 1 + 2 * eps
// (2) gamma.log = math.Log1p(2 * eps) // more accurate for numbers close to 1.
switch {
case eps == 0:
eps = defaultEps
case eps > 1 || eps < 0:
return fmt.Errorf("%g: eps must be between 0 and 1", eps)
}
eps *= 2
c.gamma.v = 1 + eps
c.gamma.ln = math.Log1p(eps)
// (2) norm
// pick the next smaller power of gamma for min
// this value will have a key of 1.
switch {
case minValue == 0:
minValue = defaultMin
case minValue < 0:
return fmt.Errorf("%g: min must be > 0", minValue)
}
emin := int(math.Floor(c.logGamma(minValue)))
// TODO: error when bias doesn't fit in an int16
c.norm.bias = -emin + 1
c.norm.emin = emin
// TODO: double check c.key(c.f64(1)) == 1
c.norm.min = c.f64(1)
c.norm.max = c.f64(maxKey)
// sanity check, should never happen
if c.norm.min > minValue {
return fmt.Errorf("%g > %g", c.norm.min, minValue)
}
return nil
}
// NewConfig creates a config object with.
// TODO|DOC: describe params
func NewConfig(eps, min float64, binLimit int) (*Config, error) {
c := &Config{}
switch {
case binLimit == 0:
binLimit = defaultBinLimit
case binLimit < 0:
return nil, fmt.Errorf("binLimit can't be negative: %d", binLimit)
}
c.binLimit = binLimit
if err := c.refresh(eps, min); err != nil {
return nil, err
}
return c, nil
}