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
+95 -16

No files matched your search

@@ -1,6 +1,7 @@
using FluentAssertions;
using Xunit;
using PerfectNumbers.Core;
using PerfectNumbers.Core.Gpu;
using System.Reflection;
namespace EvenPerfectBitScanner.Tests;
@@ -66,6 +67,41 @@ public class ProgramTests
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]
[Trait("Category", "Fast")]
public void Main_displays_help_when_requested()
+59 -16
View File
@@ -1,6 +1,7 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text;
using System.Collections.Generic;
using PerfectNumbers.Core;
using PerfectNumbers.Core.Gpu;
@@ -29,6 +30,7 @@ internal static class Program
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 unsafe delegate*<ulong, ref ulong, ulong> _transformP;
private static long _state;
@@ -357,14 +359,8 @@ internal static class Program
return;
}
if (useDivisor && divisor == 0UL)
{
Console.WriteLine("Divisor mode requires --divisor=<value>.");
return;
}
// Apply GPU prime sieve runtime configuration
GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads);
// Apply GPU prime sieve runtime configuration
GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads);
PerfectNumbers.Core.PrimeTester.GpuBatchSize = Math.Max(1, gpuPrimeBatch);
MersenneDivisorCycles mersenneDivisorCycles = new();
@@ -444,6 +440,10 @@ internal static class Program
else
{
_divisorTester = new MersenneNumberDivisorGpuTester();
if (divisor == 0UL)
{
_divisorCandidates = BuildDivisorCandidates();
}
}
// Load RLE blacklist (optional)
@@ -690,7 +690,7 @@ internal static class Program
Console.WriteLine(" --threads=<value> number of worker threads");
Console.WriteLine(" --block-size=<value> values processed per thread batch");
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(" --mersenne-device=cpu|gpu Device for Mersenne method (default gpu)");
Console.WriteLine(" --primes-device=cpu|gpu Device for prime-scan kernels (default gpu)");
@@ -875,9 +875,9 @@ internal static class Program
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static ulong TransformPAdd(ulong value, ref ulong remainder)
{
ulong next = value;
internal static ulong TransformPAdd(ulong value, ref ulong remainder)
{
ulong next = value;
value = remainder switch
{
0UL => 1UL,
@@ -897,8 +897,29 @@ internal static class Program
remainder += value;
remainder -= (remainder >= 6UL) ? 6UL : 0UL;
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)]
internal static bool IsEvenPerfectCandidate(ulong p)
@@ -972,8 +993,30 @@ internal static class Program
searchedMersenne = true;
if (_useDivisor)
{
detailedCheck = _divisorTester!.IsDivisible(p, _divisor);
return detailedCheck;
if (_divisor != 0UL)
{
detailedCheck = _divisorTester!.IsDivisible(p, _divisor);
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);