package field import ( "math" "math/rand/v2" "testing" ) // The deque has to give the same answer as the loop it replaces, on every cell, including the edges and the // seam. It is O(1) a cell against O(radius squared), which is the difference between a diagnostic and a hang // at planet scale - and an optimisation that is only nearly right is worse than the version that was slow. func TestSlidingWindowsMatchTheNaiveLoop(t *testing.T) { r := rand.New(rand.NewPCG(7, 9)) const w, h = 61, 37 f := New(w, h, 8) for i := range f.Data { f.Data[i] = float32(r.NormFloat64() * 50) } naive := func(cx, cy, radius int, wrapX, wantMax bool) float32 { best := float32(math.Inf(1)) if wantMax { best = float32(math.Inf(-1)) } for y := cy - radius; y <= cy+radius; y++ { sy := y if sy < 0 { sy = 0 } else if sy >= h { sy = h - 1 } for x := cx - radius; x <= cx+radius; x++ { sx := x if wrapX { sx = ((sx % w) + w) % w } else if sx < 0 { sx = 0 } else if sx >= w { sx = w - 1 } v := f.Data[sy*w+sx] if (wantMax && v > best) || (!wantMax && v < best) { best = v } } } return best } for _, radius := range []int{1, 3, 8, 20} { for _, wrapX := range []bool{false, true} { hi := SlidingMax(f, radius, wrapX) lo := SlidingMin(f, radius, wrapX) rel := LocalRelief(f, radius, wrapX) for y := 0; y < h; y++ { for x := 0; x < w; x++ { i := y*w + x if got, want := hi.Data[i], naive(x, y, radius, wrapX, true); got != want { t.Fatalf("max r=%d wrap=%v at (%d,%d): %v want %v", radius, wrapX, x, y, got, want) } if got, want := lo.Data[i], naive(x, y, radius, wrapX, false); got != want { t.Fatalf("min r=%d wrap=%v at (%d,%d): %v want %v", radius, wrapX, x, y, got, want) } if got := rel.Data[i]; got != hi.Data[i]-lo.Data[i] { t.Fatalf("relief r=%d at (%d,%d): %v against %v", radius, x, y, got, hi.Data[i]-lo.Data[i]) } } } } } } // A radius of zero is a no-op rather than an error, which is what a caller with a window smaller than one // cell should get. func TestASlidingWindowOfNothingIsTheFieldItself(t *testing.T) { f := New(4, 3, 8) for i := range f.Data { f.Data[i] = float32(i) } for _, got := range []*Field{SlidingMax(f, 0, true), SlidingMin(f, 0, false)} { for i := range f.Data { if got.Data[i] != f.Data[i] { t.Fatalf("radius 0 changed cell %d", i) } } } if rel := LocalRelief(f, 0, true); rel.Data[5] != 0 { t.Errorf("relief over a single cell is zero, got %v", rel.Data[5]) } }