68 lines
2.0 KiB
Go
68 lines
2.0 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|||
|
|
// SPDX-License-Identifier: MIT
|
||
|
|
|
||
|
|
package stats
|
||
|
|
|
||
|
|
import (
|
||
|
|
"math"
|
||
|
|
"strings"
|
||
|
|
"testing"
|
||
|
|
)
|
||
|
|
|
||
|
|
// TestQuantile pins linear interpolation between order statistics.
|
||
|
|
func TestQuantile(t *testing.T) {
|
||
|
|
a := mustFloats(t, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 10)
|
||
|
|
q, err := Quantile(a, []float64{0.25})
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
// Linear interpolation: position 0.25*(9)=2.25 lands between 3 and 4.
|
||
|
|
if v := q.FloatAt(0); math.Abs(v-3.25) > 1e-9 {
|
||
|
|
t.Errorf("q25: %v, want 3.25", v)
|
||
|
|
}
|
||
|
|
med, _ := Quantile(a, []float64{0.5})
|
||
|
|
if v := med.FloatAt(0); math.Abs(v-5.5) > 1e-9 {
|
||
|
|
t.Errorf("median quantile: %v", v)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// TestQuantileDegenerateInputs pins the guard that used to panic:
|
||
|
|
// Quantile on an empty sample.
|
||
|
|
func TestQuantileDegenerateInputs(t *testing.T) {
|
||
|
|
empty := mustFloats(t, []float64{}, 0)
|
||
|
|
if _, err := Quantile(empty, []float64{0.5}); err == nil {
|
||
|
|
t.Error("Quantile empty: expected error")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// TestBinCounts counts values in equally wide bins.
|
||
|
|
func TestBinCounts(t *testing.T) {
|
||
|
|
vals := mustFloats(t, []float64{-3, 5, 12, 25}, 4)
|
||
|
|
counts, err := BinCounts(vals, 2)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if counts.Len() != 2 {
|
||
|
|
t.Fatalf("counts len: %d", counts.Len())
|
||
|
|
}
|
||
|
|
if c1 := counts.FloatAt(0); c1 < 1 {
|
||
|
|
t.Errorf("first bin empty: %v", counts.RawFloats())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// TestComplexStatsErrors pins the refusals for complex input: no
|
||
|
|
// ordering, so no median, standard deviation or histogram.
|
||
|
|
func TestComplexStatsErrors(t *testing.T) {
|
||
|
|
c := mustComplexes(t, []complex128{complex(1, 1)}, 1)
|
||
|
|
|
||
|
|
if _, err := Median(c); err == nil || !strings.Contains(err.Error(), "no median") {
|
||
|
|
t.Fatalf("Median complex: %v", err)
|
||
|
|
}
|
||
|
|
if _, err := Std(c); err == nil || !strings.Contains(err.Error(), "no float standard deviation") {
|
||
|
|
t.Fatalf("Std complex: %v", err)
|
||
|
|
}
|
||
|
|
if _, _, err := Histogram(c, 2); err == nil || !strings.Contains(err.Error(), "no histogram") {
|
||
|
|
t.Fatalf("Histogram complex: %v", err)
|
||
|
|
}
|
||
|
|
}
|