From e739ff1e52b77022ccaf652608649958fc3de041 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Fri, 25 Sep 2026 21:28:36 +0200 Subject: [PATCH] tests: kill a single-slice loop-bound gap in cellular's 5x5x5 search Stryker (mtp, --since 29e50ad3) found 261 survivors / 24 timeouts across the whole repo; triaging the ones in this fix batch's own new code (cellular's loop, its prune, cellular_unit) surfaced one real gap disguised as collateral timeout noise from a batched infinite-loop mutant: dz < 2 (and the dy/dx equivalents), silently dropping the +2 layer on one axis alone, passed every existing random-sampled test. That is not the sweeps being weak: the exactness proof's own margin (F1, F2 always < sqrt(3), radius-2 cells' lower bound is exactly 2) makes a genuine win from any single |offset component| = 2 cell astronomically rare over uniformly random points (measured zero hits in 3,000,000 samples). SearchReachesEveryAxisAlignedRadiusTwoSlice constructs a real example for each of the six axis-aligned radius-2 offsets directly: for a candidate integer cell, build the query point that is the closest point of that cell's own unit cube to the target neighbour's actual jittered feature. That construction finds a hit for every offset within a couple thousand candidate cells, deterministically. This kills dz < 2 / dy < 2 / dx < 2 (all manually confirmed). Also manually confirmed via Stryker's own reported mutants: the dz--/dy--/ dx-- (increment-to-decrement) mutants are genuine infinite loops, correctly caught as timeouts. The f2 >= 0.0f mutant (disabling the F2 = 0 guard) is killed by the existing NoNaNAtIntegerPointsNear2To24 test. The remaining survivors/timeouts in the new code (the lower-bound prune's >>= vs > comparison, its arithmetic terms, and cellular_unit's >> vs >>> shift on an already-unsigned operand) are equivalent mutants: since the per-axis lower bound is always 0 or 1 by construction, and pruning is a proven-safe optimization that never changes cellular's output, no mutation confined to "when does the prune trigger" can be observed by any value-based test. Verified by hand: disabling pruning entirely reproduces the unpruned reference bit for bit, as it must. Co-Authored-By: Claude Sonnet 5 --- .../CellularAndSminGradTests.cs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/packages/cultmath/tests/CultMath.Tests/CellularAndSminGradTests.cs b/packages/cultmath/tests/CultMath.Tests/CellularAndSminGradTests.cs index b95d8926..c401fbfa 100644 --- a/packages/cultmath/tests/CultMath.Tests/CellularAndSminGradTests.cs +++ b/packages/cultmath/tests/CultMath.Tests/CellularAndSminGradTests.cs @@ -334,6 +334,57 @@ public sealed class CellularAndSminGradTests } } + [Fact] + public void SearchReachesEveryAxisAlignedRadiusTwoSlice() + { + // F6, part 2 (Soul finding): a single-slice loop-bound mutant on one axis (e.g. dz < 2 + // instead of dz <= 2, silently dropping the +2 layer on that axis alone) still passed the + // random 20,000-point sweeps above. That is not those sweeps being weak; it is the + // exactness proof's own margin (F1, F2 always < sqrt(3), radius-2 cells' lower bound is + // exactly 2) making a genuine win from any single |offset component| = 2 cell astronomically + // rare over uniformly random points (zero hits in 3,000,000 samples, measured separately). + // Hunting for one by choosing candidate query POINTS is the wrong end of the search; this + // instead scans candidate integer CELLS and, for each, builds the query point most likely to + // realize that cell's neighbour as the winner: the closest point of the query cell's own unit + // cube to the target neighbour's actual jittered feature. That construction finds a real + // example for every one of the six axis-aligned radius-2 offsets within a couple thousand + // candidate cells, deterministically (pcg3d has no randomness to get lucky or unlucky with). + var offsets = new[] + { + new float3(2.0f, 0.0f, 0.0f), new float3(-2.0f, 0.0f, 0.0f), + new float3(0.0f, 2.0f, 0.0f), new float3(0.0f, -2.0f, 0.0f), + new float3(0.0f, 0.0f, 2.0f), new float3(0.0f, 0.0f, -2.0f), + }; + + foreach (var offset in offsets) + { + var found = false; + for (var qz = -80; qz <= 80 && !found; qz++) + for (var qy = -80; qy <= 80 && !found; qy++) + for (var qx = -80; qx <= 80 && !found; qx++) + { + var q = new float3(qx, qy, qz); + var targetFeature = FeaturePoint(q + offset); + var p = new float3( + Math.Clamp(targetFeature.x, q.x, q.x + 0.999f), + Math.Clamp(targetFeature.y, q.y, q.y + 0.999f), + Math.Clamp(targetFeature.z, q.z, q.z + 0.999f)); + + var (f1, f2) = FindF1F2(p); // independent 7x7x7 oracle + var d = math.distance(p, targetFeature); + if (MathF.Abs(d - f1) > 1.0e-4f && MathF.Abs(d - f2) > 1.0e-4f) + continue; // this cell didn't realize the target offset as F1 or F2; try another. + + found = true; + var result = math.cellular(p); + Assert.Equal(f1, result.nearest.w, precision: 3); + Assert.Equal(f2 - f1, result.edge.w, precision: 3); + } + + Assert.True(found, $"could not construct a query point where offset {offset.x},{offset.y},{offset.z} ever wins F1 or F2"); + } + } + [Fact] public void PrunedSearchIsBitIdenticalToTheUnprunedFiveCubedSearch() {