// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: MIT // Pins for the rebased-view walks: a contiguous Slice shares its // source's storage, so its payload runs past the view's own element // count, and every raw-payload walk in the package is bounded by the // visible elements. Each pin holds a view's answer against the same // computation on fresh arrays carrying nothing but the visible // elements, with junk parked in the invisible tail: a walk that reads // one payload slot past its count fails the pin, and a walk that // writes one panics outright. package stats import ( "math" "testing" "sourcedock.dev/petrbalvin/tensor/internal/core" ) // viewTailPool builds a 60-sample pool whose invisible tail, from // position 40 on, holds values no caller can see, one of them a NaN. func viewTailPool(t *testing.T) *core.Array { t.Helper() pool := make([]float64, 60) for i := range pool { pool[i] = float64(i%9) + 1 } pool[55] = math.NaN() // inside the invisible tail: must be ignored a, err := core.FromFloats(pool, len(pool)) if err != nil { t.Fatalf("FromFloats: %v", err) } return a } // viewTailHalves slices the pool's head into two disjoint views of ten, // beside fresh arrays holding exactly the visible elements. func viewTailHalves(t *testing.T) (va, vb, fa, fb *core.Array) { t.Helper() pool := viewTailPool(t) va, err := core.Slice(pool, 0, 0, 10) if err != nil { t.Fatalf("Slice: %v", err) } vb, err = core.Slice(pool, 0, 10, 20) if err != nil { t.Fatalf("Slice: %v", err) } visible := pool.RawFloats()[:20] fa, err = core.FromFloats(visible[:10], 10) if err != nil { t.Fatalf("FromFloats: %v", err) } fb, err = core.FromFloats(visible[10:], 10) if err != nil { t.Fatalf("FromFloats: %v", err) } return va, vb, fa, fb } // TestViewTailANOVAOneWay pins the analysis of variance on rebased // views: the group sums, the finite scan and the within sum of squares // read the visible elements only. func TestViewTailANOVAOneWay(t *testing.T) { va, vb, fa, fb := viewTailHalves(t) f1, p1, err1 := ANOVAOneWay([]*core.Array{va, vb}) f2, p2, err2 := ANOVAOneWay([]*core.Array{fa, fb}) if (err1 == nil) != (err2 == nil) { t.Fatalf("ANOVAOneWay on views errored %v, on fresh arrays %v", err1, err2) } if err1 == nil && (f1 != f2 || p1 != p2) { t.Fatalf("ANOVAOneWay on views = (%v, %v), fresh arrays answer (%v, %v)", f1, p1, f2, p2) } } // TestViewTailWelchTTest pins Welch's t-test on rebased views: the // sample mean and variance read the visible elements only. func TestViewTailWelchTTest(t *testing.T) { va, vb, fa, fb := viewTailHalves(t) t1, df1, p1, err1 := WelchTTest(va, vb) t2, df2, p2, err2 := WelchTTest(fa, fb) if (err1 == nil) != (err2 == nil) { t.Fatalf("WelchTTest on views errored %v, on fresh arrays %v", err1, err2) } if err1 == nil && (t1 != t2 || df1 != df2 || p1 != p2) { t.Fatalf("WelchTTest on views = (%v, %v, %v), fresh arrays answer (%v, %v, %v)", t1, df1, p1, t2, df2, p2) } } // TestViewTailMAD pins the median absolute deviation on a rebased // view: the deviation walk writes one cell per visible element, never // one per payload slot. func TestViewTailMAD(t *testing.T) { pool := viewTailPool(t) va, err := core.Slice(pool, 0, 0, 10) if err != nil { t.Fatalf("Slice: %v", err) } visible := pool.RawFloats()[:10] fa, err := core.FromFloats(visible, 10) if err != nil { t.Fatalf("FromFloats: %v", err) } got, err1 := MedianAbsoluteDeviation(va) want, err2 := MedianAbsoluteDeviation(fa) if err1 != nil || err2 != nil { t.Fatalf("MedianAbsoluteDeviation: %v against %v", err1, err2) } if got != want { t.Fatalf("MedianAbsoluteDeviation on a view = %v, fresh array answers %v", got, want) } } // TestViewTailChiSquareGoodnessOfFit pins the goodness-of-fit test on // rebased views: both frequency walks are bounded by the bin count, // where an unbounded index into the other payload was a panic before it // was a wrong statistic. func TestViewTailChiSquareGoodnessOfFit(t *testing.T) { pool := viewTailPool(t) obsV, err := core.Slice(pool, 0, 20, 30) if err != nil { t.Fatalf("Slice: %v", err) } expV, err := core.Slice(pool, 0, 30, 40) if err != nil { t.Fatalf("Slice: %v", err) } visible := pool.RawFloats()[:40] fObs, err := core.FromFloats(visible[20:30], 10) if err != nil { t.Fatalf("FromFloats: %v", err) } fExp, err := core.FromFloats(visible[30:40], 10) if err != nil { t.Fatalf("FromFloats: %v", err) } c1, df1, p1, errV := ChiSquareGoodnessOfFit(obsV, expV) c2, df2, p2, errF := ChiSquareGoodnessOfFit(fObs, fExp) if (errV == nil) != (errF == nil) { t.Fatalf("ChiSquareGoodnessOfFit on views errored %v, on fresh arrays %v", errV, errF) } if errV == nil && (c1 != c2 || df1 != df2 || p1 != p2) { t.Fatalf("ChiSquareGoodnessOfFit on views = (%v, %d, %v), fresh arrays answer (%v, %d, %v)", c1, df1, p1, c2, df2, p2) } } // TestViewTailLinearRegression pins the fit on rebased views: the // design and response scans refuse only what the visible rows hold, so // junk in the invisible tail cannot reject a clean fit. func TestViewTailLinearRegression(t *testing.T) { design := make([]float64, 40) for i := range design { design[i] = float64(i % 7) } resp := make([]float64, 20) for i := range resp { resp[i] = float64(i%5) * 0.5 } // Both junk slots sit past their views' visible rows: design row 15 // is invisible to the ten-row design view, response element 15 to // the ten-row response view. design[30] = math.NaN() resp[15] = math.NaN() designArr, err := core.FromFloats(design, len(design)) if err != nil { t.Fatalf("FromFloats: %v", err) } respArr, err := core.FromFloats(resp, len(resp)) if err != nil { t.Fatalf("FromFloats: %v", err) } design2d, err := core.Reshape(designArr, 20, 2) if err != nil { t.Fatalf("Reshape: %v", err) } designView, err := core.Slice(design2d, 0, 0, 10) if err != nil { t.Fatalf("Slice: %v", err) } respView, err := core.Slice(respArr, 0, 0, 10) if err != nil { t.Fatalf("Slice: %v", err) } if _, err := LinearRegression(designView, respView); err != nil { t.Fatalf("LinearRegression refused clean visible rows: %v", err) } }