Allow divisor mode to scan all valid divisors
This commit is contained in:
1 parent
4acc546d0a
commit
e6be18ad39
2 files changed
+95
-16
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()
|
||||||
|
|||||||
@@ -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,14 +359,8 @@ internal static class Program
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useDivisor && divisor == 0UL)
|
// Apply GPU prime sieve runtime configuration
|
||||||
{
|
GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads);
|
||||||
Console.WriteLine("Divisor mode requires --divisor=<value>.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply GPU prime sieve runtime configuration
|
|
||||||
GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads);
|
|
||||||
PerfectNumbers.Core.PrimeTester.GpuBatchSize = Math.Max(1, gpuPrimeBatch);
|
PerfectNumbers.Core.PrimeTester.GpuBatchSize = Math.Max(1, gpuPrimeBatch);
|
||||||
|
|
||||||
MersenneDivisorCycles mersenneDivisorCycles = new();
|
MersenneDivisorCycles mersenneDivisorCycles = new();
|
||||||
@@ -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)");
|
||||||
@@ -875,9 +875,9 @@ internal static class Program
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal static ulong TransformPAdd(ulong value, ref ulong remainder)
|
internal static ulong TransformPAdd(ulong value, ref ulong remainder)
|
||||||
{
|
{
|
||||||
ulong next = value;
|
ulong next = value;
|
||||||
value = remainder switch
|
value = remainder switch
|
||||||
{
|
{
|
||||||
0UL => 1UL,
|
0UL => 1UL,
|
||||||
@@ -897,8 +897,29 @@ internal static class Program
|
|||||||
remainder += value;
|
remainder += value;
|
||||||
remainder -= (remainder >= 6UL) ? 6UL : 0UL;
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal static bool IsEvenPerfectCandidate(ulong p)
|
internal static bool IsEvenPerfectCandidate(ulong p)
|
||||||
@@ -972,8 +993,30 @@ internal static class Program
|
|||||||
searchedMersenne = true;
|
searchedMersenne = true;
|
||||||
if (_useDivisor)
|
if (_useDivisor)
|
||||||
{
|
{
|
||||||
detailedCheck = _divisorTester!.IsDivisible(p, _divisor);
|
if (_divisor != 0UL)
|
||||||
return detailedCheck;
|
{
|
||||||
|
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);
|
detailedCheck = MersenneTesters.Value!.IsMersennePrime(p);
|
||||||
|
|||||||
Reference in new issue
Block a user