CultMath GLSL: Phacelle lowered into its own MPL-2.0 file; iv_frustum_ball and iv_exp in the fixture

GlslLowering.SeparateFiles is the one name-keyed rule: CultMath.Phacelle.hlsl
lowers into shaders/CultMath.Phacelle.glsl (MPL-2.0, guard
CULTMATH_PHACELLE_GLSL) and its include line leaves CultMath.glsl, which is
now MIT and says so in its provenance line. ReadShaderSource takes an
include predicate (one resolver); the C# mirror still inlines everything.
GlslMirrorTests pins both files, checks tokens in each, takes the union of
definitions and asserts cultmath_phacelle( and CultPhasor live only in the
Phacelle file. The fixture appends cultmath_iv_frustum_ball (exact) and
cultmath_iv_exp ("ulp-bounded, platform exp", platform named); the
comparison is per family, the platform family within 1 ulp with the
maximum printed. THIRD-PARTY-NOTICES lists CultMath.Phacelle.glsl, not
CultMath.glsl, as MPL-2.0.

Regenerated on Yggdrasil (Ubuntu 24.04.5, .NET 10.0.12); 286/286 green,
iv_exp within 0 ulp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-03 13:40:16 +02:00
commit 2ba0dc17f4
7 changed files with 799 additions and 144 deletions

View file

@ -36,8 +36,8 @@ gradient and the exact pruning are new here.
covers exactly the files that carry the port and nothing else:
`src/CultMath/math.Phacelle.cs`, `src/CultMath/CultPhasor.cs`,
`shaders/CultMath.Phacelle.hlsl` and the Unity copy of that include, and
`shaders/CultMath.glsl`, which is generated from the HLSL with that include
inlined, so the generated file as a whole is MPL-2.0. Each carries the
MPL-2.0 header and this attribution. The rest of CultMath is MIT,
so the `GameCult.Math` NuGet package and the `org.gamecult.cultmath` Unity
package both declare `MIT AND MPL-2.0` and ship this file.
`shaders/CultMath.Phacelle.glsl`, which is generated from that include. Each
carries the MPL-2.0 header and this attribution. The rest of CultMath, the
generated GLSL library included, is MIT, so the `GameCult.Math` NuGet
package and the `org.gamecult.cultmath` Unity package both declare
`MIT AND MPL-2.0` and ship this file.

View file

@ -0,0 +1,99 @@
// Generated from shaders/CultMath.Phacelle.hlsl by GlslLowering (packages/cultmath/tests/CultMath.Tests/GlslLowering.cs). Do not edit; regenerate with CULTMATH_WRITE_GLSL=1 dotnet test --filter GlslMirrorTests. MPL-2.0; requires CultMath.glsl before it.
#ifndef CULTMATH_PHACELLE_GLSL
#define CULTMATH_PHACELLE_GLSL
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of
// the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
//
// Phacelle noise. Copyright (c) 2025 Rune Skovbo Johansen (Shadertoy t3dyWl; MPL-2.0), read from the
// C# port PhacelleNoise in lpmitchell/AdvancedTerrainErosion, converted to burstable C# by Luke
// Mitchell (2026). Modified by GameCult: 3D cells, a caller-supplied stripe wave vector, an exact
// gradient and exact pruning. This file is an MPL-2.0 file; CultMath.hlsl, which includes it after
// its own definitions of CULTMATH_TAU and cultmath_pcg3d, is MIT (see THIRD-PARTY-NOTICES.md).
// Invariant 8's struct return shape for cultmath_phacelle, compared field by field and bit for bit
// against CultMath.CultPhasor, same field order.
struct CultPhasor
{
vec4 cos;
vec4 sin;
};
// Johansen's Phacelle noise on 3D cells. See math.phacelle's comment (math.Phacelle.cs) for the stripe wave
// vector, the weight, the normalization, the exact gradient and why the prune is exact. Mirrors
// the C# body operation for operation; the C#-only internal overload that switches the prune off
// has no shader twin.
CultPhasor cultmath_phacelle(vec3 p, vec3 side, float offset, float normalization)
{
vec3 cell = floor(p);
vec3 local = p - cell;
float phaseOffset = offset * CULTMATH_TAU;
float sumW = 0.0;
float sumCos = 0.0;
float sumSin = 0.0;
vec3 gradW = vec3(0.0, 0.0, 0.0);
vec3 gradCos = vec3(0.0, 0.0, 0.0);
vec3 gradSin = vec3(0.0, 0.0, 0.0);
for (int dz = -1; dz <= 2; dz++)
{
float gz = max(0.0, abs(local.z - float(dz)) - 0.5);
for (int dy = -1; dy <= 2; dy++)
{
float gy = max(0.0, abs(local.y - float(dy)) - 0.5);
for (int dx = -1; dx <= 2; dx++)
{
float gx = max(0.0, abs(local.x - float(dx)) - 0.5);
if (gx * gx + gy * gy + gz * gz >= 2.25)
continue;
vec3 gridStep = vec3(dx, dy, dz);
ivec3 hash = cultmath_pcg3d(ivec3(cell + gridStep));
vec3 jitter = vec3(
float(uint(hash.x) >> 8) * (1.0 / 16777216.0) - 0.5,
float(uint(hash.y) >> 8) * (1.0 / 16777216.0) - 0.5,
float(uint(hash.z) >> 8) * (1.0 / 16777216.0) - 0.5);
vec3 v = local - gridStep - jitter;
float falloff = exp(-2.0 * dot(v, v));
float w = max(0.0, falloff - 0.01111);
vec3 dw = w > 0.0 ? (-4.0 * falloff) * v : vec3(0.0, 0.0, 0.0);
float phase = dot(v, side) + phaseOffset;
float c = cos(phase);
float s = sin(phase);
sumW += w;
sumCos += w * c;
sumSin += w * s;
gradW += dw;
gradCos += dw * c - (w * s) * side;
gradSin += dw * s + (w * c) * side;
}
}
}
float invW = 1.0 / sumW;
float rawCos = sumCos * invW;
float rawSin = sumSin * invW;
vec3 gradRawCos = (gradCos - rawCos * gradW) * invW;
vec3 gradRawSin = (gradSin - rawSin * gradW) * invW;
float rawLength = sqrt(rawCos * rawCos + rawSin * rawSin);
float floorLength = 1.0 - normalization;
CultPhasor result;
if (rawLength > floorLength)
{
float outCos = rawCos / rawLength;
float outSin = rawSin / rawLength;
vec3 gradLength = (rawCos * gradRawCos + rawSin * gradRawSin) / rawLength;
result.cos = vec4((gradRawCos - outCos * gradLength) / rawLength, outCos);
result.sin = vec4((gradRawSin - outSin * gradLength) / rawLength, outSin);
}
else
{
result.cos = vec4(gradRawCos / floorLength, rawCos / floorLength);
result.sin = vec4(gradRawSin / floorLength, rawSin / floorLength);
}
return result;
}
#endif

View file

@ -1,4 +1,4 @@
// Generated from shaders/CultMath.hlsl and its includes by GlslLowering (packages/cultmath/tests/CultMath.Tests/GlslLowering.cs). Do not edit; regenerate with CULTMATH_WRITE_GLSL=1 dotnet test --filter GlslMirrorTests.
// Generated from shaders/CultMath.hlsl and CultMath.Interval.hlsl by GlslLowering (packages/cultmath/tests/CultMath.Tests/GlslLowering.cs). Do not edit; regenerate with CULTMATH_WRITE_GLSL=1 dotnet test --filter GlslMirrorTests. MIT. cultmath_phacelle is in CultMath.Phacelle.glsl, to be concatenated after this file by consumers that call it.
#ifndef CULTMATH_GLSL
#define CULTMATH_GLSL
// CultMath HLSL mirror.
@ -509,106 +509,6 @@ CultCellular cultmath_cellular(vec3 p)
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of
// the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
//
// Phacelle noise. Copyright (c) 2025 Rune Skovbo Johansen (Shadertoy t3dyWl; MPL-2.0), read from the
// C# port PhacelleNoise in lpmitchell/AdvancedTerrainErosion, converted to burstable C# by Luke
// Mitchell (2026). Modified by GameCult: 3D cells, a caller-supplied stripe wave vector, an exact
// gradient and exact pruning. This file is an MPL-2.0 file; CultMath.hlsl, which includes it after
// its own definitions of CULTMATH_TAU and cultmath_pcg3d, is MIT (see THIRD-PARTY-NOTICES.md).
// Invariant 8's struct return shape for cultmath_phacelle, compared field by field and bit for bit
// against CultMath.CultPhasor, same field order.
struct CultPhasor
{
vec4 cos;
vec4 sin;
};
// Johansen's Phacelle noise on 3D cells. See math.phacelle's comment (math.Phacelle.cs) for the stripe wave
// vector, the weight, the normalization, the exact gradient and why the prune is exact. Mirrors
// the C# body operation for operation; the C#-only internal overload that switches the prune off
// has no shader twin.
CultPhasor cultmath_phacelle(vec3 p, vec3 side, float offset, float normalization)
{
vec3 cell = floor(p);
vec3 local = p - cell;
float phaseOffset = offset * CULTMATH_TAU;
float sumW = 0.0;
float sumCos = 0.0;
float sumSin = 0.0;
vec3 gradW = vec3(0.0, 0.0, 0.0);
vec3 gradCos = vec3(0.0, 0.0, 0.0);
vec3 gradSin = vec3(0.0, 0.0, 0.0);
for (int dz = -1; dz <= 2; dz++)
{
float gz = max(0.0, abs(local.z - float(dz)) - 0.5);
for (int dy = -1; dy <= 2; dy++)
{
float gy = max(0.0, abs(local.y - float(dy)) - 0.5);
for (int dx = -1; dx <= 2; dx++)
{
float gx = max(0.0, abs(local.x - float(dx)) - 0.5);
if (gx * gx + gy * gy + gz * gz >= 2.25)
continue;
vec3 gridStep = vec3(dx, dy, dz);
ivec3 hash = cultmath_pcg3d(ivec3(cell + gridStep));
vec3 jitter = vec3(
float(uint(hash.x) >> 8) * (1.0 / 16777216.0) - 0.5,
float(uint(hash.y) >> 8) * (1.0 / 16777216.0) - 0.5,
float(uint(hash.z) >> 8) * (1.0 / 16777216.0) - 0.5);
vec3 v = local - gridStep - jitter;
float falloff = exp(-2.0 * dot(v, v));
float w = max(0.0, falloff - 0.01111);
vec3 dw = w > 0.0 ? (-4.0 * falloff) * v : vec3(0.0, 0.0, 0.0);
float phase = dot(v, side) + phaseOffset;
float c = cos(phase);
float s = sin(phase);
sumW += w;
sumCos += w * c;
sumSin += w * s;
gradW += dw;
gradCos += dw * c - (w * s) * side;
gradSin += dw * s + (w * c) * side;
}
}
}
float invW = 1.0 / sumW;
float rawCos = sumCos * invW;
float rawSin = sumSin * invW;
vec3 gradRawCos = (gradCos - rawCos * gradW) * invW;
vec3 gradRawSin = (gradSin - rawSin * gradW) * invW;
float rawLength = sqrt(rawCos * rawCos + rawSin * rawSin);
float floorLength = 1.0 - normalization;
CultPhasor result;
if (rawLength > floorLength)
{
float outCos = rawCos / rawLength;
float outSin = rawSin / rawLength;
vec3 gradLength = (rawCos * gradRawCos + rawSin * gradRawSin) / rawLength;
result.cos = vec4((gradRawCos - outCos * gradLength) / rawLength, outCos);
result.sin = vec4((gradRawSin - outSin * gradLength) / rawLength, outSin);
}
else
{
result.cos = vec4(gradRawCos / floorLength, rawCos / floorLength);
result.sin = vec4(gradRawSin / floorLength, rawSin / floorLength);
}
return result;
}
// Interval arithmetic over vec2(lo, hi), lo <= hi, both finite. Every cultmath_iv_* function returns
// an interval containing f(x) for every x in its input intervals, mirroring math.Interval.cs bit for
// bit; see that file and docs/design.md ("Intervals") for each op's enclosure and its ulp widening.

View file

@ -4,24 +4,66 @@ using System.Text.RegularExpressions;
namespace CultMath.Tests;
/// <summary>
/// The HLSL to GLSL ES 3.00 transformation that produces <c>shaders/CultMath.glsl</c> from
/// <c>shaders/CultMath.hlsl</c>. The steps below, in order, are the complete list documented in
/// docs/design.md (GLSL Target: Source Transformations). A dialect gap outside the list is fixed in the
/// HLSL, in the subset both languages share, never by a special case here; GlslMirrorTests fails on any
/// HLSL-only token that survives.
/// The HLSL to GLSL ES 3.00 transformation that produces <c>shaders/CultMath.glsl</c>, and one file per
/// <see cref="SeparateFiles"/> entry, from <c>shaders/CultMath.hlsl</c> and its includes. The steps below,
/// in order, are the complete list documented in docs/design.md (GLSL Target: Source Transformations).
/// A dialect gap outside the list is fixed in the HLSL, in the subset both languages share, never by a
/// special case here; GlslMirrorTests fails on any HLSL-only token that survives.
/// </summary>
internal static class GlslLowering
{
internal const string ProvenanceLine =
"// Generated from shaders/CultMath.hlsl and its includes by GlslLowering " +
"(packages/cultmath/tests/CultMath.Tests/GlslLowering.cs). Do not edit; regenerate with " +
/// <summary>
/// Includes under a file-level licence of their own: each is lowered into its own output file instead
/// of being inlined, so CultMath.glsl stays MIT. This table is the lowering's only rule keyed by name.
/// </summary>
internal static readonly IReadOnlyDictionary<string, (string Glsl, string Licence)> SeparateFiles =
new Dictionary<string, (string, string)>
{
// Phacelle is a port of MPL-2.0 code, and MPL-2.0 covers the files that carry it.
["CultMath.Phacelle.hlsl"] = ("CultMath.Phacelle.glsl", "MPL-2.0"),
};
private const string Regenerate =
"by GlslLowering (packages/cultmath/tests/CultMath.Tests/GlslLowering.cs). Do not edit; regenerate with " +
"CULTMATH_WRITE_GLSL=1 dotnet test --filter GlslMirrorTests.";
/// <summary>
/// Lowers the HLSL text whose includes are already inlined in place (step 1:
/// <see cref="HlslSourceCompatibilityTests.ReadShaderSource"/>, the C# mirror's own resolver).
/// The lowered files as (name under shaders/, text): CultMath.glsl first, then one per
/// <see cref="SeparateFiles"/> entry, which requires CultMath.glsl before it.
/// </summary>
internal static string Lower(string hlsl)
internal static IReadOnlyList<(string File, string Text)> Lower(string cultMathRoot)
{
// 1. Each #include is inlined in place by the C# mirror's own resolver, except a SeparateFiles
// include, which leaves nothing behind and is lowered into its own file.
var inlined = new List<string>();
var library = HlslSourceCompatibilityTests.ReadShaderSource(cultMathRoot, name =>
{
if (SeparateFiles.ContainsKey(name))
return false;
inlined.Add(name);
return true;
});
var outputs = new List<(string File, string Text)>();
var pointers = new List<string>();
foreach (var (hlsl, (glsl, licence)) in SeparateFiles)
{
var text = LowerText(
File.ReadAllText(Path.Combine(cultMathRoot, "shaders", hlsl)),
$"// Generated from shaders/{hlsl} {Regenerate} {licence}; requires CultMath.glsl before it.",
glsl);
var functions = Regex.Matches(text, @"(?m)^\w+\s+(cultmath_\w+)\s*\(").Select(m => m.Groups[1].Value);
pointers.Add($" {string.Join(", ", functions)} is in {glsl}, to be concatenated after this file by consumers that call it.");
outputs.Add((glsl, text));
}
var includes = string.Concat(inlined.Select(name => " and " + name));
outputs.Insert(0, ("CultMath.glsl", LowerText(
library, $"// Generated from shaders/CultMath.hlsl{includes} {Regenerate} MIT.{string.Concat(pointers)}", "CultMath.glsl")));
return outputs;
}
private static string LowerText(string hlsl, string provenance, string file)
{
var source = hlsl.ReplaceLineEndings("\n");
@ -56,8 +98,9 @@ internal static class GlslLowering
// 7. Float literals pass through: HLSL's unsuffixed 0.5 and 1.0e30 are GLSL literals already.
// 8. The CULTMATH_GLSL guard, and 9. the provenance line.
return ProvenanceLine + "\n#ifndef CULTMATH_GLSL\n#define CULTMATH_GLSL\n" + source.Trim('\n') + "\n\n#endif\n";
// 8. The guard named for the file (CultMath.glsl -> CULTMATH_GLSL), and 9. the provenance line.
var guard = file.ToUpperInvariant().Replace('.', '_');
return provenance + "\n#ifndef " + guard + "\n#define " + guard + "\n" + source.Trim('\n') + "\n\n#endif\n";
}
private static readonly Regex Cast = new(@"\((float|int|uint)\)");

View file

@ -1,5 +1,7 @@
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json.Nodes;
using System.Text.RegularExpressions;
using CultMath;
using Xunit;
@ -7,34 +9,40 @@ using Xunit;
namespace CultMath.Tests;
/// <summary>
/// shaders/CultMath.glsl is derived: it is exactly <see cref="GlslLowering.Lower"/> of the committed HLSL,
/// no HLSL-only token survives the lowering, no public function is dropped except the texture samplers,
/// and the golden fixture a WebGL2 consumer evaluates is C#'s current output. With CULTMATH_WRITE_GLSL=1
/// set, the first and last tests write the committed files before comparing, which is the one
/// regeneration path.
/// shaders/CultMath.glsl and shaders/CultMath.Phacelle.glsl are derived: each is exactly its
/// <see cref="GlslLowering.Lower"/> output from the committed HLSL, no HLSL-only token survives the
/// lowering, no public function is dropped except the texture samplers, the MPL-2.0 Phacelle code lives
/// only in its own file, and the golden fixture a WebGL2 consumer evaluates is C#'s current output. With
/// CULTMATH_WRITE_GLSL=1 set, the first and last tests write the committed files before comparing, which
/// is the one regeneration path.
/// </summary>
public sealed class GlslMirrorTests
{
private readonly ITestOutputHelper output;
public GlslMirrorTests(ITestOutputHelper output) => this.output = output;
private static bool Write => Environment.GetEnvironmentVariable("CULTMATH_WRITE_GLSL") == "1";
private static string Root => HlslSourceCompatibilityTests.FindCultMathRoot();
private static string GlslPath => Path.Combine(Root, "shaders", "CultMath.glsl");
private static string FixturePath => Path.Combine(Root, "tests", "CultMath.Tests", "fixtures", "glsl-parity.json");
private static string Hlsl => HlslSourceCompatibilityTests.ReadShaderSource(Root).ReplaceLineEndings("\n");
private static string Lowered => GlslLowering.Lower(HlslSourceCompatibilityTests.ReadShaderSource(Root));
private static IReadOnlyList<(string File, string Text)> Lowered => GlslLowering.Lower(Root);
[Fact]
public void CommittedGlslEqualsLowering()
{
var expected = Lowered;
if (Write)
File.WriteAllText(GlslPath, expected);
var committed = File.ReadAllText(GlslPath).ReplaceLineEndings("\n");
Assert.True(committed == expected, "shaders/CultMath.glsl is not the lowering of the committed HLSL (regenerate with CULTMATH_WRITE_GLSL=1):\n" + UnifiedDiff(committed, expected));
foreach (var (file, expected) in Lowered)
{
var path = Path.Combine(Root, "shaders", file);
if (Write)
File.WriteAllText(path, expected);
var committed = File.ReadAllText(path).ReplaceLineEndings("\n");
Assert.True(committed == expected, $"shaders/{file} is not the lowering of the committed HLSL (regenerate with CULTMATH_WRITE_GLSL=1):\n" + UnifiedDiff(committed, expected));
}
}
// Identifier boundaries: cultmath_lerp is a CultMath function, lerp( is HLSL.
@ -44,8 +52,11 @@ public sealed class GlslMirrorTests
[Fact]
public void NoHlslOnlyTokensSurvive()
{
var survivors = HlslOnly.Matches(Lowered).Select(m => m.Value).Distinct().ToArray();
Assert.True(survivors.Length == 0, "HLSL-only tokens in the lowering: " + string.Join(", ", survivors));
foreach (var (file, text) in Lowered)
{
var survivors = HlslOnly.Matches(text).Select(m => m.Value).Distinct().ToArray();
Assert.True(survivors.Length == 0, $"HLSL-only tokens in {file}: " + string.Join(", ", survivors));
}
}
private static readonly Regex Definition = new(@"(?m)^\w+\s+(cultmath_\w+)\s*\(([^)]*)\)");
@ -53,11 +64,21 @@ public sealed class GlslMirrorTests
[Fact]
public void EveryPublicFunctionIsLowered()
{
var lowered = Lowered;
var hlsl = Definition.Matches(Hlsl).Where(m => !Regex.IsMatch(m.Groups[2].Value, @"\b(Texture2D|SamplerState)\b"))
.Select(m => m.Groups[1].Value).ToHashSet();
var glsl = Definition.Matches(Lowered).Select(m => m.Groups[1].Value).ToHashSet();
var glsl = lowered.SelectMany(output => Definition.Matches(output.Text)).Select(m => m.Groups[1].Value).ToHashSet();
Assert.Contains("cultmath_snoise", glsl);
Assert.True(hlsl.SetEquals(glsl), "HLSL only: " + string.Join(", ", hlsl.Except(glsl)) + "; GLSL only: " + string.Join(", ", glsl.Except(hlsl)));
// The MPL-2.0 code is in its own file and nowhere in the MIT library.
var library = lowered.Single(output => output.File == "CultMath.glsl").Text;
var phacelle = lowered.Single(output => output.File == "CultMath.Phacelle.glsl").Text;
foreach (var token in new[] { "cultmath_phacelle(", "CultPhasor" })
{
Assert.DoesNotContain(token, library);
Assert.Contains(token, phacelle);
}
}
[Fact]
@ -70,10 +91,59 @@ public sealed class GlslMirrorTests
File.WriteAllText(FixturePath, expected);
}
var committed = File.ReadAllText(FixturePath).ReplaceLineEndings("\n");
Assert.True(committed == expected, "fixtures/glsl-parity.json is not C#'s current output (regenerate with CULTMATH_WRITE_GLSL=1):\n" + UnifiedDiff(committed, expected));
// Family by family: every family is compared as text, except one whose tolerance names the
// platform, whose results are compared case by case within one ulp.
var committed = JsonNode.Parse(File.ReadAllText(FixturePath))!.AsObject();
var current = JsonNode.Parse(expected)!.AsObject();
var committedFamilies = committed["functions"]!.AsArray();
var currentFamilies = current["functions"]!.AsArray();
Assert.Equal(currentFamilies.Count, committedFamilies.Count);
for (var f = 0; f < currentFamilies.Count; f++)
{
var want = currentFamilies[f]!.AsObject();
var have = committedFamilies[f]!.AsObject();
var name = (string)want["glsl"]!;
Assert.Equal(name, (string?)have["glsl"]);
Assert.Equal((string)want["tolerance"]!, (string?)have["tolerance"]);
if (((string)want["tolerance"]!).Contains(PlatformTolerance))
{
var ulps = MaxUlps(have["cases"]!.AsArray(), want["cases"]!.AsArray(), name);
output.WriteLine($"{name}: within {ulps} ulp of this platform's exp ({RuntimeInformation.OSDescription}); fixture generated on {have["platform"]}");
Assert.True(ulps <= 1, $"{name}: {ulps} ulp from this platform's result, more than 1");
}
else
{
Assert.True(JsonNode.DeepEquals(want, have), $"fixtures/glsl-parity.json family {name} is not C#'s current output (regenerate with CULTMATH_WRITE_GLSL=1)");
}
}
committed.Remove("functions");
current.Remove("functions");
Assert.True(JsonNode.DeepEquals(current, committed), "fixtures/glsl-parity.json header is not the generator's (regenerate with CULTMATH_WRITE_GLSL=1)");
}
// The largest distance in float32 ulps between two families' results; arguments must be identical.
private static long MaxUlps(JsonArray committed, JsonArray current, string name)
{
Assert.Equal(current.Count, committed.Count);
long max = 0;
for (var c = 0; c < current.Count; c++)
{
var have = ((string)committed[c]!).Split(" -> ");
var want = ((string)current[c]!).Split(" -> ");
Assert.True(have[0] == want[0], $"{name} case {c}: arguments differ");
var haveBits = have[1].Split(' ').Select(h => (int)Convert.ToUInt32(h, 16)).ToArray();
var wantBits = want[1].Split(' ').Select(h => (int)Convert.ToUInt32(h, 16)).ToArray();
Assert.Equal(wantBits.Length, haveBits.Length);
max = Math.Max(max, haveBits.Zip(wantBits, (x, y) => Math.Abs((long)Ordered(x) - Ordered(y))).Max());
}
return max;
}
// A float32 bit pattern as an integer whose order and differences are the floats' order and ulps.
private static int Ordered(int bits) => bits < 0 ? int.MinValue - bits : bits;
// ---- The golden fixture ----
private const int Seed = 20261002;
@ -92,9 +162,14 @@ public sealed class GlslMirrorTests
return new float2(centre - width * 0.5f, centre + width * 0.5f);
}
// Every family the site's WebGL2 readback evaluates. pcg3d and pcg4d are integer-only and must match
// exactly; the float families are ulp-bounded, with the bound measured on the device by the consumer.
// iv_exp is left out: it calls the platform's exp, so its C# bits are not the same on every OS.
// A tolerance containing this names a family whose C# bits come from the platform's exp and so differ
// by OS: GoldenFixtureMatchesCSharp compares it within one ulp, and its entry records the platform.
private const string PlatformTolerance = "platform exp";
// Every family the site's WebGL2 readback evaluates, in a fixed order. A new family is appended, so the
// seeded stream and every earlier family's cases stay as they were. pcg3d, pcg4d and iv_frustum_ball
// are "exact"; the other float families are ulp-bounded, with the bound measured on the device by the
// consumer.
private static readonly Family[] Families =
{
new("float cultmath_snoise(vec3)", "ulp-bounded", r => new object[] { Point3(r) }),
@ -122,6 +197,13 @@ public sealed class GlslMirrorTests
new("vec2 cultmath_iv_lerp(vec2, vec2, float)", "ulp-bounded", r => new object[] { Interval(r), Interval(r), Uniform(r, 0.0f, 1.0f) }),
new("vec2 cultmath_iv_snoise_ball(vec3, float)", "ulp-bounded", r => new object[] { Point3(r), Uniform(r, 0.001f, 2.0f) }),
new("vec2 cultmath_iv_fbm_ball(vec3, float, int, float, float)", "ulp-bounded", r => new object[] { Point3(r), Uniform(r, 0.001f, 2.0f), 4, 2.0f, 0.5f }),
new("vec4 cultmath_iv_frustum_ball(vec2, float, float, float, float)", "exact", r =>
{
var slope = new float2(Uniform(r, -1.0f, 1.0f), Uniform(r, -1.0f, 1.0f));
var z0 = Uniform(r, 0.1f, 100.0f);
return new object[] { slope, z0, z0 + Uniform(r, 0.01f, 50.0f), Uniform(r, 0.0001f, 0.05f), Uniform(r, 0.0f, 2.0f) };
}),
new("vec2 cultmath_iv_exp(vec2)", "ulp-bounded, " + PlatformTolerance, r => new object[] { Interval(r) }),
};
private static string GoldenFixture()
@ -141,6 +223,8 @@ public sealed class GlslMirrorTests
json.Append(" {\n");
json.Append($" \"glsl\": \"{family.Glsl}\",\n");
json.Append($" \"tolerance\": \"{family.Tolerance}\",\n");
if (family.Tolerance.Contains(PlatformTolerance))
json.Append($" \"platform\": \"{RuntimeInformation.OSDescription}; {RuntimeInformation.FrameworkDescription}\",\n");
json.Append(" \"cases\": [\n");
for (var p = 0; p < Points; p++)
{

View file

@ -315,13 +315,17 @@ public sealed class HlslSourceCompatibilityTests
/// <summary>
/// The text of <c>CultMath.hlsl</c> with each <c>#include "name"</c> replaced in place by the
/// included file from the same directory, as the shader compiler would resolve it. Only the
/// package's own includes exist, so a missing file is an error rather than a skip.
/// package's own includes exist, so a missing file is an error rather than a skip. An include for
/// which <paramref name="inline"/> returns false is dropped with its line instead (the GLSL
/// lowering's separately licensed files); the C# mirror inlines every one.
/// </summary>
internal static string ReadShaderSource(string cultMathRoot)
internal static string ReadShaderSource(string cultMathRoot, Func<string, bool>? inline = null)
{
var shaders = Path.Combine(cultMathRoot, "shaders");
return Regex.Replace(File.ReadAllText(Path.Combine(shaders, "CultMath.hlsl")), @"(?m)^#include ""([^""]+)""[ \t]*\r?$",
match => File.ReadAllText(Path.Combine(shaders, match.Groups[1].Value)));
return Regex.Replace(File.ReadAllText(Path.Combine(shaders, "CultMath.hlsl")), @"(?m)^#include ""([^""]+)""[ \t]*\r?$(\n?)",
match => inline is null || inline(match.Groups[1].Value)
? File.ReadAllText(Path.Combine(shaders, match.Groups[1].Value)) + match.Groups[2].Value
: string.Empty);
}
/// <summary>The documented HLSL-to-C# transformations, and nothing else.</summary>

View file

@ -6553,6 +6553,531 @@
"41968490 c1ad3591 c0f22840 3f0bd3ed 00000004 40000000 3f000000 -> bff00000 3ff00000",
"419cc42c 41bf0d0c 4103e728 3ec4ecc5 00000004 40000000 3f000000 -> bff00000 3ff00000"
]
},
{
"glsl": "vec4 cultmath_iv_frustum_ball(vec2, float, float, float, float)",
"tolerance": "exact",
"cases": [
"3eba7b90 3eb7145c 427992e9 42ba552a 3c779789 3f90a589 -> 41e2a26a 41de7fa1 429b8f4f 419e80d6",
"3f73f64c 3f0c1560 4222d6fe 4297c95b 3d2b135d 3f4911fc -> 425e3d89 41ff38c0 426934da 41f0be8c",
"bf0b3bac bdf4d708 3fcf79e5 40cf912e 3d3114bd 3e3e1d32 -> c00d1a19 bef82057 4081b7d4 40500a91",
"3f3a6c22 3e316390 42b4bb59 42c660ce 3ca20ba7 3fabaad2 -> 428a094c 418358f4 42bd8e14 410d060f",
"be31a3d8 3d899d70 42748f85 42bfc55b 3cfe1a00 3fce5929 -> c159ec11 40a8d228 429d068f 41b20e9c",
"3f4a9c78 3f411d36 4206979d 42890daa 3c1cb84a 3ed166e1 -> 4221bb7f 421a26c5 424c5978 41d73c63",
"bf6a8307 bf066589 428944ec 428eff1a 3b87959a 3f7e8c43 -> c2805ed0 c21322c4 428c2203 4057d762",
"3e53d0f0 bc876000 41751d2c 4233d09e 3c035ac7 3efddf6e -> 40c77b7a befefc0a 41f117e9 417fd2f9",
"beed4c2a beb0f3c0 42239431 428e1c3a 3d333100 3ffeee4d -> c1cf8ad7 c19ac388 425fe652 41b427bb",
"bed8bcb0 3c3e8000 41e413bf 4298518b 3c26e20b 3fc469e0 -> c1b13b43 3f1bc6db 4251567b 41e1777c",
"3f687470 bedf98e0 4177bf60 42360b63 3d258870 3ff533f9 -> 41dd8aa6 c155198b 41f3fb3b 41c88b16",
"bf62de25 3d7a4040 41c9076e 42835586 3b3af21b 3fa07ae9 -> c220ed38 40318369 42359762 41e46bc0",
"be3ee140 3f1d3204 424a7a45 428de3fc 3ceaff9c 3fe71569 -> c1354881 42154ad0 4273211e 417ea87a",
"3d155040 3f242db2 4216395d 425ede18 3caf3282 3f15b6a9 -> 3fd99b9b 41ef459f 423a8bba 41491a58",
"be5186a0 bed461aa 3eb176b7 418ad2a2 3ca8146d 3faf1262 -> bfe7c7e9 c06af099 410d987d 41317a97",
"3f571914 bf527068 4068a7bb 42464d73 3babcf63 3fcc9d21 -> 41b2d633 c1aef697 41d4d7ef 42153b67",
"bea098aa bf7e7b00 4288d592 429c4d84 3cdbcdc2 3fa0cc15 -> c1b7e4bd c291b2d4 4292918b 41262439",
"3f7ea95c bdadf0f8 41da358f 4284b466 3c2226dc 3e0a9e8e -> 423a4728 c07e7771 423b41ca 41e3353b",
"bca1cf40 3f438e26 42ba9a04 42e0c8f8 3cb28a08 3f434e6d -> c0020326 429d206a 42cdb17e 41739f77",
"be5c8f00 3e3a3608 42ab0a9e 430099fa 3c27696a 3f83e9c8 -> c1b87a7e 419bbfec 42d61f49 41c5cbfd",
"3f692462 3ede1ca8 41439ca8 422240ba 3d367174 3ff75747 -> 41c04d51 4137343b 41d327e4 41beeaf2",
"3e3b5de8 bcc485a0 428cd979 42d37f82 3b5555d3 3f91e546 -> 4180f120 c0073e06 42b02c7e 419b8e6a",
"bf0ff773 beb4875a 41e48a22 4283d273 3c964fe0 3ff81378 -> c1d486fc c1854029 423cf4fc 41cc7754",
"bf759152 3e6e00c0 411b7809 4261d395 3d2e1d59 3f86f841 -> c1fde838 40f615e2 420458cc 4211296a",
"3f007a50 3d9d4c50 4282ea47 42c450a8 3cd80964 3f831c17 -> 422439d0 40c910b1 42a39d78 41af98b7",
"3f636f84 bd16e440 42a01495 42b0f436 3c452c66 3fd19c95 -> 4295b6cf c046a7a8 42a88466 41058ed4",
"3c5cb300 be0de0f4 4262f0a1 429011d6 3c1cf805 3fb554e5 -> 3f5e06cc c10ebb65 4280c513 411d49fc",
"be8b8962 3f1140b4 42a3c75f 42c8185d 3c06d0b1 3e63702a -> c1c655c1 424e75b1 42b5efde 413c603a",
"bf4e97c7 3e94f9d8 41b4cb94 4285cd78 3d2c585b 3f96285a -> c210746b 415055d0 4233005d 4204b12a",
"be25d7fc bf757789 42ad6f1f 42e8db86 3d272309 3f34556b -> c1839a72 c2c2c993 42cb2552 41d16b3d",
"bec0915a 3f600790 42c04fbd 42d38210 3d1c330e 3f94e70e -> c217e150 42b0b1c0 42c9e8e6 413d3025",
"3f0a1670 bf664628 4108b80d 41fede26 3c45ec62 3fef5ae7 -> 412e59b0 c1915f6c 41a19d16 41992df4",
"be486fe8 3edbaa80 422ab7b0 4278545e 3cea40d8 3e078533 -> c1240c4c 41b3c934 42518607 414a2433",
"3ed58464 be65965c 421673f1 42818d9f 3ce4afc5 3fe18959 -> 41aacbf8 c137a6d2 424cc798 4194c7c0",
"beda2f86 bf623449 42802552 429586c3 3d199c25 3edfd7ad -> c1eca815 c2755aa0 428ad60a 412ba98b",
"3f674cd8 3f533df0 427626cd 42908fcf 3d4b8e48 3f013f1d -> 4271d09c 425cd846 4285d19b 41495e4f",
"3efcbb28 bf27f8d8 400fbbc4 4161b481 3cf35cdc 3e66b496 -> 40812616 c0ababf2 4102d1b9 41051545",
"bf726bfc 3e85765c 422265b0 425c792a 3cc69f42 3e81bc02 -> c2354815 41479acc 423f6f6d 413c3ed0",
"bf2de8d0 bf7015c3 42340781 42aa1aec 3d356d0f 3f5b0cde -> c230b549 c273f2de 42820f56 420d08c3",
"3f7412de 3f099c24 42184c5f 42287042 3d395b8a 3fb43acb -> 4218e5b6 41ac6892 42205e50 40c9c110",
"3f61efa0 3de59850 41f11e40 4292e041 3cd31933 3f27ae99 -> 4236d3e6 40b9c9de 424f27d1 41fc315b",
"beb8555c bf20097a 42902d49 42a1926c 3d03734f 3f4c5975 -> c1dc27bc c23f2322 4298dfda 410c0d6b",
"bf691e1b bd69e000 429ef423 42b94118 3cd404b3 3fed5642 -> c29cb877 c09d3ad1 42ac1a9e 41527036",
"bd47db10 3f7e4e1c 4265e4eb 42a6530b 3b34e8d7 3fb7ed3c -> c05b95b7 428bb463 428ca2c0 419e41b1",
"3f09c3fc bf32be2e 4032a729 414285bd 3c443705 3ee68bed -> 4080b78c c0a700bc 40ef2f87 40dad7f2",
"3f3e9f40 3eaa481c 41d63aab 422aa228 3c737515 3d98b369 -> 41ced0e7 4138bf66 420adfbf 412f4330",
"3f1fb802 bf2dc82b 413ae66e 41795cfe 3d46b83f 3f17c941 -> 410817ba c1141357 415a21b6 4080218a",
"be405494 3f791cc8 421c39c0 42265819 3d1af6d1 3f5410aa -> c0f257f4 421cf210 422148ec 4085d752",
"3e8877d8 3edafaec 41ca49f0 423fc5a2 3cf8189f 3fe87ee7 -> 411c25c2 417a8ee2 4212754d 417f43ec",
"bf3d965a be144368 42494b41 429ee0fa 3ba1a96f 3f3a1e60 -> c24032cc c1164e27 4281c34d 419adaa1",
"becc395a bf3bfe4c 42bde57c 42ef73e5 3c781d3b 3ff98f12 -> c22b41b4 c29da564 42d6acb0 419f44e5",
"bf3c61c8 3ee8b028 418d320e 425e105f 3cb92445 3f364f56 -> c1d75c30 41850173 421254b3 41d7fe89",
"3f1f518a be517dec 402f6f49 418520cc 3bb331ca 3e08bdb7 -> 40c0ff0b bffdc68e 411b0eb5 4108873c",
"beaf9b28 3edfeb30 421e2147 428c5912 3ce33d4f 3f862e96 -> c196823f 41bfeaa9 425b69b6 41a42f23",
"3f7230fe be255068 40c7692d 41c1736c 3c1b21c0 3fde40b6 -> 41662e07 c01d1d82 41734db7 41667a4c",
"3d8d7cd0 bf0a62ac 40cee4ba 42305acc 3d1f49ef 3fd6618e -> 3fdf8631 c15a9f99 41ca3763 41c68156",
"be9ca544 3f523dac 428913c2 42b299b0 3d3c1bd5 3d6d10c0 -> c1c1297c 4281a02a 429dd6b9 418fb2e1",
"bf05e0c4 3f408168 41d2f10d 428e5765 3d03997b 3ed42d63 -> c1cc08fa 4212b15a 424313a8 4204514f",
"3f437482 3f3426f8 42004794 42577efe 3c511304 3f41a29a -> 42033c5b 41f1ebf3 422be349 41894800",
"3f7005ae 3ec05170 4267bdef 429e6929 3cd6611c 3fe1d7a1 -> 428094da 41ce0d59 42892410 41979fcc",
"3f53b14c 3f0be0ca 4257fad5 428ac0d7 3c37bddf 3ee28024 -> 424c09b3 4206d1f7 4276be42 4140c8a2",
"bed4a09a bf61eb11 42784d7c 428f119a 3d3ea961 3ec9afe4 -> c1ddf24d c26bd1e4 42859c2c 41254876",
"be7248cc 3f53a884 42a01b57 42c4b295 3c99cc55 3fad8b25 -> c1a8d810 4293804c 42b266f6 417447e1",
"bef485c8 bf5894a5 429b8756 42f0f2e8 3cc3db31 3f2bf933 -> c23d59da c2a7b6ad 42c63d1f 42054dae",
"3dc53f20 bf4d87cc 42c333f6 4303e17f 3d20930f 3e3756fa -> 4130d09b c2b83d9c 42e57b7a 41db217d",
"bef74dce 3f57eea6 421b85ce 429e0d80 3ccde299 3e949ec7 -> c1e3cdbf 4246e7db 426bd067 41f22740",
"bcacdba0 3f5caf3a 4282b80a 4286e9b2 3cf797dd 3f954671 -> bfb35c8e 4264fccf 4284d0de 4092de01",
"3e31d288 3ec55ca4 42b838f1 42c48bbe 3b3809b0 3f2e3fbd -> 41843e91 4212c693 42be6258 4089a73e",
"3e1f74b8 3e52f818 41fb696e 4298f538 3c07c796 3f6410d6 -> 41066c52 4131d96d 4257cf94 41c65214",
"bf4a5ad3 bf61a4da 41ed6d75 421f7761 3d1b9c7c 3e7e4ada -> c1dbe314 c1f531a9 420b170e 411a852d",
"bf027502 3e5acab0 41dcaf58 4258884a 3d3e9953 3c94d55b -> c1a69322 410baec2 42236ffb 418d9f16",
"3f1e1676 3e216db8 4224eeaa 4293fe26 3d3385fa 3f62ec26 -> 420e50ba 411152a8 4266757b 41bc71dd",
"bf736034 bf0fa2e2 423d88ab 42646dd4 3c5c35b7 3fc8cd4c -> c246ad06 c1ea82a0 4250fb40 41194085",
"be2e61d0 be1966c8 4090d234 42113fd8 3bb35275 3eb8f1cc -> c05e8b55 c043c4e3 41a35a1e 4186db1f",
"3f0d4866 be91f542 42979500 42f5dbcc 3d0f1823 3f627d75 -> 425b5786 c1e299a7 42c6b866 4203b3be",
"3f32b786 bedb4b24 418a02c0 422ed72c 3d233756 3fff31a2 -> 41aa3b61 c150e1d2 41f3d88c 41a6afc1",
"bec4a242 bf6357ae 42987da8 42f3b000 3c814be6 3f736e2b -> c2182708 c2afea16 42c616d4 420a648b",
"3f5b6fd6 3f586726 4291640e 42e86e34 3d338823 3ff1d23f -> 42a1edf6 429fb0d2 42bce921 42242731",
"3f71bb6a 3f5227fa 429e6e2b 42d19c23 3d219f6b 3f2b21d0 -> 42adc392 429710f6 42b80527 41ca62c6",
"bf571be5 3e6056a0 4247ddfd 425acec2 3d1ab4de 3f92b3dc -> c22fe65a 4137726a 42515660 40cb2247",
"befe8cbe 3f3b65d2 42b02b2b 43088db7 3d252d55 3f972a0b -> c25f5d85 42a470a1 42e0a34c 421c31af",
"bee9f5ee bc10fec0 4229656f 42845ab2 3d3591a6 3ed87261 -> c1c65db9 bef5df13 42590d6a 4183a56d",
"bf74a6a3 3bbe4800 4132b52b 421e76e1 3d10edb7 3ebd7165 -> c1c222b5 3e16fdfb 41cb242c 41ab91ae",
"bdbcb4e8 3f4fa734 4240a4c3 42b1a182 3d46cb93 3fc9a29c -> c0c9f0b3 425e3723 4288f9f2 420081b3",
"3e9a07a4 bea68b80 42470a0d 42b74464 3cb8733e 3ebf7eae -> 41aa259a c1b7f8b3 428d64b5 41cab2ce",
"bb97c700 bf12b63d 41f16809 421e914a 3d076a8a 3f65854d -> be2592f8 c1a00c54 420ba2a7 40f532d3",
"3e41ffb8 3f6b1d62 413bd8e1 4194788f 3cf59dda 3f42f7ea -> 4037b046 415e9e89 41726500 40bfbca1",
"3d31c600 bc0daf80 40209f69 41fc56df 3cc8ee2d 3f125a99 -> 3f3d2c8a be16c594 41883566 417e03d9",
"bf0d995f 3cb47540 42b4834e 42d3f16b 3d03572d 3fd96e5a -> c2591369 400a52e8 42c43a5c 41613f5a",
"bf50e9b9 bdfc7750 42b31956 42bc6cfe 3c833420 3f700442 -> c295f658 c13539c2 42b7c32a 40af09a3",
"bf502f2f be070110 40976663 4184461b 3d48761c 3fac4249 -> c10a591d bfb36ed9 412a1fb4 411cd1fc",
"3f5214c8 3f2a7fda 42a88172 42eac3b2 3d4782eb 3fbf740f -> 42a577ba 42864aa8 42c9a292 41fa8548",
"bf5ceff2 bebaa414 41f5ef3f 420f0fcf 3c6fd64a 3f732bfa -> c1e597bb c141f3e7 420503b7 409d4b51",
"3f06eb94 bf5c3ca2 42a99885 42bebc80 3cfbcfe3 3ff02e01 -> 423de816 c29aff34 42b42a82 41450d63",
"bedca1a2 bf2209ae 426550b5 429fd72a 3ba232e4 3ee3808a -> c1ec92e5 c22dbf13 42893fc2 4171126d",
"3dd8a5c0 3b56ea00 42801f2f 42c0e718 3b1289d0 3ea8b8d9 -> 4107d693 3e86c058 42a08324 4186a5d7",
"bea988de bedb58cc 424ef45d 428a9d1f 3d09673f 3fa81207 -> c1a052d1 c1cf6de9 4272174e 415a092f",
"bde784e0 3df98600 4256f0e3 4275e52f 3c4a7fb0 3f254dc3 -> c0d0622e 40e096b8 42666b09 40aa7e82",
"3dae8d40 3f7bbcc0 416c94c3 420f41ee 3c878a8c 3eff1685 -> 400a01c4 41c70857 41ca671f 417dd268",
"3e77b7a8 3f43a826 42a40d21 42aee283 3d42dc23 3fcb95b2 -> 41a3fc2f 42818595 42a977d2 41138c90",
"bf4cc979 bda99760 42933734 42d13e49 3bb68839 3f71e998 -> c28e9314 c0ec2456 42b23abe 41ab6b73",
"bf48e6a4 be22514c 41dc85bc 420aafde 3d1cf156 3c36242d -> c1c35dec c09dd8aa 41f8f2bc 40bc8638",
"3f6a846e be33a5fc 422013ff 42465eec 3d1d55a9 3dd96f64 -> 42242f3c c0fb8ac4 42333976 4108e86c",
"be4a1c98 bf3d78dc 42bb9479 42d4985e 3d3a38c0 3e5819f8 -> c19df80a c2941704 42c8166c 414ebf0b",
"bf5984a7 bf3c2fe6 418a515e 4213694a 3abc478f 3ea484ed -> c1b803f5 c19f33b8 41d891f9 417163ad",
"be9f0948 bf0c24c6 42060978 428b3068 3c4f048a 3ed8bd2e -> c1801a7b c1e1c55d 424e3524 41b4f9ac",
"3f576772 3f258474 4234bc5c 4241a76d 3b203676 3fd32f32 -> 421d828b 41f21005 423b31e4 4083ee46",
"bf677596 be4b73e0 41b546a7 41edece4 3cbdd74a 3fead377 -> c1bd8208 c0a693cf 41d199c6 40eb27a4",
"3f32f38e be101180 4138693a 424782ac 3d10e1bc 3fd2493a -> 41abb0d2 c08a3917 41f59cfa 41d7ab9c",
"3f36ff22 3f333640 425bfa20 42ad0686 3cdf9f14 3ffa2888 -> 424a4e9b 42461f7b 428d81cb 41d4df09",
"becc5fc4 3eddabb8 421dd3a0 4234b70b 3d235cf7 3f8a215e -> c1872293 41929266 42294556 40c671b7",
"3eaf3d20 bf06f7bb 41818740 41ccc480 3c5edce2 3ff1ca69 -> 40e4d59a c1303f26 41a725e0 40f953ce",
"3e507960 bea7016a 4106300f 4160a610 3c26d80f 3f822982 -> 40121c0e c06a17aa 41336b10 408608bb",
"3f4267b6 3f5e0efe 4286131d 429f914d 3cde39ad 3f98dd6c -> 425efd8e 427eb5c6 4292d235 41515ce0",
"bf684c68 3e1f20e0 424ff7a2 429b4b09 3d310d0c 3f4d684b -> c26b4598 41212a4d 4281a36d 41acc146",
"3ea60f1c bed69d58 41df2a6d 424e6d06 3d1c8fba 3f2938d3 -> 414e4838 c1854ca9 421f011e 418068cf",
"be0d5b44 3f5e0986 427183ee 42bd638c 3cc113b8 3ee76fd4 -> c12b413b 42868009 429b12c2 41cc2802",
"3f032efa 3ea574e4 417fd8e6 41c3f0ce 3c08b29f 3f3d6832 -> 4125f5a6 40d1517b 41a1eea0 40bd4456",
"3ef433fc 3efa6288 4281a92d 4285c8df 3d19d31d 3f9c804c -> 41fb4e32 4200d563 4283b906 409f7882",
"bec57db6 bf68a26d 418c8a3d 4265a500 3b64d728 3ee0d619 -> c1675e70 c208453b 4215f50f 41e511e4",
"bf75b100 bf2f08fe 42151d13 42968cd4 3cd7be4c 3ffaecc2 -> c2580ad5 c219e99d 42611b5e 42053838",
"3eaa3fe8 bf3ea02e 426556ce 42bdeb19 3d0bc9a0 3f096c57 -> 41ca8ffa c262ce48 42984b40 41e06b41",
"3f4f633e bf1cf456 423991e3 428932fa 3ccc9f37 3fe14abf -> 423a4fdc c20d0105 4265fbec 419a6b26",
"bdfdaf88 3f40b98c 42253c54 42432b8c 3d1eb6ab 3e752f0c -> c0b292e3 4207a990 423433f0 40dac5e6",
"3f01a7e8 3e780e20 42581c66 42aec09d 3c0a2600 3fb7e5ef -> 420f3bb4 418903fa 428d6768 41aa5dff",
"bf304d18 3e895da4 42ad2a68 42d38f7c 3d088ea1 3f1fd6ca -> c28479d4 41ce7035 42c05cf2 4180b244",
"3f352cac 3f7e1f50 415aadc0 41b4f5df 3cceb02c 3d99a7d4 -> 414d72ec 419015d4 41912660 40f686f2",
"3f18125a 3f78da5c 40cb4e94 41cab204 3be1a410 3f8ea160 -> 41169989 417671bb 417d85a9 417ad4f9",
"bf34409a 3f39e924 425b02b3 428affeb 3c9379eb 3ec4277f -> c22ef99a 423477bb 42788144 4141f5b3",
"3f1d37a2 3f3dc374 4219b180 424c35f4 39e26e74 3fe79703 -> 41dbccb9 4204a691 4232f3ba 41298ae9",
"bdf44b38 3f7fabae 42c02677 430ace14 3cf4c524 3dc3aee5 -> c16023c4 42ea93f3 42eae150 420a2ecb",
"be473f94 3d2de880 42531f52 42716594 3cf21237 3f809221 -> c13019d9 4019b465 42624273 40d4afc9",
"3efa1934 bef69a6c 42155ecc 42606496 3c90e8c0 3fc9d999 -> 41b692cd c1b4059b 423ae1b1 415f1119",
"bf62c0d3 be815848 424241e0 42c36550 3cd4f595 3f1b943d -> c2818d6d c193cc87 42924320 4212309d",
"bf284e22 3f38c27c 41dce214 42754831 3cec966d 3fcba4cc -> c1e9de21 42005db1 4231dc9e 41d7573b",
"3f5b6d56 3e4fb4c0 42c5f03b 42dd11ea 3d175bb0 3f811735 -> 42b392e5 41a9fb49 42d18112 414ccb26",
"bf58ad78 3f62885e 421bf330 421ef427 3d4810ce 3f9d6120 -> c205444a 420b53fd 421d73ac 4070e914",
"bf70bbde 3f7e54fc 41daed7a 41ecf107 3ce1e14f 3e4bf610 -> c1d65791 41e2730c 41e3ef40 403b1dc1",
"3f23344c be0b1b3c 424f735c 42a33f5a 3c2151ff 3fa209f3 -> 422a331a c111119e 42857c84 419ea8c5",
"be24d2f0 bdad4c98 42405e06 42a1e00b 3d0c1142 3fbc6520 -> c1262647 c0aeb162 42810787 41a77489",
"3f0b775a 3eea0f40 42bce7a8 43030c31 3c37a7c5 3ffd1f13 -> 4275b349 424e2c73 42e18005 41cf32a4",
"3ccd9680 3cd63780 4250733d 42bf14de 3c756c9c 3f51f0d3 -> 3fed2775 3ff71ba1 4293a73e 41bfd66d",
"be4e69c0 bf6a578c 421efb33 42a6879d 3d4329c3 3f85aff2 -> c1465dd2 c26134e6 42760536 420b584a",
"bf702991 bf3f43e2 41f8c92b 421f956a 3d2f3da4 3e9427ca -> c205343e c1d42ad3 420dfd00 410dd7b6",
"bee40158 beaa91d8 4222ade1 426a0c65 3d264d31 3f0fca3f -> c1b0abfe c1842adb 42465d23 41525397",
"bf73c865 be23eeb4 41cfeb1f 426aa46e 3c47d659 3efe38a3 -> c221389d c0d8d3c6 42294cff 41bf5d06",
"3eae619c becee1c8 429adde8 42b74c81 3cda6951 3d9990a6 -> 41e659c3 c208a430 42a91534 4128ee29",
"3e21ad98 3f668342 41579c48 41971e48 3c21d25c 3e9cafb4 -> 40238643 4169252b 41817636 4085220b",
"bf071c06 3dc95610 424a2252 42c4e2da 3d005723 3f8e8dff -> c21d404c 40ea54c2 4294fa02 41fb184b",
"be1851a8 3e45d968 42aeaa15 42fea5a0 3cba9d5a 3fe3a2ff -> c17f7043 41a5e576 42d6a7da 41ca158d",
"3e294510 bf3036f4 428c589f 42a4578d 3d2d0da9 3fe0d3ca -> 41497680 c251ba86 42985816 41494082",
"bf32bf6e 3efbc45c 42a003cb 42aee3fb 3c253572 3fa7a677 -> c269d797 4224af10 42a773e3 40e2a1d4",
"3efe0ff4 3efcad90 426dd177 4292342a 3cf4cc3a 3fe7b877 -> 42038d99 4202d618 42848e73 41452db9",
"bf7a7965 bf295a1c 415d613e 41d3fc5a 3bc87a44 3f0664d9 -> c19ddaf7 c15575c4 41a1567c 4127c14f",
"3f0d4fda bf5e5f50 429ed973 42e515ee 3c3bd066 3fa55483 -> 425623d9 c2a87d02 42c1f7b0 41de6b15",
"be7fdc74 3f3db342 42a8b8f1 43009b84 3d0fcb74 3f40bffd -> c1d4da6a 429dd035 42d4f7fc 4205690d",
"bea7e01c bea4b8f4 412a4d12 41bb793d 3d440e6f 3f666e84 -> c0b2c6e4 c0af6b52 41884fe3 4110f3b4",
"be966274 bf532fa0 420be4c9 429af20a 3c2d6339 3fe1107f -> c1841c56 c239861d 4260e46e 41f696d2",
"3f6ce8ea bf31c0ff 42c7f3ce 431460da 3d21ac1b 3f120fce -> 42e5d59f c2ac71fb 42f85ac1 422dbef9",
"3f00df9c beae8788 4067a1c9 41cc9399 3cbe6a10 3fc76c5f -> 40eb1fc9 c09f35f3 416987d2 416ff8af",
"bf54e4f9 bdd29d80 424f7372 426e4ba1 3d1e826f 3f28bc74 -> c2395876 c0b75c7e 425edf8a 40ffd630",
"3f1f7382 3f39f60e 4093ca91 4178292a 3d3fdee4 3f03f214 -> 40c89849 40e9f1fc 41210739 410c77f1",
"3f19671c 3f02a0e6 41aa3dbc 425e9ee4 3d3859cc 3d877a71 -> 41b8685c 419d07c8 4219dee1 41c38c6a",
"3f53a9cc 3f5a2264 42ab2654 430351e3 3d17930b 3e196331 -> 42b354a6 42b8d030 42d8e50d 42220f3e",
"3e95908c be3e6674 4103e4b1 4210a074 3bdd11b8 3f3e7228 -> 40cf8563 c0841718 41b199a0 417c1f53",
"bea292c2 3f4b52a8 42af1761 42cfc26a 3c0dd300 3f823647 -> c1f3214d 42980929 42bf6ce6 414a9edb",
"3f5d2424 bf1a1148 41fbc156 4201d229 3b048cc6 3f3198e2 -> 41dce17a c199e2d6 41ffb2d4 3fbcd2fc",
"3f2af89c 3efaeba4 41ad7e3e 426b2afe 3c0b556a 3d8ab058 -> 41d6fe16 419dc378 4220f50e 41c54612",
"bf5a7d38 bf5f1844 422c1293 422c8af9 3cd7a64a 3f89aad0 -> c2130f5d c21628f4 422c4ec6 401372e8",
"3f58c6fe bf295088 428ddb47 42ba3150 3cd07a34 3f3ba454 -> 428ae4d0 c258f783 42a4064c 419af92c",
"bf00ffdc befcac32 42920e49 42d5638c 3d338b8b 3da24fd9 -> c2352029 c23162ea 42b3b8ea 41cad7b7",
"be70e5b4 bd5c9f80 4269a1fe 42bb28dc 3d1ad4bb 3fb36bc5 -> c18f057f c082fc0f 4297fcee 41b83d3c",
"3ee05348 3f49ab34 42b1868e 42e468ae 3b87a5ff 3ffe9ac3 -> 4231dab2 429fe428 42caf79e 419cb4b3",
"3f37b66e 3f4ed99c 41bf7f1e 41d2a285 3cc70b15 3e0d2a4d -> 41904a4e 41a27673 41c910d2 40227bec",
"3f627c2e bea4c6e2 4214e1b3 42586c08 3ca59ed6 3b8d9b84 -> 422197ea c16b2199 4236a6de 414b1864",
"3f196b94 bcb9ae00 428a47c7 42e9088e 3d1e1759 3fc90806 -> 425e870b c006a8b7 42b9a82a 4206c21a",
"3e6f5490 bf2dbda0 42adbf21 42bf689c 3c93d98f 3fb202ae -> 41aab05b c277d255 42b693de 4108dac1",
"bdcc0168 be853b62 41f78cd5 4286c48b 3c6913c3 3fa1b424 -> c09cb6c2 c14cb189 4244a7c0 41a916b9",
"3e99da60 be2ec77c 410ed285 42329b06 3cefb9ab 3f9a9f16 -> 4100cc6b c0925123 41d64fa7 41ab4f60",
"bf3e86ac bf196622 4299b68f 42dbd3f6 3d15cd09 3f898ff5 -> c28b00a8 c25fd4d0 42bac542 41dfa502",
"3eac15a0 bf690f4e 40e2b494 4210a7e8 3d391f41 3f08a12b -> 40e8932e c19d7df5 41acfe7a 41b36e8a",
"bf7aff66 3f0b0850 4145434b 418b9917 3d36b33b 3feb984a -> c1699307 410161a0 416e3abc 40cee096",
"bf3f83d5 3ebabc18 4259e958 42a34a5c 3cf50cc6 3e6b9217 -> c24bab3a 41c695d4 42881f84 41a2c319",
"3f053692 bf12a241 40b63931 422ac5c0 3d0ce444 3ffd0dfa -> 41496eeb c15dba0f 41c18ce6 41d6b2c2",
"3f53deb4 bf545fde 42b4dd8d 42bd9810 3d27efb7 3f92324b -> 42994c9b c299aa10 42b93ace 41064047",
"bf775306 3f695bf8 422c20e6 426e67b0 3cd0a526 3f213766 -> c2464f76 423b1ce6 424d444b 417ec08e",
"3e8072d4 3f3cc4d2 423aae25 424318d0 3d135c9a 3f82f40e -> 413f8eb9 420cc1d0 423ee37a 4083913c",
"3c977140 3dc4a660 42514dff 4272f32e 3cf16286 3fe6ee8d -> 3f85c541 40adb3e9 42622096 40fa3a5b",
"bf617357 3e2d4ec8 427154d5 42ba00e8 3d388b6f 3f846e27 -> c287095f 414f9c26 429955a9 41d9532a",
"bd18c3a0 3f5b33ee 420f400c 421f9233 3d08e15e 3fd18a61 -> bfb4b43d 4201a5a5 42176920 40b50751",
"be8cac80 3f5867d6 42be6415 42f58879 3b8ebd6f 3fefa3e1 -> c1ef8b1b 42b84034 42d9f647 41a6cfb2",
"bc9af820 3f16e2ba 418bd2eb 41e7aa1a 3d3c00b5 3fba90d0 -> bee0e144 415af443 41b9be82 41173533",
"3e986e38 3edf8778 42460dae 42997b76 3c3e3b3b 3e8ea7cb -> 41965a15 41dc7b20 427c824d 41848a50",
"bd8c7390 3efba588 4246e2b4 42475b34 3ac08d91 3ef0a89e -> c05a7da1 41c3bc1a 42471ef4 3f1be83c",
"3f6fb12a be8ba324 40b2a4c5 424cd2b1 3d2e1ecf 3f948cd2 -> 41d4aee2 c0f7ce2e 41e3274a 420ccd43",
"3f6a2f64 3db44b70 41a16ef4 425242b7 3d0b3ede 3fcbdb00 -> 42051747 404ceda8 42117d18 41cafa67",
"3df43e70 3f6a7362 42394d59 4297540e 3d0ae34a 3fcb3647 -> 40e8c67b 425f7125 4273faba 41c0f94b",
"3ee99940 3f29128c 42856d7b 42dcd89a 39a8e24c 3ebafdbe -> 4221a2ee 4269f9ee 42b1230a 41e36792",
"bf74639c 3f5fafbe 428b8d18 42a16246 3cfc828a 3fec04c5 -> c28fa49f 4283798d 429677af 41542173",
"beb67832 be8ee122 42b31132 42fac2fd 3cef369b 3fb8be7e -> c2192f55 c1efe5b3 42d6ea18 41c63b6b",
"3f432e66 3e1ff0e0 42bcf8ad 42f858f8 3c600f47 3f3f62cb -> 42a6b639 41889c98 42daa8d2 41aa0bb1",
"3d6391a0 bf7f618e 407fe383 421bab2d 3d49b348 3f0ba7f7 -> 3f9898db c1ab3f26 41aba965 41d92420",
"3eb4d508 3f420ee8 40dd5182 42284269 3c61acce 3ec9f321 -> 410a6566 419484b4 41c3ec99 41bf11f1",
"3e0bb358 bf4fe0a3 420fee13 429f31de 3d364503 3fe9baf7 -> 40fc4a4e c23bb4eb 426728e8 420677c9",
"3f44010e 3ef4f694 42b9acf7 42d59835 3c860463 3f2339a8 -> 4298d94d 423f0741 42c7a296 413c9db0",
"3f4d5a46 3f2f7bce 42162435 42952e9a 3d109a2f 3e4b9de0 -> 4233e2e4 4219b8af 426040b4 41ee228d",
"3f32af80 bf3b2a36 425f715a 428d0936 3c8e69a3 3bd47f6c -> 42306c17 c238cb4e 427cc1e3 413a7274",
"bf367bc6 beb3db08 42be6b94 42e8e685 3cde349d 3fbc97c3 -> c296e084 c214b44c 42d3a90c 4191943f",
"3f351a78 3e737e68 410bc39f 422e8e58 3bf66112 3f7a77a1 -> 419434a1 40c7433b 41d17f40 41b8aa11",
"be8cd402 bf7b6c19 4289ba60 428ced3a 3d11b3d4 3f8867b8 -> c1994a6d c288d5ff 428b53cd 4096dd79",
"bddf41a0 bf00a5e2 42872368 42cf2a88 3d235b84 3e9180d9 -> c11542d2 c22c04c6 42ab26f8 41c54f29",
"3f4bcb94 3f5efe1e 418b7eeb 4283c160 3d2ef13e 3e8eefd0 -> 4204a642 4211251d 4226a11b 4222399a",
"3e8e637c bee62fb4 42959286 42b92dee 3d15588f 3f90be93 -> 41ba30d7 c2167fa4 42a7603a 41693759",
"3f6c026a bdab7c80 419ec1f8 41eb77fc 3caeaf54 3fb4fa94 -> 41b5b892 c0040a38 41c51cfa 410932f0",
"bf090815 bf1807e2 428adfda 42e36e1e 3c9b943d 3fb2502c -> c244134a c2598993 42b726fc 41ff2e89",
"be36147c 3f22685e 4229aa09 422ec50b 3d2da1f2 3e47fa55 -> c0f4fa88 41da82b6 422c378a 4033f41c",
"beb04164 be1cf30c 425309a6 42ba84c6 3cd81a81 3f9b25bd -> c1c91131 c1330b1b 429204cc 41ca7fb9",
"3e015378 3f4512dc 42990ac0 42f7ba71 3b4760af 3fdf9b82 -> 414a7609 429a42a2 42c86298 42009606",
"3ef761f0 bf7ca7fe 41e0c7b6 4209025e 3b6e3514 3f016556 -> 4171010b c1f62449 41f96639 40a6796c",
"bea8c484 befc0758 42a4b77b 4302c794 3cc83f75 3f981bc2 -> c20c82cb c251d4e2 42d52352 42021a28",
"bdd4e1b0 bf799930 425ef466 429f3044 3ce4cd55 3d966382 -> c0e113c9 c283f2e3 4287553c 41980349",
"3edd2fc8 3e68e568 422b4ee7 4296a852 3ac4ac73 3f1c5306 -> 41cc2cf9 4156fc0f 426c4fc6 419676b7",
"bf6c65b3 bf32b0ae 42ba4f75 42f57a84 3d00dcce 3f8a58d3 -> c2c75cdd c296b237 42d7e4fc 41dc8ed4",
"3f624a5c bedba3d2 4167eea2 425e1c30 3beea70e 3f3e7e65 -> 41f79668 c1704fa3 420c0bec 41ef485f",
"be2b7528 bed8e920 429552e9 42a66d1e 3d0d77f3 3f97f848 -> c15379c5 c205c4c8 429de004 410c27f2",
"bee72e0a be8fea76 42bb1d07 430c346b 3c94c541 3f839f1e -> c25318ef c20369fb 42e9c2ee 41eff0b0",
"bf66dc3d bf53e573 420325b0 428a67aa 3c3e5f4e 3f63f710 -> c237f28d c228d64c 424bfa82 41f3cb95",
"bdde21f8 bf51171d 42c3b27e 43129879 3b8da9ea 3feb7ab3 -> c1541b0d c2c7a6e9 42f471b8 420837f2",
"beb70c1c 3ef96f0c 427a5053 4299785b 3aba11cd 3ecf35f6 -> c1c739ec 4207bd7f 428b5042 410c8b89",
"bdcc8170 3e827c54 42adbca8 42c03de6 3b4d254d 3f0a0149 -> c1122e5a 41ba8afa 42b6fd47 40b45e04",
"bed6ae6c 3db8e9c0 42b8bae9 430611ed 3c9a6899 3fab29f6 -> c23de363 41238ed0 42e26f62 41d46eb9",
"be854e4a bf4a1108 429ddace 42c7999c 3cde951d 3f8ad607 -> c1ba22cc c28d12d2 42b2ba35 418aef9e",
"bf12d47c bf129400 40b1ee60 42220661 3c9a0b00 3dd65745 -> c1535fa7 c15302d2 41b8442d 41badb30",
"be59ebb4 bdf03f38 41a377f5 42770bd2 3d2e1010 3ff2518f -> c10bf009 c09a4640 422463e6 41ce43e0",
"3f6fbad6 3f7b8508 420578c2 428c0e56 3cc635a8 3fbc0891 -> 4241a62d 424b2c37 424ecab7 4208439a",
"3e951670 3f3a7a70 4028e196 40b0fd27 3cb3d3ef 3faf8a5c -> 3f983fe8 403e6eeb 4082b6f9 40552b32",
"3d3185a0 3e64c430 428f3c5f 42ae3c36 3cf82dd1 3ffab316 -> 405c2614 418dd958 429ebc4a 4148bc27",
"bf3d0a9f 3f4eccc8 41a18289 41c2d0de 3b6ab514 3fd00f17 -> c183902e 418fec13 41b229b4 40998f95",
"bf34612c 3f009f30 41caacca 42844b79 3d222462 3f9e8fc1 -> c200eae4 41b7da37 4236f6ac 41f6c0f6",
"bf5e3503 bd942bb0 428b2ab7 42ecde78 3d316137 3f44da12 -> c2a332eb c0d9a57c 42bc0498 421928b9",
"bea85958 3e8bf354 427922db 42820aed 3d1e2583 3fada402 -> c1a76f77 418b30e7 427e9c5a 40ab6191",
"3d0a91c0 bf305956 429b34bd 42c01d2c 3c127ff5 3fc35c69 -> 403bffe5 c26f4184 42ada8f4 41598122",
"bea77dc2 bf552054 42a7475d 42bdb7fc 3d0e9d0e 3ff219f9 -> c1e991f2 c2949ac5 42b27fac 414b8a1a",
"3efb5c1c 3f45312c 42b84ff0 42ec599d 3cf02406 3fbb4e29 -> 424e84cc 42a203a3 42d254c6 41b4615b",
"bd1432a0 3f28a31c 40ec522d 41dd8a66 3d4b1806 3ff95d84 -> bf227362 4138db1b 418c4f79 4177c56f",
"3f7af83c 3db6e620 429538fa 42b660b8 3d4789ee 3ff7ec0c -> 42a28ad1 40ece95a 42a5ccd9 4190158b",
"be929a90 bf58f981 4154d95b 421dcad3 3ce841d3 3fea08e3 -> c0f1ac1f c1b2d6a2 41d3012a 41a3e5b0",
"bf6d1840 3f0eb766 4265c805 42a3d8fe 3cc4f2af 3fb52249 -> c28113b0 421b6486 428b5e80 41ab4450",
"3f10f92a 3f2efadc 4299a986 42e4a639 3c3ef190 3ffb0f59 -> 425880f6 4282a86b 42bf27e0 41e2e2bb",
"bd979428 3f2170ec 40a6ef64 42470814 3ceae80f 3ff13f6a -> c00233ed 418aaca5 41dbe600 41ed89fb",
"3e57c750 bf473a54 4183cd3a 426240d9 3c9ba809 3fa1edc2 -> 40f6409a c1e35d3e 421213bb 41e0b2f6",
"bf77285e bf7d6e99 41309743 4259b5bd 3d31b344 3f9e9930 -> c1fcd020 c2019d8e 4202edc7 42227e76",
"3f20891a bf0dfefe 41ffb971 42015bdc 3cc66812 3fce847b -> 41a14d1e c18eac5f 42009c4a 402901ac",
"bf2f4a96 3f7c215a 420c1793 425530f5 3cff70e8 3e997e01 -> c1f1e783 422df8b6 4230a444 4181da37",
"bf1686a4 bf04f04c 41fb087a 428b474f 3d31e84b 3ea2c24d -> c1ed9792 c1d1d503 424a096e 41dd4108",
"3f559eca bd8b2348 4249a0d0 426f9e4e 3d3bed26 3f691ae3 -> 4238199b c06fd215 425c9f8f 411da3b1"
]
},
{
"glsl": "vec2 cultmath_iv_exp(vec2)",
"tolerance": "ulp-bounded, platform exp",
"platform": "Ubuntu 24.04.5 LTS; .NET 10.0.12",
"cases": [
"41202b2a 4120314e -> 46ade79e 46ae2a6b",
"c0d1a016 bfb9db96 -> 3abb4cbc 3e6fb77f",
"bf8fdd04 bf8cd5fc -> 3ea6664d 3eaa61db",
"40024c0a 40b9ecd3 -> 40f51721 43a6d60d",
"4197c9d9 4197cafb -> 4d25c5b1 4d25dd2e",
"418b9b87 418b9bc1 -> 4c10a577 4c10a991",
"40ceb567 40cf50f9 -> 441fb557 4422c533",
"c0d71f2c c0d71180 -> 3a9dbd63 3a9e00d8",
"41149716 4115dd36 -> 4628ae10 4636a8d3",
"406d8085 406d9a7b -> 422392dc 4223d546",
"bef5bff4 bef5b08c -> 3f1e6964 3f1e6e2a",
"40750628 40750e38 -> 4237f95f 42381090",
"c0e728c3 c0e719d5 -> 3a3f1fca 3a3f790c",
"c18aec70 c13b5136 -> 32f6c219 370a21a5",
"41902341 4190380f -> 4c7ed262 4c80b63b",
"c176d7d2 c174bd5e -> 3456290a 34743f6f",
"c10d5076 c0d80228 -> 39190e2c 3a996ddd",
"c11b8847 c11b8125 -> 387bc1f8 387c324f",
"c06ec60d c06eb253 -> 3cc46278 3cc49f0b",
"bebf1b28 3fff14ca -> 3f3040e5 40eac236",
"41284f27 412dd379 -> 4710a018 474c2b74",
"c14fa678 c14fa5ac -> 361b0a4e 361b1209",
"c0fad4ed c0fab9bb -> 39ceb51b 39cf6513",
"c17de632 c17db6c8 -> 3409c9b1 340b6464",
"414f6c8c 41531eac -> 48d06242 490343fa",
"40e7d966 413623fd -> 44af2f87 47aba695",
"4070804e 40800e69 -> 422b6be7 425ac6f1",
"41939c17 41977345 -> 4cc4a616 4d1ee90f",
"40b28176 40b2921a -> 43844fa5 43849487",
"409c38a3 409c42f5 -> 4303e1fe 43040c90",
"c0d1e402 c0cc5be6 -> 3ab9c0d3 3adcce50",
"41947227 4195082d -> 4cda50ce 4ceae88e",
"c19594e2 c1958d68 -> 32023b0e 3202b4fe",
"40f50c60 410a2aac -> 4504502d 45afdbe3",
"c140d47e c140d36a -> 36c3be27 36c3cb5a",
"4157dcae 41587a42 -> 49308d18 493779c5",
"c18e5e55 c18b41ad -> 32a06b8f 32ecb2ea",
"bfbee7f0 bf3151e0 -> 3e66724b 3f00101e",
"41583ab2 415903ee -> 4934a680 493dbf51",
"4155faca 4155fb46 -> 491cf494 491cf956",
"c1642b98 c15d8292 -> 352bff46 3582660f",
"c170f311 c170e907 -> 349ac48d 349b25c9",
"40d7b019 40d7b79f -> 44537137 4453a2f5",
"c197fc9c c196e2aa -> 31c0d460 31dd4a53",
"417d6191 417e0607 -> 4ae63cd8 4aefab93",
"408b17d2 408b1c2e -> 429a709b 429a85a9",
"4070bc94 4070f07c -> 422c0da3 422c9969",
"3fbb7abf 3fbb87c1 -> 408a7002 408a7e16",
"411a81e4 411c27dc -> 46742941 468753f5",
"c1bc5882 c167813d -> 2e8314cf 350ba3e2",
"4080a820 4080c3b0 -> 425eebc5 425fac1c",
"c13b3160 c132dfce -> 370b358e 376a223e",
"41565c23 4156610d -> 4920baf8 4920ec5f",
"4154b6dc 417c728c -> 49110553 4ad92fd3",
"c13cf6ed c13cf369 -> 36f93c12 36f972de",
"417e9504 417e9654 -> 4af82f45 4af843a3",
"c03dd70d c036ea23 -> 3d52ed7e 3d6b086f",
"c189a001 c188ae87 -> 33111f87 332348b9",
"3f52dca6 3f8c44ed -> 4011d8fd 403f7897",
"417629fd 4187c3a2 -> 4a92a631 4bb2ef53",
"c1091e4a c1091d4e -> 3946f2f5 3946ff35",
"417f141f 41818095 -> 4b0000e3 4b2399cf",
"bf91ec53 bf59891a -> 3ea3be49 3edae43d",
"4184d364 4189dc9c -> 4b77dc1a 4be8937a",
"c14bf8de c14be10a -> 36431c63 36443fd0",
"c193ea76 c193e75e -> 32206043 32209e56",
"be900eda 3fad7536 -> 3f41378f 407825b7",
"c08794db c025ce1a -> 3c6cc82d 3d99886a",
"3ec7f803 3ecc0d7d -> 3fbd2902 3fbeacd1",
"c012cb8e c0119f12 -> 3dcea215 3dd27533",
"c0daaa3f c0daa5c1 -> 3a8d34be 3a8d4894",
"c050634f c039c7b1 -> 3d1ddc02 3d60bdf3",
"409cd424 409dee6c -> 43066900 430b1f58",
"408cef3e 415094cd -> 42a3968b 48e003a2",
"c14e4c28 c10ce1c8 -> 3628b807 391d3f60",
"40752252 4075256e -> 42384a67 4238535d",
"40fd9a10 40fda0f0 -> 452cdb86 452d00af",
"41036130 41040d3c -> 456620f1 45700037",
"c077e68c c074d5f4 -> 3caa48ab 3cb2a306",
"c1147257 c114703b -> 38c40310 38c41ceb",
"c1048df6 c1048c9c -> 39844f07 39845a37",
"40dfedd6 410d03dd -> 4488c689 45d22042",
"c1624386 c15f0030 -> 3541c354 356d98cb",
"405e12af 40636bf1 -> 4200886a 420bbc84",
"c14500ef c144fb45 -> 3696ccaf 3697021d",
"c17d28a2 c17d267e -> 34105085 341063d7",
"c123eec5 c123a46f -> 3814ecdd 3817a718",
"3eddffe7 3ede4019 -> 3fc57a2b 3fc592f1",
"40c3acf4 40c3b46c -> 43e24452 43e2792a",
"c1a9f6d8 c165a819 -> 30230d14 351cbd2c",
"4184c5e2 41aaf14e -> 4b763af9 4ee31c73",
"3f9ec35a 3f9f0406 -> 405d3bd5 405dabba",
"40c71490 4165a95c -> 43fbab24 49d1201c",
"3fc1015d 3fc19163 -> 40908b7c 40912e7d",
"c17ee168 c151c9c2 -> 34019755 3607a61b",
"c0c3c1df 3fa3ddfc -> 3b107367 40663bd7",
"c17826ef c17819e9 -> 34455621 3445f704",
"c159be3f c159b8bf -> 35a50371 35a53c36",
"c0020079 bffad0ae -> 3e0650d0 3e104fe8",
"bf44e41c 40b3c1d4 -> 3eed4658 43899686",
"41864aa6 41864b9a -> 4b94d9e9 4b94eba8",
"413cd099 413cd103 -> 48023ffc 4802435d",
"414b49e2 414b9486 -> 48a0ebfd 48a3e196",
"414771e6 414778e2 -> 487d1ce3 487d8b7a",
"40dd96fa 40dfcc76 -> 447e4461 4488382e",
"416ded4c 416df1fc -> 4a2f472a 4a2f7a8d",
"bf429f16 bf41ceea -> 3eef6343 3ef0263e",
"c154a284 c1548980 -> 35e3142d 35e4784d",
"40394b3b 403a4ab5 -> 4190b309 4192f92f",
"c181b997 c181b831 -> 33c2cb42 33c2ed54",
"c1708f7c c1708764 -> 349e939c 349ee3ea",
"c006ab5a c0067636 -> 3df9bcce 3dfa8c83",
"4138ad4a 4138af22 -> 47c922dd 47c93a0e",
"c1aaddba c18114f2 -> 3011aaed 33d319ca",
"c1052891 c1051da3 -> 397ed0c3 397f7f11",
"40f07cfb 40f07f15 -> 44e57ad1 44e589e6",
"40a83277 40a834e1 -> 433fbe6b 433fcce4",
"c1970cb1 c18a3aaf -> 31d8cba2 330690ee",
"c1664f64 c16597a8 -> 35167759 351d5e8f",
"c016aa8e c01673a2 -> 3dc28157 3dc3288b",
"c152f8e3 c152ed29 -> 35fbf1e6 35fcaad3",
"40c4c97f 40c707f1 -> 43ea43a1 43fb47f7",
"c19a2cb6 c19a29e4 -> 3192b092 3192e454",
"bf5cb30b 3e91dd16 -> 3ed833f5 3faa30d9",
"c173577e c162d986 -> 3485462a 353acbab",
"40c3d3a0 40c3d490 -> 43e35669 43e35d14",
"bf70eaa9 bf6e8997 -> 3ec7c912 3ec9a69e",
"4156037d 41593933 -> 491d4a01 49403b2d",
"40edd117 40eebf99 -> 44d3199b 44d95626",
"c13a6ff3 c13611a9 -> 3711f0d7 373fc165",
"c0886ac7 c004b4a2 -> 3c66adc4 3e00c274",
"41915f3c 41917138 -> 4c94aa97 4c95fa47",
"416c02e9 417a49a7 -> 4a1b802a 4abdc33b",
"404d6730 404df460 -> 41c61e4c 41c7d540",
"c11e8199 c11e6ff3 -> 38510e1b 3851f534",
"c1357761 c13576b7 -> 37471dab 374725f1",
"419b9eea 41a2b6d6 -> 4d85d234 4e22655d",
"4199121e 41a4bcae -> 4d42976e 4e511dca",
"403ceedf 40ad4180 -> 41992a72 43609544",
"c10ac2eb c109be27 -> 39338851 393f5577",
"40b06b2d 40b0756b -> 4377ea0c 43783974",
"4146c423 414736b5 -> 4872998b 48797b3f",
"40a579c5 40ad2ef3 -> 43301c69 43601338",
"40867c7c 40868924 -> 4285bb91 4285f082",
"c11b8dde c11b6a62 -> 387b6a13 387d9a15",
"c15e5062 c15b8992 -> 3578044a 3593823d",
"c0f20ac4 c0df35c8 -> 3a080645 3a7504c4",
"4175e861 4175f5af -> 4a9051a2 4a90c9d8",
"c0d0b202 c0d0a082 -> 3ac0d2ae 3ac13c40",
"40f23807 40f24359 -> 44f23b80 44f29142",
"40bcb7fe 40bcbac2 -> 43b60e43 43b61e01",
"3fec5a99 403a96c4 -> 40cace61 4193a841",
"4058f90a 406037f6 -> 41ed6007 4204ea3f",
"c19c9154 c19a637e -> 315987b7 318ed16a",
"c083c2e8 c083c20c -> 3c85670b 3c856aa2",
"c1927ebc c189bbdc -> 323f8b7c 330f29a6",
"c13bc94e c138e5ba -> 370623e5 3720af66",
"c1903287 c1903073 -> 327f45d2 327f882c",
"c18dcc20 c18da0d0 -> 32ac4a91 32aff94d",
"41241856 41241c9a -> 46de4628 46de8174",
"c149a219 c1498cf3 -> 3661d2b7 3662fdfc",
"c17daadb c15be833 -> 340bcc6f 359023d4",
"c1526edf c1515197 -> 36024a19 360baffd",
"410f5073 410f5735 -> 45f2990a 45f2ff98",
"418081a1 4182dfcb -> 4b107375 4b4234b8",
"be2f2f61 be2a219f -> 3f57bec7 3f58d00a",
"c147088e c14707c6 -> 3684d52f 3684dbae",
"418415d1 41847393 -> 4b61f244 4b6c87da",
"40cca9b9 40ccaa97 -> 4415d180 4415d592",
"418103b8 418103fc -> 4b19ec9e 4b19f1bc",
"40915d28 4106f73c -> 42bbdf9c 458ff907",
"415a09c0 415a1148 -> 494a45ac 494aa4fb",
"c0d79718 c0d76aa4 -> 3a9b728e 3a9c4b17",
"c022fa3b c0152575 -> 3da077df 3dc72e06",
"4100d622 4104fde6 -> 45444f18 457e8659",
"c1356667 c110c6bd -> 3747f160 38f68d2d",
"3ebbca75 3ec9858b -> 3fb8b6cf 3fbdbc1c",
"c19237e7 c192310d -> 32464918 3246f335",
"419707fd 4199690f -> 4d16cce4 4d4b07b9",
"40ab2960 40ab2a60 -> 43525b19 435261ad",
"4171dd3d 417d47d3 -> 4a602e46 4ae4cb97",
"4131b591 4136ef6f -> 47822040 47b4644a",
"3e10a062 3f06df68 -> 3f936ad1 3fd8c755",
"409828ee 40982db2 -> 42e85391 42e8762f",
"411800b7 41189eb9 -> 4650c83b 4658fe59",
"3fd45d75 3fd4636b -> 40a8259a 40a82d71",
"400f4bec 402d8b74 -> 411624f8 4170dcbf",
"3fd386e5 3fd3cdbb -> 40a70caa 40a76938",
"bf91ee69 3fa50909 -> 3ea3bb9e 40685834",
"3f29298b 3fff963a -> 3ff7d963 40ebb013",
"c16d1803 c16d1739 -> 34c4f0fc 34c4fab4",
"3fc7105d 3fc73ac3 -> 40978d13 4097bf50",
"c00692a2 bffc4cbd -> 3dfa1d56 3e0ea5e3",
"41500857 41554a31 -> 48d87662 49165507",
"c19c5c71 c19b7aa9 -> 315f3881 31793cc7",
"bf8bdf6c 40b3764b -> 3eabab4a 4388533e",
"c0cf7436 c0737944 -> 3ac87344 3cb67ab6",
"c180611b c152db2e -> 33e679ea 35fdc764",
"c168c603 c168c403 -> 3500fedf 35010f02",
"411b944b 411d77cd -> 46828a0c 4692e51f",
"4111ea58 4111f560 -> 460eb64f 460f18d8",
"418942a2 41894466 -> 4bd7bb33 4bd7ead7",
"c165800d c1657c43 -> 351e4765 351e6ce7",
"c0ff15d8 c0e732e0 -> 39b4fa92 3a3ee36f",
"418b2695 41913acb -> 4c089e3d 4c920b63",
"4141eb39 4141ec7f -> 4833310b 48333f51",
"c1618094 c14fd848 -> 354b353c 36192a8e",
"416d127a 4181d013 -> 4a2628ec 4b2a1365",
"c12c2882 c12b27f0 -> 37b22074 37bda432",
"c132c5f9 c10872dd -> 376b9d70 394f73c3",
"415a1b25 418b5009 -> 494b220b 4c0b695d",
"c1664144 c1663f72 -> 3516fc69 35170d99",
"4193287c 4193c44c -> 4cb9db17 4cc88c31",
"40e35323 40eb070d -> 4498166f 44c17aa3",
"407ae7f7 4083215c -> 4249af1d 4270d60b",
"c16c6d87 c16c5279 -> 34cd4fbb 34ceac0d",
"c1854c1f c14bc377 -> 33794569 3645abde",
"c1878438 c174a2d0 -> 333ce31c 3475d61f",
"c16e15b2 c16dfe3c -> 34b91d2a 34ba2d63",
"c186191d c1861537 -> 33618758 3361f55c",
"c0ee532b c0ee51a1 -> 3a18c798 3a18cef4",
"4125bcb1 4125c2ef -> 46f64bf5 46f6ac20",
"41435826 4143a7e2 -> 4843e39d 4847bd60",
"4141e5b9 4142f0c7 -> 4832f37d 483f01e0",
"419f32eb 419f39c5 -> 4dd14cf3 4dd20083",
"40cb4342 40cdb75e -> 440f676c 441ad4f1",
"4165e3b7 4182eb9d -> 49d4204a 4b43547f",
"41079ed6 411a174a -> 4595fc6e 466de380",
"c16a9ed5 c15ab975 -> 34e5dd5c 359b327a",
"c12f9849 c12e8603 -> 378fb266 3799a604",
"3edb505a 3f136953 -> 3fc471ac 3fe3a921",
"c1506cd1 c15060a7 -> 3613b61c 36142695",
"40b9f7e9 40ba784f -> 43a70fe1 43a9b37f",
"419521ae 41952506 -> 4cedda1d 4cee3d9e",
"41074ff8 410750a8 -> 45932031 45932685",
"40d9f8c3 4115426e -> 4463159f 462fe2c7",
"c0b7ab40 40806080 -> 3b52c17e 425cfafe",
"4005e1fc 40640484 -> 41019b63 420d0b33",
"c1762c19 c1416a91 -> 345f546a 36bcb364",
"c0e1be65 c0e0b257 -> 3a625df5 3a69e58a",
"412e612d 412e62c3 -> 47535b4e 47537044",
"40e4c428 40e4dcb8 -> 449f1864 449f92b3",
"419c93fb 419d0c35 -> 4d96d4f0 4d9ff38c",
"bf4f1957 bf4e8269 -> 3ee3ffea 3ee48680",
"c144d67d c144cba1 -> 36985ed0 3698c65f",
"40fcf00d 40fd1413 -> 45294e9f 452a0da4",
"c170377a c16f9c1a -> 34a2054c 34a848e7",
"4119be6c 411dc410 -> 4668c871 4695a7d5",
"41732663 41737c65 -> 4a72f06f 4a78182a",
"40690b7e 40697ad2 -> 421891cf 42199c1e",
"beb85bd2 be97a02e -> 3f329775 3f3e61d7",
"41325a08 4187bbc4 -> 4787750a 4bb23fb3",
"406ea634 40909ea6 -> 4226883c 42b78e0b",
"c18b47ee c16b52c0 -> 32ebfa26 34dbfc84",
"c11e1519 c0deff46 -> 3856aab4 3a76a784",
"40c786cc 40c7941c -> 43ff33d9 43ff9e1c"
]
}
]
}