diff --git a/EvenPerfectBitScanner.Tests/ProgramTests.cs b/EvenPerfectBitScanner.Tests/ProgramTests.cs index d3c98c9..5cf366e 100644 --- a/EvenPerfectBitScanner.Tests/ProgramTests.cs +++ b/EvenPerfectBitScanner.Tests/ProgramTests.cs @@ -68,7 +68,7 @@ public class ProgramTests } [Fact] - public void Divisor_mode_checks_all_candidates_when_none_specified() + public void Divisor_mode_scans_dynamic_cycles_when_none_specified() { var useDivisorField = typeof(Program).GetField("_useDivisor", BindingFlags.NonPublic | BindingFlags.Static)!; var divisorField = typeof(Program).GetField("_divisor", BindingFlags.NonPublic | BindingFlags.Static)!; @@ -80,7 +80,7 @@ public class ProgramTests useDivisorField.SetValue(null, true); divisorField.SetValue(null, 0UL); - testerField.SetValue(null, new MersenneNumberDivisorGpuTester()); + testerField.SetValue(null, null); primeField.SetValue(null, new ThreadLocal(() => new PrimeTester(), trackAllValues: true)); residueField.SetValue(null, new ThreadLocal(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true)); candidatesField.SetValue(null, new (ulong, uint)[] { (7UL, 3U), (23UL, 11U) }); @@ -88,8 +88,8 @@ public class ProgramTests try { - Program.IsEvenPerfectCandidate(11UL).Should().BeTrue(); - Program.IsEvenPerfectCandidate(5UL).Should().BeFalse(); + Program.IsEvenPerfectCandidate(11UL).Should().BeFalse(); + Program.IsEvenPerfectCandidate(5UL).Should().BeTrue(); } finally { diff --git a/EvenPerfectBitScanner/Program.cs b/EvenPerfectBitScanner/Program.cs index 5585dc3..0650705 100644 --- a/EvenPerfectBitScanner/Program.cs +++ b/EvenPerfectBitScanner/Program.cs @@ -29,9 +29,10 @@ internal static class Program private static bool _useResidue; private static bool _useDivisor; private static ulong _divisor; - private static MersenneNumberDivisorGpuTester? _divisorTester; - private static (ulong divisor, uint cycle)[]? _divisorCandidates; - private static ulong? _orderWarmupLimitOverride; +private static MersenneNumberDivisorGpuTester? _divisorTester; +private static (ulong divisor, uint cycle)[]? _divisorCandidates; +private const int ExtraDivisorCycleSearchLimit = 32; +private static ulong? _orderWarmupLimitOverride; private static unsafe delegate* _transformP; private static long _state; private static bool _limitReached; @@ -921,6 +922,26 @@ internal static class Program return list.ToArray(); } + private static bool IsDivisible(ulong exponent, ulong divisor) + { + if (_divisorTester is not null) + { + return _divisorTester.IsDivisible(exponent, divisor); + } + + ulong remainder = 1UL; + for (ulong i = 0; i < exponent; i++) + { + remainder <<= 1; + if (remainder >= divisor) + { + remainder -= divisor; + } + } + + return remainder == 1UL; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool IsEvenPerfectCandidate(ulong p) { @@ -995,8 +1016,14 @@ internal static class Program { if (_divisor != 0UL) { - detailedCheck = _divisorTester!.IsDivisible(p, _divisor); - return detailedCheck; + if (IsDivisible(p, _divisor)) + { + detailedCheck = true; + return false; + } + + detailedCheck = false; + return true; } var candidates = _divisorCandidates!; @@ -1009,14 +1036,37 @@ internal static class Program continue; } - if (_divisorTester!.IsDivisible(p, d)) + if (IsDivisible(p, d)) { detailedCheck = true; - return true; + return false; } } - return false; + for (int k = 1; k <= ExtraDivisorCycleSearchLimit; k++) + { + ulong kMul2 = 2UL * (ulong)k; + if (p > ulong.MaxValue / kMul2) + { + break; + } + + ulong d = kMul2 * p + 1UL; + ulong cycle = MersenneDivisorCycles.Shared.GetCycle(d); + if (p % cycle != 0UL) + { + continue; + } + + if (IsDivisible(p, d)) + { + detailedCheck = true; + return false; + } + } + + detailedCheck = false; + return true; } detailedCheck = MersenneTesters.Value!.IsMersennePrime(p);