diff --git a/PerfectNumbers.Core.Tests/MersenneNumberDivisorGpuTesterTests.cs b/PerfectNumbers.Core.Tests/MersenneNumberDivisorGpuTesterTests.cs index a382431..e19dc8e 100644 --- a/PerfectNumbers.Core.Tests/MersenneNumberDivisorGpuTesterTests.cs +++ b/PerfectNumbers.Core.Tests/MersenneNumberDivisorGpuTesterTests.cs @@ -142,6 +142,22 @@ public class MersenneNumberDivisorGpuTesterTests divisorsExhausted.Should().BeTrue(); } + [Fact] + [Trait("Category", "Fast")] + public void ByDivisor_session_computes_cycle_when_zero_is_provided() + { + var tester = new MersenneNumberDivisorByDivisorGpuTester(); + tester.ConfigureFromMaxPrime(43UL); + + using var session = tester.CreateDivisorSession(); + ulong[] primes = { 31UL, 37UL, 41UL }; + byte[] hits = new byte[primes.Length]; + + session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), 0UL, primes, hits); + + hits.Should().ContainInOrder(new byte[] { 0, 1, 0 }); + } + [Fact] [Trait("Category", "Fast")] public void ByDivisor_session_checks_divisors_across_primes() diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs index a25cc51..367be54 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs @@ -471,15 +471,26 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv } ulong cycle = divisorCycle; - // EvenPerfectBitScanner resolves divisor cycles before scheduling GPU work, so the zero-cycle fallback stays disabled. - // if (cycle == 0UL) - // { - // throw new InvalidOperationException("GPU divisor cycle length must be non-zero."); - // } - ulong firstPrime = primes[0]; + + if (cycle == 0UL) + { + if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponent(divisor, firstPrime, cachedData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL) + { + cycle = MersenneDivisorCycles.CalculateCycleLength(divisor, cachedData, skipPrimeOrderHeuristic: primeOrderFailed); + } + else + { + cycle = computedCycle; + } + + if (cycle == 0UL) + { + throw new InvalidOperationException("GPU divisor cycle length must be non-zero."); + } + } + // Precompute the initial cycle remainder on the host to keep the GPU kernel branch-free. - // EvenPerfectBitScanner resolves divisor cycles before scheduling GPU work, so cycle never equals zero here. ulong firstRemainder = firstPrime % cycle; Monitor.Enter(_lease.ExecutionLock);