From 2a3cab1bd7ab5f2532f8a2ff32f328d057ae2de5 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 29 Sep 2026 23:58:43 +0200 Subject: [PATCH] CultMath tests: a dense sweep pins cultmath_phacelle against C# bit for bit The generic mirror comparison passes about a hundred inputs through the shader and missed a mirror whose prune cut was 2.0 instead of 2.25, because only a rare cell has a nonzero weight under that cut. Twenty thousand random arguments compare the shader text and math.phacelle field by field and kill that mutant. Co-Authored-By: Claude Opus 5.5 --- .../HlslSourceCompatibilityTests.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/packages/cultmath/tests/CultMath.Tests/HlslSourceCompatibilityTests.cs b/packages/cultmath/tests/CultMath.Tests/HlslSourceCompatibilityTests.cs index ad690759..2cf737df 100644 --- a/packages/cultmath/tests/CultMath.Tests/HlslSourceCompatibilityTests.cs +++ b/packages/cultmath/tests/CultMath.Tests/HlslSourceCompatibilityTests.cs @@ -134,6 +134,35 @@ public sealed class HlslSourceCompatibilityTests Assert.Equal(34, compared.Count); } + /// + /// The generic comparison above passes about a hundred inputs through cultmath_phacelle, and + /// its prune only changes an output for the rare cell whose weight is nonzero but whose lower + /// bound sits just under the 2.25 cut (Soul's F6 lesson for cellular): a mirror whose cut is 2.0 + /// survives a hundred inputs. A dense sweep of realistic arguments closes that gap. + /// + [Fact] + public void PhacelleMirrorMatchesCSharpBitForBitAcrossADenseSweep() + { + var (assembly, errors) = CompileShaderMirror(); + Assert.True(assembly is not null, string.Join(Environment.NewLine, errors)); + var shaderType = assembly!.GetType("CultMathHlsl.HlslShader")!; + var shader = Activator.CreateInstance(shaderType); + var mirror = ShaderFunction(shaderType, "cultmath_phacelle", typeof(float3), typeof(float3), typeof(float), typeof(float))!; + + var random = new System.Random(0x5EED2); + float Next(float extent) => (random.NextSingle() * 2.0f - 1.0f) * extent; + for (var i = 0; i < 20000; i++) + { + var p = new float3(Next(50.0f), Next(50.0f), Next(50.0f)); + var side = new float3(Next(9.0f), Next(9.0f), Next(9.0f)); + var offset = random.NextSingle(); + var normalization = random.NextSingle(); + var expected = Values(phacelle(p, side, offset, normalization)); + var actual = Values(mirror.Invoke(shader, new object[] { p, side, offset, normalization })!); + Assert.True(BitwiseEqual(expected, actual), $"mirror differs from C# at p = {p}, side = {side}"); + } + } + /// /// A hand-built pair of structs standing in for a mirror struct return and its C# counterpart /// (invariant 8's one struct return shape, e.g. ): same field shape,