Merge pull request #10 from mlemanczyk/codex/modify-evenperfectbitscanner-for-mersenne-mode

Enable automatic divisor scanning in Mersenne mode
This commit is contained in:
Marek Lemańczyk authored and GitHub committed 2025-09-10 22:23:06 +02:00
commit 873328b944
2 files changed
+86 -7

No files matched your search

@@ -1,6 +1,7 @@
using FluentAssertions; using FluentAssertions;
using Xunit; using Xunit;
using PerfectNumbers.Core; using PerfectNumbers.Core;
using PerfectNumbers.Core.Gpu;
using System.Reflection; using System.Reflection;
namespace EvenPerfectBitScanner.Tests; namespace EvenPerfectBitScanner.Tests;
@@ -66,6 +67,41 @@ public class ProgramTests
Program.IsCompositeByGcd(5UL).Should().BeFalse(); Program.IsCompositeByGcd(5UL).Should().BeFalse();
} }
[Fact]
public void Divisor_mode_checks_all_candidates_when_none_specified()
{
var useDivisorField = typeof(Program).GetField("_useDivisor", BindingFlags.NonPublic | BindingFlags.Static)!;
var divisorField = typeof(Program).GetField("_divisor", BindingFlags.NonPublic | BindingFlags.Static)!;
var testerField = typeof(Program).GetField("_divisorTester", BindingFlags.NonPublic | BindingFlags.Static)!;
var candidatesField = typeof(Program).GetField("_divisorCandidates", 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 forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
useDivisorField.SetValue(null, true);
divisorField.SetValue(null, 0UL);
testerField.SetValue(null, new MersenneNumberDivisorGpuTester());
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
residueField.SetValue(null, new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
candidatesField.SetValue(null, new (ulong, uint)[] { (7UL, 3U), (23UL, 11U) });
forceCpuProp!.SetValue(null, true);
try
{
Program.IsEvenPerfectCandidate(11UL).Should().BeTrue();
Program.IsEvenPerfectCandidate(5UL).Should().BeFalse();
}
finally
{
useDivisorField.SetValue(null, false);
testerField.SetValue(null, null);
candidatesField.SetValue(null, null);
primeField.SetValue(null, null);
residueField.SetValue(null, null);
forceCpuProp!.SetValue(null, false);
}
}
[Fact] [Fact]
[Trait("Category", "Fast")] [Trait("Category", "Fast")]
public void Main_displays_help_when_requested() public void Main_displays_help_when_requested()
+50 -7
View File
@@ -1,6 +1,7 @@
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Collections.Generic;
using PerfectNumbers.Core; using PerfectNumbers.Core;
using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu;
@@ -29,6 +30,7 @@ internal static class Program
private static bool _useDivisor; private static bool _useDivisor;
private static ulong _divisor; private static ulong _divisor;
private static MersenneNumberDivisorGpuTester? _divisorTester; private static MersenneNumberDivisorGpuTester? _divisorTester;
private static (ulong divisor, uint cycle)[]? _divisorCandidates;
private static ulong? _orderWarmupLimitOverride; private static ulong? _orderWarmupLimitOverride;
private static unsafe delegate*<ulong, ref ulong, ulong> _transformP; private static unsafe delegate*<ulong, ref ulong, ulong> _transformP;
private static long _state; private static long _state;
@@ -357,12 +359,6 @@ internal static class Program
return; return;
} }
if (useDivisor && divisor == 0UL)
{
Console.WriteLine("Divisor mode requires --divisor=<value>.");
return;
}
// Apply GPU prime sieve runtime configuration // Apply GPU prime sieve runtime configuration
GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads); GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads);
PerfectNumbers.Core.PrimeTester.GpuBatchSize = Math.Max(1, gpuPrimeBatch); PerfectNumbers.Core.PrimeTester.GpuBatchSize = Math.Max(1, gpuPrimeBatch);
@@ -444,6 +440,10 @@ internal static class Program
else else
{ {
_divisorTester = new MersenneNumberDivisorGpuTester(); _divisorTester = new MersenneNumberDivisorGpuTester();
if (divisor == 0UL)
{
_divisorCandidates = BuildDivisorCandidates();
}
} }
// Load RLE blacklist (optional) // Load RLE blacklist (optional)
@@ -690,7 +690,7 @@ internal static class Program
Console.WriteLine(" --threads=<value> number of worker threads"); Console.WriteLine(" --threads=<value> number of worker threads");
Console.WriteLine(" --block-size=<value> values processed per thread batch"); Console.WriteLine(" --block-size=<value> values processed per thread batch");
Console.WriteLine(" --mersenne=pow2mod|incremental|lucas|residue|divisor Mersenne test method"); Console.WriteLine(" --mersenne=pow2mod|incremental|lucas|residue|divisor Mersenne test method");
Console.WriteLine(" --divisor=<value> divisor for --mersenne=divisor mode"); Console.WriteLine(" --divisor=<value> optional divisor for --mersenne=divisor mode");
Console.WriteLine(" --residue-max-k=<value> max k for residue Mersenne test (q = 2*p*k + 1)"); Console.WriteLine(" --residue-max-k=<value> max k for residue Mersenne test (q = 2*p*k + 1)");
Console.WriteLine(" --mersenne-device=cpu|gpu Device for Mersenne method (default gpu)"); Console.WriteLine(" --mersenne-device=cpu|gpu Device for Mersenne method (default gpu)");
Console.WriteLine(" --primes-device=cpu|gpu Device for prime-scan kernels (default gpu)"); Console.WriteLine(" --primes-device=cpu|gpu Device for prime-scan kernels (default gpu)");
@@ -900,6 +900,27 @@ internal static class Program
return next + value; return next + value;
} }
private static (ulong divisor, uint cycle)[] BuildDivisorCandidates()
{
uint[] snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
List<(ulong divisor, uint cycle)> list = new(snapshot.Length / 2);
ulong d;
uint cycle;
for (int i = 3; i < snapshot.Length; i += 2)
{
cycle = snapshot[i];
if (cycle == 0U)
{
continue;
}
d = (ulong)i;
list.Add((d, cycle));
}
return list.ToArray();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool IsEvenPerfectCandidate(ulong p) internal static bool IsEvenPerfectCandidate(ulong p)
{ {
@@ -971,11 +992,33 @@ internal static class Program
searchedMersenne = true; searchedMersenne = true;
if (_useDivisor) if (_useDivisor)
{
if (_divisor != 0UL)
{ {
detailedCheck = _divisorTester!.IsDivisible(p, _divisor); detailedCheck = _divisorTester!.IsDivisible(p, _divisor);
return detailedCheck; return detailedCheck;
} }
var candidates = _divisorCandidates!;
int len = candidates.Length;
for (int i = 0; i < len; i++)
{
var (d, cycle) = candidates[i];
if (p % cycle != 0UL)
{
continue;
}
if (_divisorTester!.IsDivisible(p, d))
{
detailedCheck = true;
return true;
}
}
return false;
}
detailedCheck = MersenneTesters.Value!.IsMersennePrime(p); detailedCheck = MersenneTesters.Value!.IsMersennePrime(p);
return detailedCheck; return detailedCheck;
} }