package stats_test
import (
"reflect"
"testing"
"github.com/montanaflynn/stats"
)
func TestPercentile(t *testing.T) {
m, _ := stats.Percentile([]float64{43, 54, 56, 61, 62, 66}, 90)
if m != 64.0 {
t.Errorf("%.1f != %.1f", m, 64.0)
}
m, _ = stats.Percentile([]float64{43}, 90)
if m != 43.0 {
t.Errorf("%.1f != %.1f", m, 43.0)
}
m, _ = stats.Percentile([]float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 50)
if m != 5.0 {
t.Errorf("%.1f != %.1f", m, 5.0)
}
m, _ = stats.Percentile([]float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 99.9)
if m != 9.5 {
t.Errorf("%.1f != %.1f", m, 9.5)
}
m, _ = stats.Percentile([]float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 100)
if m != 10.0 {
t.Errorf("%.1f != %.1f", m, 10.0)
}
_, err := stats.Percentile([]float64{}, 99.9)
if err != stats.EmptyInputErr {
t.Errorf("Empty slice didn't return expected error; got %v", err)
}
_, err = stats.Percentile([]float64{1, 2, 3, 4, 5}, 0)
if err != stats.BoundsErr {
t.Errorf("Zero percent didn't return expected error; got %v", err)
}
_, err = stats.Percentile([]float64{1, 2, 3, 4, 5}, 0.13)
if err != stats.BoundsErr {
t.Errorf("Too low percent didn't return expected error; got %v", err)
}
_, err = stats.Percentile([]float64{1, 2, 3, 4, 5}, 101)
if err != stats.BoundsErr {
t.Errorf("Too high percent didn't return expected error; got %v", err)
}
}
func TestPercentileSortSideEffects(t *testing.T) {
s := []float64{43, 54, 56, 44, 62, 66}
a := []float64{43, 54, 56, 44, 62, 66}
_, _ = stats.Percentile(s, 90)
if !reflect.DeepEqual(s, a) {
t.Errorf("%.1f != %.1f", s, a)
}
}
func BenchmarkPercentileSmallFloatSlice(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _ = stats.Percentile(makeFloatSlice(5), 50)
}
}
func BenchmarkPercentileLargeFloatSlice(b *testing.B) {
lf := makeFloatSlice(100000)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = stats.Percentile(lf, 50)
}
}
func TestPercentileNearestRank(t *testing.T) {
f1 := []float64{35, 20, 15, 40, 50}
f2 := []float64{20, 6, 7, 8, 8, 10, 13, 15, 16, 3}
f3 := makeFloatSlice(101)
for _, c := range []struct {
sample []float64
percent float64
result float64
}{
{f1, 30, 20},
{f1, 40, 20},
{f1, 50, 35},
{f1, 75, 40},
{f1, 95, 50},
{f1, 99, 50},
{f1, 99.9, 50},
{f1, 100, 50},
{f2, 25, 7},
{f2, 50, 8},
{f2, 75, 15},
{f2, 100, 20},
{f3, 1, 100},
{f3, 99, 9900},
{f3, 100, 10000},
{f3, 0, 0},
} {
got, err := stats.PercentileNearestRank(c.sample, c.percent)
if err != nil {
t.Errorf("Should not have returned an error")
}
if got != c.result {
t.Errorf("%v != %v", got, c.result)
}
}
_, err := stats.PercentileNearestRank([]float64{}, 50)
if err == nil {
t.Errorf("Should have returned an empty slice error")
}
_, err = stats.PercentileNearestRank([]float64{1, 2, 3, 4, 5}, -0.01)
if err == nil {
t.Errorf("Should have returned an percentage must be above 0 error")
}
_, err = stats.PercentileNearestRank([]float64{1, 2, 3, 4, 5}, 110)
if err == nil {
t.Errorf("Should have returned an percentage must not be above 100 error")
}
}
func BenchmarkPercentileNearestRankSmallFloatSlice(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _ = stats.PercentileNearestRank(makeFloatSlice(5), 50)
}
}
func BenchmarkPercentileNearestRankLargeFloatSlice(b *testing.B) {
lf := makeFloatSlice(100000)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = stats.PercentileNearestRank(lf, 50)
}
}