From 94b314df237721225a893b3bc82ea3039abb6c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Sat, 20 Sep 2025 10:50:10 +0200 Subject: [PATCH] Expand GPU coverage for even perfect candidate tests --- EvenPerfectBitScanner.Tests/ProgramTests.cs | 138 ++++++++++++++++++ .../MersenneNumberResidueCpuTesterTests.cs | 12 ++ .../MersenneNumberResidueGpuTesterTests.cs | 14 ++ .../MersennePrimeTestData.cs | 25 ++++ 4 files changed, 189 insertions(+) create mode 100644 PerfectNumbers.Core.Tests/MersennePrimeTestData.cs diff --git a/EvenPerfectBitScanner.Tests/ProgramTests.cs b/EvenPerfectBitScanner.Tests/ProgramTests.cs index ceef9af..c4e4182 100644 --- a/EvenPerfectBitScanner.Tests/ProgramTests.cs +++ b/EvenPerfectBitScanner.Tests/ProgramTests.cs @@ -1,6 +1,7 @@ using FluentAssertions; using Xunit; using System; +using System.Collections.Generic; using System.Numerics; using System.Reflection; using System.Text; @@ -398,6 +399,143 @@ public class ProgramTests } } + [Theory] + [MemberData(nameof(KnownMersennePrimeExponents))] + public void IsEvenPerfectCandidate_accepts_known_small_mersenne_primes(ulong exponent) + { + var useDivisorField = typeof(Program).GetField("_useDivisor", BindingFlags.NonPublic | BindingFlags.Static)!; + var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!; + var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!; + var residueField = typeof(Program).GetField("PResidue", BindingFlags.NonPublic | BindingFlags.Static)!; + var gcdFilterField = typeof(Program).GetField("_useGcdFilter", BindingFlags.NonPublic | BindingFlags.Static)!; + var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu"); + + useDivisorField.SetValue(null, false); + gcdFilterField.SetValue(null, false); + mersenneField.SetValue(null, new ThreadLocal(() => new MersenneNumberTester( + useIncremental: true, + useGpuScan: false, + useGpuOrder: false, + useResidue: true, + maxK: 1_024UL, + residueDivisorSets: 1UL), trackAllValues: true)); + primeField.SetValue(null, new ThreadLocal(() => new PrimeTester(), trackAllValues: true)); + residueField.SetValue(null, new ThreadLocal(() => new ModResidueTracker(ResidueModel.Identity, exponent, true), trackAllValues: true)); + forceCpuProp!.SetValue(null, true); + + try + { + Program.IsEvenPerfectCandidate(exponent, 0UL, out bool searched, out bool detailedCheck).Should().BeTrue(); + searched.Should().BeTrue(); + } + finally + { + useDivisorField.SetValue(null, false); + mersenneField.SetValue(null, null); + primeField.SetValue(null, null); + residueField.SetValue(null, null); + gcdFilterField.SetValue(null, false); + forceCpuProp!.SetValue(null, false); + } + } + + [Theory] + [MemberData(nameof(KnownMersennePrimeGpuConfigurations))] + public void IsEvenPerfectCandidate_accepts_known_small_mersenne_primes_on_gpu( + ulong exponent, + MersenneGpuCandidateConfig configuration) + { + var useDivisorField = typeof(Program).GetField("_useDivisor", BindingFlags.NonPublic | BindingFlags.Static)!; + var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!; + var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!; + var residueField = typeof(Program).GetField("PResidue", BindingFlags.NonPublic | BindingFlags.Static)!; + var gcdFilterField = typeof(Program).GetField("_useGcdFilter", BindingFlags.NonPublic | BindingFlags.Static)!; + var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu"); + + useDivisorField.SetValue(null, false); + gcdFilterField.SetValue(null, false); + mersenneField.SetValue(null, new ThreadLocal(() => new MersenneNumberTester( + useIncremental: false, + kernelType: configuration.KernelType, + useGpuScan: true, + useGpuOrder: configuration.UseGpuOrder, + useResidue: true, + maxK: (UInt128)configuration.ResidueMaxK, + residueDivisorSets: (UInt128)configuration.ResidueSetCount), trackAllValues: true)); + primeField.SetValue(null, new ThreadLocal(() => new PrimeTester(), trackAllValues: true)); + residueField.SetValue(null, new ThreadLocal(() => new ModResidueTracker(ResidueModel.Identity, exponent, true), trackAllValues: true)); + forceCpuProp!.SetValue(null, false); + + try + { + Program.IsEvenPerfectCandidate(exponent, 0UL, out bool searched, out bool detailedCheck).Should().BeTrue(); + searched.Should().BeTrue(); + } + finally + { + useDivisorField.SetValue(null, false); + mersenneField.SetValue(null, null); + primeField.SetValue(null, null); + residueField.SetValue(null, null); + gcdFilterField.SetValue(null, false); + forceCpuProp!.SetValue(null, false); + GpuContextPool.DisposeAll(); + } + } + + public static IEnumerable KnownMersennePrimeExponents() + { + foreach (ulong exponent in KnownMersennePrimeExponentValues) + { + yield return new object[] { exponent }; + } + } + + public static IEnumerable KnownMersennePrimeGpuConfigurations() + { + MersenneGpuCandidateConfig[] configs = + { + new(GpuKernelType.Incremental, true, 1_024UL, 1UL), + new(GpuKernelType.Incremental, false, 2_048UL, 1UL), + new(GpuKernelType.Pow2Mod, true, 4_096UL, 2UL), + new(GpuKernelType.Pow2Mod, false, 512UL, 2UL), + }; + + int index = 0; + foreach (ulong exponent in KnownMersennePrimeExponentValues) + { + var config = configs[index % configs.Length]; + yield return new object[] { exponent, config }; + index++; + } + } + + private static readonly ulong[] KnownMersennePrimeExponentValues = + [ + 31UL, + 61UL, + 89UL, + 107UL, + 127UL, + 521UL, + 607UL, + 1279UL, + 2203UL, + 2281UL, + 3217UL, + 4253UL, + 4423UL, + 9689UL, + 9941UL, + 11213UL, + ]; + + public readonly record struct MersenneGpuCandidateConfig( + GpuKernelType KernelType, + bool UseGpuOrder, + ulong ResidueMaxK, + ulong ResidueSetCount); + [Fact] public void Composite_candidates_flagged_for_residue_output_skip() { diff --git a/PerfectNumbers.Core.Tests/MersenneNumberResidueCpuTesterTests.cs b/PerfectNumbers.Core.Tests/MersenneNumberResidueCpuTesterTests.cs index a5e960a..8270f37 100644 --- a/PerfectNumbers.Core.Tests/MersenneNumberResidueCpuTesterTests.cs +++ b/PerfectNumbers.Core.Tests/MersenneNumberResidueCpuTesterTests.cs @@ -21,6 +21,18 @@ public class MersenneNumberResidueCpuTesterTests RunCase(tester, 127UL, 1_000UL, expectedPrime: true); } + [Fact] + [Trait("Category", "Fast")] + public void Scan_recognizes_all_known_small_mersenne_primes() + { + var tester = new MersenneNumberResidueCpuTester(); + + foreach (ulong exponent in MersennePrimeTestData.Exponents) + { + RunCase(tester, exponent, 1UL, expectedPrime: true); + } + } + private static void RunCase(MersenneNumberResidueCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime) { bool isPrime = true; diff --git a/PerfectNumbers.Core.Tests/MersenneNumberResidueGpuTesterTests.cs b/PerfectNumbers.Core.Tests/MersenneNumberResidueGpuTesterTests.cs index 06289b9..e3e1fed 100644 --- a/PerfectNumbers.Core.Tests/MersenneNumberResidueGpuTesterTests.cs +++ b/PerfectNumbers.Core.Tests/MersenneNumberResidueGpuTesterTests.cs @@ -25,6 +25,20 @@ public class MersenneNumberResidueGpuTesterTests RunCase(tester, 127UL, 1_001UL, expectedPrime: true); } + [Theory] + [InlineData(false)] + [InlineData(true)] + [Trait("Category", "Fast")] + public void Scan_recognizes_all_known_small_mersenne_primes(bool useGpuOrder) + { + var tester = new MersenneNumberResidueGpuTester(useGpuOrder); + + foreach (ulong exponent in MersennePrimeTestData.Exponents) + { + RunCase(tester, exponent, 1UL, expectedPrime: true); + } + } + [Theory] [InlineData(false)] [InlineData(true)] diff --git a/PerfectNumbers.Core.Tests/MersennePrimeTestData.cs b/PerfectNumbers.Core.Tests/MersennePrimeTestData.cs new file mode 100644 index 0000000..d7811f4 --- /dev/null +++ b/PerfectNumbers.Core.Tests/MersennePrimeTestData.cs @@ -0,0 +1,25 @@ +namespace PerfectNumbers.Core.Tests; + +internal static class MersennePrimeTestData +{ + public static readonly ulong[] Exponents = + { + 31UL, + 61UL, + 89UL, + 107UL, + 127UL, + 521UL, + 607UL, + 1279UL, + 2203UL, + 2281UL, + 3217UL, + 4253UL, + 4423UL, + 9689UL, + 9941UL, + 11213UL + }; +} +