From c7b941823ff0146723d6c43dc70b381000849986 Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 14 Apr 2026 14:20:26 +0000 Subject: [PATCH 1/4] Add tests to verify precalculated factorial and Catalan constants Adds two tests that independently compute factorial and Catalan numbers using BigInteger arithmetic and verify they match the precalculated constants in Math._factorials and Math._catalans. Computations that exceed 10 seconds are skipped with a note to use external resources (OEIS A000142 for factorials, OEIS A000108 for Catalan numbers). Fixes #59 Co-Authored-By: Claude Sonnet 4.6 --- csharp/Platform.Numbers.Tests/MathTests.cs | 83 ++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/csharp/Platform.Numbers.Tests/MathTests.cs b/csharp/Platform.Numbers.Tests/MathTests.cs index b5f38cd..f928aa8 100644 --- a/csharp/Platform.Numbers.Tests/MathTests.cs +++ b/csharp/Platform.Numbers.Tests/MathTests.cs @@ -1,10 +1,52 @@ using System; +using System.Diagnostics; +using System.Numerics; using Xunit; namespace Platform.Numbers.Tests { public static class MathTests { + private static readonly TimeSpan ComputationTimeLimit = TimeSpan.FromSeconds(10); + + /// + /// Computes factorial of n using BigInteger so it never overflows. + /// Returns null if computation exceeds the time limit. + /// + private static BigInteger? ComputeFactorialWithTimeLimit(ulong n) + { + var stopwatch = Stopwatch.StartNew(); + BigInteger result = BigInteger.One; + for (ulong i = 2; i <= n; i++) + { + result *= i; + if (stopwatch.Elapsed > ComputationTimeLimit) + { + return null; + } + } + return result; + } + + /// + /// Computes the nth Catalan number from scratch using the formula: + /// C(n) = (2n)! / ((n+1)! * n!) + /// Returns null if computation exceeds the time limit. + /// + private static BigInteger? ComputeCatalanWithTimeLimit(ulong n) + { + var twoNFact = ComputeFactorialWithTimeLimit(2 * n); + if (twoNFact is null) return null; + + var nPlusOneFact = ComputeFactorialWithTimeLimit(n + 1); + if (nPlusOneFact is null) return null; + + var nFact = ComputeFactorialWithTimeLimit(n); + if (nFact is null) return null; + + return twoNFact / (nPlusOneFact * nFact); + } + [Theory] [InlineData(0ul, 1ul)] [InlineData(1ul, 1ul)] @@ -107,5 +149,46 @@ public static void MaximumConstantsTest() Assert.Equal(20ul, Math.MaximumFactorialNumber); Assert.Equal(36ul, Math.MaximumCatalanIndex); } + + [Fact] + public static void PrecalculatedFactorialsMatchComputedValues() + { + // Verify that every precalculated factorial constant in Math._factorials is actually + // a correct factorial value, computed independently using BigInteger arithmetic. + // Only computes values that finish within 10 seconds; larger values would need + // verification via an external trusted resource (e.g. OEIS A000142: https://oeis.org/A000142). + for (ulong n = 0; n <= Math.MaximumFactorialNumber; n++) + { + var computed = ComputeFactorialWithTimeLimit(n); + if (computed is null) + { + // Computation exceeded 10 seconds — skip and rely on external verification. + continue; + } + var precalculated = Math.Factorial(n); + Assert.Equal((ulong)computed, precalculated); + } + } + + [Fact] + public static void PrecalculatedCatalansMatchComputedFactorials() + { + // Verify that every precalculated Catalan constant in Math._catalans is actually + // a correct Catalan number, computed from scratch using the formula: + // C(n) = (2n)! / ((n+1)! * n!) + // Only computes values that finish within 10 seconds; larger values would need + // verification via an external trusted resource (e.g. OEIS A000108: https://oeis.org/A000108). + for (ulong n = 0; n <= Math.MaximumCatalanIndex; n++) + { + var computed = ComputeCatalanWithTimeLimit(n); + if (computed is null) + { + // Computation exceeded 10 seconds — skip and rely on external verification. + continue; + } + var precalculated = Math.Catalan(n); + Assert.Equal((ulong)computed, precalculated); + } + } } } \ No newline at end of file From 697cfea6f317a44693132e684ab3d1bcaf2ae612 Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 14 Apr 2026 14:26:44 +0000 Subject: [PATCH 2/4] Fix Codacy static analysis: remove XML docs from private methods and add missing newline Replace XML summary comments on private helper methods with regular line comments (matching existing code style in BitTests.cs/SystemTests.cs), and add missing newline at end of file. Co-Authored-By: Claude Sonnet 4.6 --- csharp/Platform.Numbers.Tests/MathTests.cs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/csharp/Platform.Numbers.Tests/MathTests.cs b/csharp/Platform.Numbers.Tests/MathTests.cs index f928aa8..db9bdc9 100644 --- a/csharp/Platform.Numbers.Tests/MathTests.cs +++ b/csharp/Platform.Numbers.Tests/MathTests.cs @@ -9,10 +9,8 @@ public static class MathTests { private static readonly TimeSpan ComputationTimeLimit = TimeSpan.FromSeconds(10); - /// - /// Computes factorial of n using BigInteger so it never overflows. - /// Returns null if computation exceeds the time limit. - /// + // Computes factorial of n using BigInteger so it never overflows. + // Returns null if computation exceeds the time limit. private static BigInteger? ComputeFactorialWithTimeLimit(ulong n) { var stopwatch = Stopwatch.StartNew(); @@ -28,11 +26,9 @@ public static class MathTests return result; } - /// - /// Computes the nth Catalan number from scratch using the formula: - /// C(n) = (2n)! / ((n+1)! * n!) - /// Returns null if computation exceeds the time limit. - /// + // Computes the nth Catalan number from scratch using the formula: + // C(n) = (2n)! / ((n+1)! * n!) + // Returns null if computation exceeds the time limit. private static BigInteger? ComputeCatalanWithTimeLimit(ulong n) { var twoNFact = ComputeFactorialWithTimeLimit(2 * n); @@ -191,4 +187,4 @@ public static void PrecalculatedCatalansMatchComputedFactorials() } } } -} \ No newline at end of file +} From 7ba276719301d2a5ac0525ef85bba5ea27cca24f Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 14 Apr 2026 14:31:25 +0000 Subject: [PATCH 3/4] Fix Codacy code style: add curly braces to if statements in ComputeCatalanWithTimeLimit Co-Authored-By: Claude Sonnet 4.6 --- csharp/Platform.Numbers.Tests/MathTests.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/csharp/Platform.Numbers.Tests/MathTests.cs b/csharp/Platform.Numbers.Tests/MathTests.cs index db9bdc9..f9126cc 100644 --- a/csharp/Platform.Numbers.Tests/MathTests.cs +++ b/csharp/Platform.Numbers.Tests/MathTests.cs @@ -32,13 +32,22 @@ public static class MathTests private static BigInteger? ComputeCatalanWithTimeLimit(ulong n) { var twoNFact = ComputeFactorialWithTimeLimit(2 * n); - if (twoNFact is null) return null; + if (twoNFact is null) + { + return null; + } var nPlusOneFact = ComputeFactorialWithTimeLimit(n + 1); - if (nPlusOneFact is null) return null; + if (nPlusOneFact is null) + { + return null; + } var nFact = ComputeFactorialWithTimeLimit(n); - if (nFact is null) return null; + if (nFact is null) + { + return null; + } return twoNFact / (nPlusOneFact * nFact); } From 00f106bb9cbc3fe22172ffc4a3cc23576825a510 Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 14 Apr 2026 14:55:00 +0000 Subject: [PATCH 4/4] Verify precalculated constants against OEIS via HTTP when computation times out When local BigInteger computation exceeds the 10-second limit, the expected value is now fetched from the authoritative OEIS b-files via real HTTP requests: - Factorials: OEIS A000142 (https://oeis.org/A000142/b000142.txt) - Catalan numbers: OEIS A000108 (https://oeis.org/A000108/b000108.txt) This replaces the previous behaviour of silently skipping those values. Co-Authored-By: Claude Sonnet 4.6 --- csharp/Platform.Numbers.Tests/MathTests.cs | 74 ++++++++++++++++++---- 1 file changed, 61 insertions(+), 13 deletions(-) diff --git a/csharp/Platform.Numbers.Tests/MathTests.cs b/csharp/Platform.Numbers.Tests/MathTests.cs index f9126cc..cb68211 100644 --- a/csharp/Platform.Numbers.Tests/MathTests.cs +++ b/csharp/Platform.Numbers.Tests/MathTests.cs @@ -1,6 +1,9 @@ using System; +using System.Collections.Generic; using System.Diagnostics; +using System.Net.Http; using System.Numerics; +using System.Text.RegularExpressions; using Xunit; namespace Platform.Numbers.Tests @@ -8,6 +11,39 @@ namespace Platform.Numbers.Tests public static class MathTests { private static readonly TimeSpan ComputationTimeLimit = TimeSpan.FromSeconds(10); + private static readonly HttpClient HttpClient = new(); + + // Fetches all entries from an OEIS b-file (plain text format: "n value" per line). + // Returns a dictionary mapping index -> value as BigInteger. + // The b-file URL format is: https://oeis.org/AXXXXXX/b000XXX.txt + private static Dictionary FetchOeisSequence(string bFileUrl) + { + var result = new Dictionary(); + string content = HttpClient.GetStringAsync(bFileUrl).GetAwaiter().GetResult(); + foreach (var line in content.Split('\n')) + { + var trimmed = line.Trim(); + if (trimmed.StartsWith("#") || trimmed.Length == 0) + { + continue; + } + var parts = Regex.Split(trimmed, @"\s+"); + if (parts.Length >= 2 && ulong.TryParse(parts[0], out var index) && BigInteger.TryParse(parts[1], out var value)) + { + result[index] = value; + } + } + return result; + } + + // Lazily-fetched OEIS sequences, cached for the lifetime of the test run. + private static Dictionary? _oeisFactorials; + private static Dictionary OeisFactorials => + _oeisFactorials ??= FetchOeisSequence("https://oeis.org/A000142/b000142.txt"); + + private static Dictionary? _oeisCatalans; + private static Dictionary OeisCatalans => + _oeisCatalans ??= FetchOeisSequence("https://oeis.org/A000108/b000108.txt"); // Computes factorial of n using BigInteger so it never overflows. // Returns null if computation exceeds the time limit. @@ -159,19 +195,25 @@ public static void MaximumConstantsTest() public static void PrecalculatedFactorialsMatchComputedValues() { // Verify that every precalculated factorial constant in Math._factorials is actually - // a correct factorial value, computed independently using BigInteger arithmetic. - // Only computes values that finish within 10 seconds; larger values would need - // verification via an external trusted resource (e.g. OEIS A000142: https://oeis.org/A000142). + // a correct factorial value. For values that can be computed locally within 10 seconds, + // an independent BigInteger computation is used. For values that take longer, the + // expected value is fetched from OEIS A000142 (https://oeis.org/A000142) via HTTP. for (ulong n = 0; n <= Math.MaximumFactorialNumber; n++) { var computed = ComputeFactorialWithTimeLimit(n); + BigInteger expected; if (computed is null) { - // Computation exceeded 10 seconds — skip and rely on external verification. - continue; + // Computation exceeded 10 seconds — verify against OEIS A000142. + Assert.True(OeisFactorials.TryGetValue(n, out expected), + $"OEIS A000142 did not contain a value for n={n}"); + } + else + { + expected = computed.Value; } var precalculated = Math.Factorial(n); - Assert.Equal((ulong)computed, precalculated); + Assert.Equal((ulong)expected, precalculated); } } @@ -179,20 +221,26 @@ public static void PrecalculatedFactorialsMatchComputedValues() public static void PrecalculatedCatalansMatchComputedFactorials() { // Verify that every precalculated Catalan constant in Math._catalans is actually - // a correct Catalan number, computed from scratch using the formula: - // C(n) = (2n)! / ((n+1)! * n!) - // Only computes values that finish within 10 seconds; larger values would need - // verification via an external trusted resource (e.g. OEIS A000108: https://oeis.org/A000108). + // a correct Catalan number. For values that can be computed locally within 10 seconds + // using the formula C(n) = (2n)! / ((n+1)! * n!), an independent BigInteger computation + // is used. For values that take longer, the expected value is fetched from OEIS A000108 + // (https://oeis.org/A000108) via HTTP. for (ulong n = 0; n <= Math.MaximumCatalanIndex; n++) { var computed = ComputeCatalanWithTimeLimit(n); + BigInteger expected; if (computed is null) { - // Computation exceeded 10 seconds — skip and rely on external verification. - continue; + // Computation exceeded 10 seconds — verify against OEIS A000108. + Assert.True(OeisCatalans.TryGetValue(n, out expected), + $"OEIS A000108 did not contain a value for n={n}"); + } + else + { + expected = computed.Value; } var precalculated = Math.Catalan(n); - Assert.Equal((ulong)computed, precalculated); + Assert.Equal((ulong)expected, precalculated); } } }