-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathbuckets.js
203 lines (162 loc) · 5.75 KB
/
buckets.js
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright (c) 2018, Joyent, Inc.
*/
var assert = require('assert-plus');
// These are all the different bucket values node-artedi 1.x could generate
// which it called "log/linear" though these values are not compatible with
// other log/linear quantizations such as llquantize in DTrace.
var POSSIBLE_ARTEDI_1_BUCKETS = [
0.0001, 0.0002, 0.0003, 0.0004, 0.0005, 0.0006, 0.0007, 0.0008, 0.0009,
0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.007, 0.008, 0.009,
0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09,
0.18, 0.27, 0.36, 0.45, 0.54, 0.63, 0.72, 0.81,
1.62, 2.43, 3.24, 4.05, 4.86, 5.67, 6.48, 7.29, 8.1,
25, 42, 59, 76,
228, 380, 532, 684,
2052, 3420, 4788, 6156,
18468, 30780, 43092, 55404,
166212, 277020, 387828, 498636,
1495908, 2493180, 3490452, 4487724,
13463172, 22438620, 31414068, 40389516,
121168548, 201947580, 282726612, 363505644,
1090516932, 1817528220, 2544539508, 3271550796 ];
var POSSIBLE_ARTEDI_1_BUCKETS_MIN_MIN = POSSIBLE_ARTEDI_1_BUCKETS[0];
var POSSIBLE_ARTEDI_1_BUCKETS_MAX_MIN =
POSSIBLE_ARTEDI_1_BUCKETS[POSSIBLE_ARTEDI_1_BUCKETS.length - 2];
var POSSIBLE_ARTEDI_1_BUCKETS_MAX_MAX =
POSSIBLE_ARTEDI_1_BUCKETS[POSSIBLE_ARTEDI_1_BUCKETS.length - 1];
function fixDecimals(value) {
var fixed;
// keep 4 decimal places when < 10, otherwise use integers.
if (value < 10) {
fixed = +(value.toFixed(4));
} else {
fixed = Math.ceil(value);
}
return (fixed);
}
function artedi1Buckets(min, max) {
if (max === undefined) {
max = POSSIBLE_ARTEDI_1_BUCKETS_MAX_MAX;
}
if (min === undefined) {
min = POSSIBLE_ARTEDI_1_BUCKETS_MIN_MIN;
}
assert.number(min, 'min', 'min is required');
assert.number(max, 'max', 'max is required');
assert.ok(min < max, 'min must be < max');
assert.ok(min <= POSSIBLE_ARTEDI_1_BUCKETS_MAX_MIN, 'min must be <= ' +
POSSIBLE_ARTEDI_1_BUCKETS_MAX_MIN);
assert.ok(max <= POSSIBLE_ARTEDI_1_BUCKETS_MAX_MAX, 'max must be <= ' +
POSSIBLE_ARTEDI_1_BUCKETS_MAX_MAX);
var begin;
var buckets = [];
var i;
var end;
// Shortcut in case they want everything
if (min === POSSIBLE_ARTEDI_1_BUCKETS_MIN_MIN &&
max === POSSIBLE_ARTEDI_1_BUCKETS_MAX_MAX) {
return (POSSIBLE_ARTEDI_1_BUCKETS.slice());
}
// find the lowest bucket that's >= min
i = 0;
while (POSSIBLE_ARTEDI_1_BUCKETS[i] < min) {
i++;
}
begin = i;
// find the highest bucket that's >= max
if (max === POSSIBLE_ARTEDI_1_BUCKETS_MAX_MAX) {
end = POSSIBLE_ARTEDI_1_BUCKETS.length - 1;
} else {
// work backward from the end
i = POSSIBLE_ARTEDI_1_BUCKETS.length - 1;
while (POSSIBLE_ARTEDI_1_BUCKETS[i] > max) {
i--;
}
end = i + 1;
}
return (POSSIBLE_ARTEDI_1_BUCKETS.slice(begin, end + 1));
}
function linearBuckets(min, width, count) {
var buckets = [];
var i;
var n = min;
assert.number(min, 'min', 'min is required');
assert.number(width, 'width', 'width is required');
assert.number(count, 'count', 'count is required');
assert.ok(width > 0, 'width must be > 0');
// TODO:
// - sanity limit on count?
for (i = 0; i < count; i++) {
buckets.push(fixDecimals(n));
n += width;
}
return (buckets);
}
function exponentialBuckets(min, factor, count) {
var buckets = [];
var i;
var n = min;
var step;
assert.number(min, 'min', 'min is required');
assert.number(factor, 'factor', 'factor is required');
assert.number(count, 'count', 'count is required');
assert.ok(min > 0, 'min must be > 0');
// TODO
// - limit on factor?
// - sanity limit on count?
for (i = 0; i < count; i++) {
buckets.push(fixDecimals(n));
n *= factor;
}
return (buckets);
}
function logLinearBuckets(base, lowPower, highPower, bucketsPerMagnitude) {
var bucketIdx;
var buckets = [];
var curBucket;
var curMagnitudeLast;
var curSteps;
var exponent;
var prevMagnitudeLast = 0;
assert.number(base, 'base');
assert.number(lowPower, 'lowPower');
assert.number(highPower, 'highPower');
assert.number(bucketsPerMagnitude, 'bucketsPerMagnitude');
assert.ok(lowPower < highPower, 'lowPower must be < highPower');
assert.ok(bucketsPerMagnitude >= base, 'bucketsPerMagnitude must be >= base');
assert.ok(bucketsPerMagnitude % base === 0,
'base must be a factor of bucketsPerMagnitude');
// TODO:
//
// - ensure bucketsPerMagnitude divides evenly into a power of base (that's what skinner requires anyway)
// - other checks?
//
for (exponent = lowPower; exponent <= highPower; exponent++) {
// Start where we left off in the last magnitude
curBucket = prevMagnitudeLast;
// This will be the largest bucket in this magnitude
curMagnitudeLast = Math.pow(base, exponent + 1);
step = curMagnitudeLast / bucketsPerMagnitude;
if (bucketsPerMagnitude > curMagnitudeLast) {
step = Math.pow(base, exponent);
}
curSteps = ((curMagnitudeLast - curBucket) / step);
for (bucketIdx = 0; bucketIdx < curSteps; bucketIdx++) {
curBucket += step;
buckets.push(fixDecimals(curBucket));
}
prevMagnitudeLast = curMagnitudeLast;
}
return (buckets);
}
module.exports = {
artedi1Buckets: artedi1Buckets,
exponentialBuckets: exponentialBuckets,
linearBuckets: linearBuckets,
logLinearBuckets: logLinearBuckets
};