Skip to content

Commit 14e69b9

Browse files
committed
fix array index out of bounds
1 parent 5bd422f commit 14e69b9

File tree

2 files changed

+50
-16
lines changed

2 files changed

+50
-16
lines changed

summary/percentiles.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,17 @@ func (s Uint64Slice) GetPercentile(d float64) uint64 {
4444
return 0
4545
}
4646
sort.Sort(s)
47+
if d == 1.0 {
48+
return s[count-1]
49+
}
4750
n := float64(d * (float64(count) + 1))
4851
idx, frac := math.Modf(n)
4952
index := int(idx)
53+
54+
if index < 1 {
55+
index = 1
56+
}
57+
5058
percentile := float64(s[index-1])
5159
if index > 1 && index < count {
5260
percentile += frac * float64(s[index]-s[index-1])

summary/percentiles_test.go

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,48 @@ func assertPercentile(t *testing.T, s Uint64Slice, f float64, want uint64) {
3030
}
3131

3232
func TestPercentile(t *testing.T) {
33-
N := 100
34-
s := make(Uint64Slice, 0, N)
35-
for i := N; i > 0; i-- {
36-
s = append(s, uint64(i))
37-
}
38-
assertPercentile(t, s, 0.2, 20)
39-
assertPercentile(t, s, 0.7, 70)
40-
assertPercentile(t, s, 0.9, 90)
41-
N = 105
42-
for i := 101; i <= N; i++ {
43-
s = append(s, uint64(i))
44-
}
45-
// 90p should be between 94 and 95. Promoted to 95.
46-
assertPercentile(t, s, 0.2, 21)
47-
assertPercentile(t, s, 0.7, 74)
48-
assertPercentile(t, s, 0.9, 95)
33+
tests := []struct {
34+
name string
35+
data Uint64Slice
36+
p float64
37+
want uint64
38+
}{
39+
40+
{"20p of 100 elements", generateSlice(100, true), 0.2, 20},
41+
{"70p of 100 elements", generateSlice(100, true), 0.7, 70},
42+
{"90p of 100 elements", generateSlice(100, true), 0.9, 90},
43+
44+
// 90p should be between 94 and 95. Promoted to 95.
45+
{"20p of 105 elements", generateSlice(105, true), 0.2, 21},
46+
{"70p of 105 elements", generateSlice(105, true), 0.7, 74},
47+
{"90p of 105 elements", generateSlice(105, true), 0.9, 95},
48+
49+
// boundary value
50+
{"90p of 5 elements", Uint64Slice{1, 2, 3, 4, 5}, 0.9, 5},
51+
{"Out of range p > 1", Uint64Slice{1, 2, 3, 4, 5}, 1.1, 0},
52+
{"Empty slice", Uint64Slice{}, 0, 0},
53+
{"Zero percentile", generateSlice(100, true), 0, 1},
54+
{"100p of 11 elements", Uint64Slice{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 1.0, 11},
55+
{"100p of 105 elements", generateSlice(105, true), 1.0, 105},
56+
}
57+
58+
for _, tt := range tests {
59+
t.Run(tt.name, func(t *testing.T) {
60+
assertPercentile(t, tt.data, tt.p, tt.want)
61+
})
62+
}
63+
}
64+
65+
func generateSlice(n int, descending bool) Uint64Slice {
66+
s := make(Uint64Slice, 0, n)
67+
for i := 1; i <= n; i++ {
68+
if descending {
69+
s = append(s, uint64(n-i+1))
70+
} else {
71+
s = append(s, uint64(i))
72+
}
73+
}
74+
return s
4975
}
5076

5177
func TestMean(t *testing.T) {

0 commit comments

Comments
 (0)