Merge pull request #204 from mlemanczyk/codex/execute-todos-from-latest-commit
Add tests for CalculationResultsFile helpers
This commit is contained in:
5 files changed
+448
-328
No files matched your search
@@ -4,19 +4,47 @@ using System.Reflection;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
|
||||
using EvenPerfectBitScanner;
|
||||
using EvenPerfectBitScanner.Candidates;
|
||||
using EvenPerfectBitScanner.Candidates.Transforms;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace EvenPerfectBitScanner.Tests;
|
||||
|
||||
[Trait("Category", "Fast")]
|
||||
public class ProgramTests
|
||||
{
|
||||
private static readonly FieldInfo CliArgumentsField = typeof(Program).GetField("_cliArguments", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
private static readonly FieldInfo DivisorField = typeof(Program).GetField("_divisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
private static readonly CliArguments DefaultCliArguments = CliArguments.Parse(Array.Empty<string>());
|
||||
|
||||
public ProgramTests()
|
||||
{
|
||||
ResetCliArguments();
|
||||
}
|
||||
|
||||
private static void ConfigureCliArguments(params string[] args)
|
||||
{
|
||||
SetCliArguments(CliArguments.Parse(args));
|
||||
}
|
||||
|
||||
private static void SetCliArguments(CliArguments arguments)
|
||||
{
|
||||
CliArgumentsField.SetValue(null, arguments);
|
||||
}
|
||||
|
||||
private static void ResetCliArguments()
|
||||
{
|
||||
SetCliArguments(DefaultCliArguments);
|
||||
DivisorField.SetValue(null, UInt128.Zero);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CountOnes_returns_correct_count()
|
||||
{
|
||||
(ulong)BitOperations.PopCount(0b101010UL).Should().Be(3UL);
|
||||
BitOperations.PopCount(0b101010).Should().Be(3);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -70,19 +98,15 @@ public class ProgramTests
|
||||
[Fact]
|
||||
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)!;
|
||||
ConfigureCliArguments("--mersenne=divisor");
|
||||
var testerField = typeof(Program).GetField("_divisorTester", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var candidatesField = typeof(MersenneNumberDivisorGpuTester).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, UInt128.Zero);
|
||||
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));
|
||||
CandidatesCalculator.OverrideResidueTrackers(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);
|
||||
|
||||
@@ -94,31 +118,28 @@ public class ProgramTests
|
||||
}
|
||||
finally
|
||||
{
|
||||
useDivisorField.SetValue(null, false);
|
||||
testerField.SetValue(null, null);
|
||||
candidatesField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
residueField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Divisor_mode_sets_detailedCheck_based_on_exhaustion()
|
||||
{
|
||||
var useDivisorField = typeof(Program).GetField("_useDivisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var divisorField = typeof(Program).GetField("_divisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
ConfigureCliArguments("--mersenne=divisor");
|
||||
|
||||
var testerField = typeof(Program).GetField("_divisorTester", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var candidatesField = typeof(MersenneNumberDivisorGpuTester).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, UInt128.Zero);
|
||||
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));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
candidatesField.SetValue(null, Array.Empty<(ulong, uint)>());
|
||||
forceCpuProp!.SetValue(null, true);
|
||||
|
||||
@@ -130,30 +151,28 @@ public class ProgramTests
|
||||
}
|
||||
finally
|
||||
{
|
||||
useDivisorField.SetValue(null, false);
|
||||
testerField.SetValue(null, null);
|
||||
candidatesField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
residueField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Divisor_mode_accepts_large_search_limit()
|
||||
{
|
||||
var useDivisorField = typeof(Program).GetField("_useDivisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var divisorField = typeof(Program).GetField("_divisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
ConfigureCliArguments("--mersenne=divisor");
|
||||
DivisorField.SetValue(null, (UInt128)7);
|
||||
|
||||
var testerField = typeof(Program).GetField("_divisorTester", 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, (UInt128)7);
|
||||
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));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
forceCpuProp!.SetValue(null, true);
|
||||
|
||||
try
|
||||
@@ -164,29 +183,27 @@ public class ProgramTests
|
||||
}
|
||||
finally
|
||||
{
|
||||
useDivisorField.SetValue(null, false);
|
||||
testerField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
residueField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Divisor_mode_checks_divisibility_on_gpu()
|
||||
{
|
||||
var useDivisorField = typeof(Program).GetField("_useDivisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var divisorField = typeof(Program).GetField("_divisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
ConfigureCliArguments("--mersenne=divisor");
|
||||
DivisorField.SetValue(null, (UInt128)23);
|
||||
|
||||
var testerField = typeof(Program).GetField("_divisorTester", 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, (UInt128)23);
|
||||
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));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
|
||||
try
|
||||
@@ -201,28 +218,26 @@ public class ProgramTests
|
||||
}
|
||||
finally
|
||||
{
|
||||
useDivisorField.SetValue(null, false);
|
||||
divisorField.SetValue(null, UInt128.Zero);
|
||||
testerField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
residueField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Residue_mode_checks_mersenne_primes_on_gpu()
|
||||
{
|
||||
var useDivisorField = typeof(Program).GetField("_useDivisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
ConfigureCliArguments("--mersenne=residue");
|
||||
|
||||
var mersenneField = typeof(Program).GetField("MersenneTesters", 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, false);
|
||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(useIncremental: true, useResidue: true, maxK: 1_000UL), trackAllValues: true));
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
residueField.SetValue(null, new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
|
||||
try
|
||||
@@ -237,30 +252,29 @@ public class ProgramTests
|
||||
}
|
||||
finally
|
||||
{
|
||||
useDivisorField.SetValue(null, false);
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
residueField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Residue_mode_with_large_divisor_cycle_limits_accepts_known_primes()
|
||||
{
|
||||
var useDivisorField = typeof(Program).GetField("_useDivisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
ConfigureCliArguments("--mersenne=residue");
|
||||
|
||||
var mersenneField = typeof(Program).GetField("MersenneTesters", 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, false);
|
||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
||||
useIncremental: true,
|
||||
useResidue: true,
|
||||
maxK: 1_024UL), trackAllValues: true));
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
residueField.SetValue(null, new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
|
||||
try
|
||||
@@ -275,11 +289,11 @@ public class ProgramTests
|
||||
}
|
||||
finally
|
||||
{
|
||||
useDivisorField.SetValue(null, false);
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
residueField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,21 +301,18 @@ public class ProgramTests
|
||||
[Trait("Category", "Fast")]
|
||||
public void Residue_mode_accepts_known_primes_in_parallel()
|
||||
{
|
||||
var useDivisorField = typeof(Program).GetField("_useDivisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
ConfigureCliArguments("--mersenne=residue");
|
||||
|
||||
var mersenneField = typeof(Program).GetField("MersenneTesters", 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 gcdFilterField = typeof(Program).GetField("_useGcdFilter", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
||||
|
||||
useDivisorField.SetValue(null, false);
|
||||
gcdFilterField.SetValue(null, false);
|
||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
||||
useIncremental: true,
|
||||
useResidue: true,
|
||||
maxK: 5_000_000UL), trackAllValues: true));
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
residueField.SetValue(null, new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
|
||||
try
|
||||
@@ -316,30 +327,29 @@ public class ProgramTests
|
||||
}
|
||||
finally
|
||||
{
|
||||
useDivisorField.SetValue(null, false);
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
residueField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Residue_mode_with_default_limits_accepts_large_known_primes()
|
||||
{
|
||||
var useDivisorField = typeof(Program).GetField("_useDivisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
ConfigureCliArguments("--mersenne=residue");
|
||||
|
||||
var mersenneField = typeof(Program).GetField("MersenneTesters", 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, false);
|
||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
||||
useIncremental: true,
|
||||
useResidue: true,
|
||||
maxK: 5_000_000UL), trackAllValues: true));
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
residueField.SetValue(null, new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
|
||||
try
|
||||
@@ -354,11 +364,11 @@ public class ProgramTests
|
||||
}
|
||||
finally
|
||||
{
|
||||
useDivisorField.SetValue(null, false);
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
residueField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,15 +376,12 @@ public class ProgramTests
|
||||
[MemberData(nameof(KnownMersennePrimeExponents))]
|
||||
public void IsEvenPerfectCandidate_accepts_known_small_mersenne_primes(ulong exponent)
|
||||
{
|
||||
var useDivisorField = typeof(Program).GetField("_useDivisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
ConfigureCliArguments("--mersenne=residue");
|
||||
|
||||
var mersenneField = typeof(Program).GetField("MersenneTesters", 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 gcdFilterField = typeof(Program).GetField("_useGcdFilter", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
||||
|
||||
useDivisorField.SetValue(null, false);
|
||||
gcdFilterField.SetValue(null, false);
|
||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
||||
useIncremental: true,
|
||||
useGpuScan: false,
|
||||
@@ -382,7 +389,7 @@ public class ProgramTests
|
||||
useResidue: true,
|
||||
maxK: 1_024UL), trackAllValues: true));
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
residueField.SetValue(null, new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, exponent, true), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, exponent, true), trackAllValues: true));
|
||||
forceCpuProp!.SetValue(null, true);
|
||||
|
||||
try
|
||||
@@ -392,12 +399,11 @@ public class ProgramTests
|
||||
}
|
||||
finally
|
||||
{
|
||||
useDivisorField.SetValue(null, false);
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
residueField.SetValue(null, null);
|
||||
gcdFilterField.SetValue(null, false);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,15 +413,12 @@ public class ProgramTests
|
||||
ulong exponent,
|
||||
MersenneGpuCandidateConfig configuration)
|
||||
{
|
||||
var useDivisorField = typeof(Program).GetField("_useDivisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
ConfigureCliArguments("--mersenne=residue");
|
||||
|
||||
var mersenneField = typeof(Program).GetField("MersenneTesters", 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 gcdFilterField = typeof(Program).GetField("_useGcdFilter", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
||||
|
||||
useDivisorField.SetValue(null, false);
|
||||
gcdFilterField.SetValue(null, false);
|
||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
||||
useIncremental: false,
|
||||
kernelType: configuration.KernelType,
|
||||
@@ -424,7 +427,7 @@ public class ProgramTests
|
||||
useResidue: true,
|
||||
maxK: configuration.ResidueMaxK), trackAllValues: true));
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
residueField.SetValue(null, new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, exponent, true), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, exponent, true), trackAllValues: true));
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
|
||||
try
|
||||
@@ -434,12 +437,11 @@ public class ProgramTests
|
||||
}
|
||||
finally
|
||||
{
|
||||
useDivisorField.SetValue(null, false);
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
residueField.SetValue(null, null);
|
||||
gcdFilterField.SetValue(null, false);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
GpuContextPool.DisposeAll();
|
||||
}
|
||||
}
|
||||
@@ -499,17 +501,16 @@ public class ProgramTests
|
||||
[Fact]
|
||||
public void Composite_candidates_flagged_for_residue_output_skip()
|
||||
{
|
||||
var useDivisorField = typeof(Program).GetField("_useDivisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
ConfigureCliArguments("--mersenne=residue");
|
||||
|
||||
var mersenneField = typeof(Program).GetField("MersenneTesters", 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 compositeField = typeof(Program).GetField("_lastCompositeP", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
||||
|
||||
useDivisorField.SetValue(null, false);
|
||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(useIncremental: true, useResidue: false, maxK: 1_000UL), trackAllValues: true));
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
residueField.SetValue(null, new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
forceCpuProp!.SetValue(null, true);
|
||||
|
||||
try
|
||||
@@ -529,9 +530,10 @@ public class ProgramTests
|
||||
{
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
residueField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
compositeField.SetValue(null, false);
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using Xunit;
|
||||
|
||||
using EvenPerfectBitScanner.IO;
|
||||
|
||||
namespace EvenPerfectBitScanner.Tests;
|
||||
|
||||
[Trait("Category", "Fast")]
|
||||
@@ -11,41 +16,26 @@ public class ResultsFileNameTests
|
||||
[Fact]
|
||||
public void BuildResultsFileName_contains_reduction_and_devices()
|
||||
{
|
||||
var programType = typeof(global::EvenPerfectBitScanner.Program);
|
||||
var method = programType.GetMethod(
|
||||
"BuildResultsFileName",
|
||||
BindingFlags.NonPublic | BindingFlags.Static,
|
||||
binder: null,
|
||||
types: new[]
|
||||
{
|
||||
typeof(bool), typeof(int), typeof(int), typeof(global::PerfectNumbers.Core.GpuKernelType),
|
||||
typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(NttBackend), typeof(int), typeof(int), typeof(int), typeof(ulong), typeof(ModReductionMode), typeof(string), typeof(string), typeof(string)
|
||||
},
|
||||
modifiers: null)!;
|
||||
|
||||
string name = (string)method.Invoke(null, new object[]
|
||||
{
|
||||
true, // bitInc
|
||||
8, // threads
|
||||
1, // block
|
||||
global::PerfectNumbers.Core.GpuKernelType.Incremental,
|
||||
false, // useLucasFlag
|
||||
false, // useDivisorFlag
|
||||
false, // useByDivisorFlag
|
||||
true, // mersenneOnGpu (unused inside name content, devices passed explicitly below)
|
||||
false, // useOrder
|
||||
false, // useModWorkaround
|
||||
false, // useGcd
|
||||
NttBackend.Staged,
|
||||
1, // gpuPrimeThreads
|
||||
32, // llSlice
|
||||
1024, // gpuScanBatch
|
||||
1000UL, // warmupLimit
|
||||
ModReductionMode.Barrett128,
|
||||
"gpu", // mersenneDevice
|
||||
"gpu", // primesDevice
|
||||
"gpu" // orderDevice
|
||||
})!;
|
||||
string name = CalculationResultsFile.BuildFileName(
|
||||
bitInc: true,
|
||||
threads: 8,
|
||||
block: 1,
|
||||
kernelType: GpuKernelType.Incremental,
|
||||
useLucasFlag: false,
|
||||
useDivisorFlag: false,
|
||||
useByDivisorFlag: false,
|
||||
mersenneOnGpu: true,
|
||||
useOrder: false,
|
||||
useGcd: false,
|
||||
nttBackend: NttBackend.Staged,
|
||||
gpuPrimeThreads: 1,
|
||||
llSlice: 32,
|
||||
gpuScanBatch: 1024,
|
||||
warmupLimit: 1000UL,
|
||||
reduction: ModReductionMode.Barrett128,
|
||||
mersenneDevice: "gpu",
|
||||
primesDevice: "gpu",
|
||||
orderDevice: "gpu");
|
||||
|
||||
name.Should().Contain("ntt-staged");
|
||||
name.Should().Contain("red-barrett128");
|
||||
@@ -53,4 +43,63 @@ public class ResultsFileNameTests
|
||||
name.Should().Contain("primesdev-gpu");
|
||||
name.Should().Contain("orderdev-gpu");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LoadCandidatesWithinRange_skips_comments_and_applies_limit()
|
||||
{
|
||||
string path = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
File.WriteAllLines(path, new[]
|
||||
{
|
||||
"# header",
|
||||
"5 7",
|
||||
string.Empty,
|
||||
"11",
|
||||
"17",
|
||||
"19"
|
||||
});
|
||||
|
||||
List<ulong> candidates = CalculationResultsFile.LoadCandidatesWithinRange(path, (UInt128)13, maxPrimeConfigured: true, out int skipped);
|
||||
|
||||
candidates.Should().Equal(5UL, 7UL, 11UL);
|
||||
skipped.Should().Be(2);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnumerateCandidates_reads_valid_rows_and_skips_invalid_entries()
|
||||
{
|
||||
string path = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
File.WriteAllLines(path, new[]
|
||||
{
|
||||
"p,searched,detailed,passed",
|
||||
"5,true,true,true",
|
||||
string.Empty,
|
||||
"11,false,false,true",
|
||||
"invalid",
|
||||
"13,true,true,false"
|
||||
});
|
||||
|
||||
List<(ulong P, bool Detailed, bool Passed)> rows = new();
|
||||
CalculationResultsFile.EnumerateCandidates(path, (p, detailed, passed) => rows.Add((p, detailed, passed)));
|
||||
|
||||
rows.Should().Equal(new (ulong, bool, bool)[]
|
||||
{
|
||||
(5UL, true, true),
|
||||
(11UL, false, true),
|
||||
(13UL, true, false)
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using Open.Numeric.Primes;
|
||||
using PerfectNumbers.Core;
|
||||
using EvenPerfectBitScanner.Candidates.Transforms;
|
||||
|
||||
namespace EvenPerfectBitScanner.Candidates;
|
||||
@@ -10,6 +11,7 @@ internal static class CandidatesCalculator
|
||||
private static readonly Optimized PrimeIterator = new();
|
||||
private static long _state;
|
||||
private static PrimeTransformMode _transformMode;
|
||||
private static ThreadLocal<ModResidueTracker>? _residueTrackers;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void Configure(PrimeTransformMode transformMode)
|
||||
@@ -17,6 +19,68 @@ internal static class CandidatesCalculator
|
||||
_transformMode = transformMode;
|
||||
}
|
||||
|
||||
internal static void Initialize(ulong initialNumber)
|
||||
{
|
||||
SetResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, initialNumber: initialNumber, initialized: true), trackAllValues: true));
|
||||
}
|
||||
|
||||
internal static void OverrideResidueTrackers(ThreadLocal<ModResidueTracker> trackers)
|
||||
{
|
||||
if (trackers is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(trackers));
|
||||
}
|
||||
|
||||
SetResidueTrackers(trackers);
|
||||
}
|
||||
|
||||
internal static void ResetResidueTrackers()
|
||||
{
|
||||
ThreadLocal<ModResidueTracker>? previous = _residueTrackers;
|
||||
_residueTrackers = null;
|
||||
previous?.Dispose();
|
||||
}
|
||||
|
||||
private static void SetResidueTrackers(ThreadLocal<ModResidueTracker> trackers)
|
||||
{
|
||||
ThreadLocal<ModResidueTracker>? previous = _residueTrackers;
|
||||
_residueTrackers = trackers;
|
||||
previous?.Dispose();
|
||||
}
|
||||
|
||||
private static ModResidueTracker AcquireResidueTracker()
|
||||
{
|
||||
var trackers = _residueTrackers ?? throw new InvalidOperationException("CandidatesCalculator.Initialize must be called before residue checks.");
|
||||
return trackers.Value!;
|
||||
}
|
||||
|
||||
internal static bool IsCompositeByResidues(ulong p)
|
||||
{
|
||||
ModResidueTracker tracker = AcquireResidueTracker();
|
||||
// Use ModResidueTracker with a small set of primes to pre-filter composite p.
|
||||
tracker.BeginMerge(p);
|
||||
// TODO: Integrate the divisor-cycle cache here so the small-prime sweep reuses precomputed remainders instead of
|
||||
// running MergeOrAppend for every candidate and missing the cycle-accelerated early exits.
|
||||
// Use the small prime list from PerfectNumbers.Core to the extent of sqrt(p)
|
||||
var primes = PrimesGenerator.SmallPrimes;
|
||||
var primesPow2 = PrimesGenerator.SmallPrimesPow2;
|
||||
int len = primes.Length;
|
||||
for (int primeIndex = 0; primeIndex < len; primeIndex++)
|
||||
{
|
||||
if (primesPow2[primeIndex] > p)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (tracker.MergeOrAppend(p, primes[primeIndex], out bool divisible) && divisible)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void InitializeState(ulong startPrime, ulong remainder)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
using Open.Collections;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using EvenPerfectBitScanner;
|
||||
|
||||
namespace EvenPerfectBitScanner.IO;
|
||||
|
||||
internal static class CalculationResultsFile
|
||||
{
|
||||
internal static List<ulong> LoadCandidatesWithinRange(string candidateFile, UInt128 maxPrimeLimit, bool maxPrimeConfigured, out int skippedByLimit)
|
||||
{
|
||||
List<ulong> candidates = [];
|
||||
skippedByLimit = 0;
|
||||
using FileStream readStream = new(candidateFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
using StreamReader reader = new(readStream);
|
||||
string? line;
|
||||
ReadOnlySpan<char> span;
|
||||
while ((line = reader.ReadLine()) is not null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
span = line.AsSpan().Trim();
|
||||
if (span.IsEmpty || span[0] == '#')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int index = 0;
|
||||
while (index < span.Length)
|
||||
{
|
||||
if (!char.IsDigit(span[index]))
|
||||
{
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
int start = index;
|
||||
index++;
|
||||
while (index < span.Length && char.IsDigit(span[index]))
|
||||
{
|
||||
index++;
|
||||
}
|
||||
|
||||
if (Utf8CliParser.TryParseUInt64(span[start..index], out ulong parsed))
|
||||
{
|
||||
if (!maxPrimeConfigured || (UInt128)parsed <= maxPrimeLimit)
|
||||
{
|
||||
candidates.Add(parsed);
|
||||
}
|
||||
else
|
||||
{
|
||||
skippedByLimit++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return candidates;
|
||||
}
|
||||
|
||||
internal static void EnumerateCandidates(string resultsFileName, Action<ulong, bool, bool> lineProcessorAction)
|
||||
{
|
||||
using FileStream readStream = new(resultsFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
using StreamReader reader = new(readStream);
|
||||
string? line;
|
||||
bool headerSkipped = false;
|
||||
ReadOnlySpan<char> span;
|
||||
int first = 0;
|
||||
int second = 0;
|
||||
int third = 0;
|
||||
ulong parsedP = 0UL;
|
||||
ReadOnlySpan<char> detailedSpan = default;
|
||||
ReadOnlySpan<char> passedAllTestsSpan = default;
|
||||
bool detailed = false;
|
||||
bool passedAllTests = false;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
if (!headerSkipped)
|
||||
{
|
||||
headerSkipped = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
span = line.AsSpan();
|
||||
first = span.IndexOf(',');
|
||||
if (first < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
parsedP = Utf8CliParser.ParseUInt64(span[..first]);
|
||||
span = span[(first + 1)..];
|
||||
second = span.IndexOf(',');
|
||||
if (second < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
span = span[(second + 1)..];
|
||||
third = span.IndexOf(',');
|
||||
if (third < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
detailedSpan = span[..third];
|
||||
passedAllTestsSpan = span[(third + 1)..];
|
||||
|
||||
if (Utf8CliParser.TryParseBoolean(detailedSpan, out detailed) && Utf8CliParser.TryParseBoolean(passedAllTestsSpan, out passedAllTests))
|
||||
{
|
||||
lineProcessorAction(parsedP, detailed, passedAllTests);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static string BuildFileName(
|
||||
bool bitInc,
|
||||
int threads,
|
||||
int block,
|
||||
GpuKernelType kernelType,
|
||||
bool useLucasFlag,
|
||||
bool useDivisorFlag,
|
||||
bool useByDivisorFlag,
|
||||
bool mersenneOnGpu,
|
||||
bool useOrder,
|
||||
bool useGcd,
|
||||
NttBackend nttBackend,
|
||||
int gpuPrimeThreads,
|
||||
int llSlice,
|
||||
int gpuScanBatch,
|
||||
ulong warmupLimit,
|
||||
ModReductionMode reduction,
|
||||
string mersenneDevice,
|
||||
string primesDevice,
|
||||
string orderDevice)
|
||||
{
|
||||
string inc = bitInc ? "bit" : "add";
|
||||
string mers = useDivisorFlag
|
||||
? "divisor"
|
||||
: (useLucasFlag
|
||||
? "lucas"
|
||||
: (useByDivisorFlag
|
||||
? "bydivisor"
|
||||
: (kernelType == GpuKernelType.Pow2Mod ? "pow2mod" : "incremental")));
|
||||
string ntt = nttBackend == NttBackend.Staged ? "staged" : "reference";
|
||||
string red = reduction switch { ModReductionMode.Mont64 => "mont64", ModReductionMode.Barrett128 => "barrett128", ModReductionMode.GpuUInt128 => "uint128", _ => "auto" };
|
||||
string order = useOrder ? "order-on" : "order-off";
|
||||
string gcd = useGcd ? "gcd-on" : "gcd-off";
|
||||
Span<char> initialBuffer = stackalloc char[256];
|
||||
var builder = new PooledValueStringBuilder(initialBuffer);
|
||||
try
|
||||
{
|
||||
builder.Append("even_perfect_bit_scan_inc-");
|
||||
builder.Append(inc);
|
||||
builder.Append("_thr-");
|
||||
builder.Append(threads);
|
||||
builder.Append("_blk-");
|
||||
builder.Append(block);
|
||||
builder.Append("_mers-");
|
||||
builder.Append(mers);
|
||||
builder.Append("_mersdev-");
|
||||
builder.Append(mersenneDevice);
|
||||
builder.Append("_ntt-");
|
||||
builder.Append(ntt);
|
||||
builder.Append("_red-");
|
||||
builder.Append(red);
|
||||
builder.Append("_primesdev-");
|
||||
builder.Append(primesDevice);
|
||||
builder.Append('_');
|
||||
builder.Append(order);
|
||||
builder.Append("_orderdev-");
|
||||
builder.Append(orderDevice);
|
||||
builder.Append('_');
|
||||
builder.Append(gcd);
|
||||
builder.Append("_gputh-");
|
||||
builder.Append(gpuPrimeThreads);
|
||||
builder.Append("_llslice-");
|
||||
builder.Append(llSlice);
|
||||
builder.Append("_scanb-");
|
||||
builder.Append(gpuScanBatch);
|
||||
builder.Append("_warm-");
|
||||
builder.Append(warmupLimit);
|
||||
builder.Append(".csv");
|
||||
return builder.ToString();
|
||||
}
|
||||
finally
|
||||
{
|
||||
builder.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using System.Buffers;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using EvenPerfectBitScanner.Candidates;
|
||||
using EvenPerfectBitScanner.IO;
|
||||
using Open.Collections;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Cpu;
|
||||
@@ -13,9 +14,9 @@ internal static class Program
|
||||
{
|
||||
private static ThreadLocal<PrimeTester> PrimeTesters = null!;
|
||||
private static ThreadLocal<MersenneNumberTester> MersenneTesters = null!;
|
||||
private static ThreadLocal<ModResidueTracker> PResidue = null!; // p mod d tracker (per-thread)
|
||||
private const ulong InitialP = PerfectNumberConstants.BiggestKnownEvenPerfectP;
|
||||
private static MersenneNumberDivisorGpuTester? _divisorTester;
|
||||
private static UInt128 _divisor;
|
||||
private static IMersenneNumberDivisorByDivisorTester? _byDivisorTester;
|
||||
private static Dictionary<ulong, (bool DetailedCheck, bool PassedAllTests)>? _byDivisorPreviousResults;
|
||||
private static bool _limitReached;
|
||||
@@ -38,6 +39,7 @@ internal static class Program
|
||||
}
|
||||
|
||||
_cliArguments = parsedArguments;
|
||||
_divisor = UInt128.Zero;
|
||||
|
||||
if (_cliArguments.ShowHelp)
|
||||
{
|
||||
@@ -177,7 +179,7 @@ internal static class Program
|
||||
blockSize = 1;
|
||||
}
|
||||
|
||||
PResidue = new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, initialNumber: currentP, initialized: true), trackAllValues: true);
|
||||
CandidatesCalculator.Initialize(currentP);
|
||||
PrimeTesters = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
|
||||
// Note: --primes-device controls default device for library kernels; p primality remains CPU here.
|
||||
// Initialize per-thread p residue tracker (Identity model) at currentP
|
||||
@@ -243,7 +245,7 @@ internal static class Program
|
||||
|
||||
Console.WriteLine("Initialization...");
|
||||
// Compose a results file name that encodes configuration (before opening file)
|
||||
var builtName = BuildResultsFileName(
|
||||
var builtName = CalculationResultsFile.BuildFileName(
|
||||
useBitTransform,
|
||||
threadCount,
|
||||
blockSize,
|
||||
@@ -287,7 +289,7 @@ internal static class Program
|
||||
if (File.Exists(resultsFileName))
|
||||
{
|
||||
Console.WriteLine("Processing previous results...");
|
||||
LoadResultsFile(resultsFileName, (p, detailedCheck, passedAllTests) =>
|
||||
CalculationResultsFile.EnumerateCandidates(resultsFileName, (p, detailedCheck, passedAllTests) =>
|
||||
{
|
||||
CalculationResultHandler.RegisterExistingResult(detailedCheck, passedAllTests);
|
||||
|
||||
@@ -319,7 +321,7 @@ internal static class Program
|
||||
{
|
||||
Console.WriteLine("Loading filter...");
|
||||
int skippedByLimit = 0;
|
||||
byDivisorCandidates = LoadByDivisorCandidates(filterFile, maxPrimeLimit, maxPrimeConfigured, out skippedByLimit);
|
||||
byDivisorCandidates = CalculationResultsFile.LoadCandidatesWithinRange(filterFile, maxPrimeLimit, maxPrimeConfigured, out skippedByLimit);
|
||||
if (maxPrimeConfigured)
|
||||
{
|
||||
if (skippedByLimit > 0)
|
||||
@@ -344,7 +346,7 @@ internal static class Program
|
||||
filterCount = 0;
|
||||
bool addedWithinLimit = false;
|
||||
int skippedByLimit = 0;
|
||||
LoadResultsFile(filterFile, (p, detailedCheck, passedAllTests) =>
|
||||
CalculationResultsFile.EnumerateCandidates(filterFile, (p, detailedCheck, passedAllTests) =>
|
||||
{
|
||||
if (!passedAllTests)
|
||||
{
|
||||
@@ -546,179 +548,6 @@ internal static class Program
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Move this to a new static class CalculationResultsFile under IO namespace / folder. Rename it to LoadCandidatesWithinRange.
|
||||
private static List<ulong> LoadByDivisorCandidates(string candidateFile, UInt128 maxPrimeLimit, bool maxPrimeConfigured, out int skippedByLimit)
|
||||
{
|
||||
List<ulong> candidates = [];
|
||||
skippedByLimit = 0;
|
||||
using FileStream readStream = new(candidateFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
using StreamReader reader = new(readStream);
|
||||
string? line;
|
||||
ReadOnlySpan<char> span;
|
||||
while ((line = reader.ReadLine()) is not null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
span = line.AsSpan().Trim();
|
||||
if (span.IsEmpty || span[0] == '#')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int index = 0;
|
||||
while (index < span.Length)
|
||||
{
|
||||
if (!char.IsDigit(span[index]))
|
||||
{
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
int start = index;
|
||||
index++;
|
||||
while (index < span.Length && char.IsDigit(span[index]))
|
||||
{
|
||||
index++;
|
||||
}
|
||||
|
||||
if (Utf8CliParser.TryParseUInt64(span[start..index], out ulong parsed))
|
||||
{
|
||||
if (!maxPrimeConfigured || (UInt128)parsed <= maxPrimeLimit)
|
||||
{
|
||||
candidates.Add(parsed);
|
||||
}
|
||||
else
|
||||
{
|
||||
skippedByLimit++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return candidates;
|
||||
}
|
||||
|
||||
// TODO: Move this to a new static class CalculationResultsFile under IO namespace / folder. Rename it to EnumerateCandidates.
|
||||
private static void LoadResultsFile(string resultsFileName, Action<ulong, bool, bool> lineProcessorAction)
|
||||
{
|
||||
using FileStream readStream = new(resultsFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
using StreamReader reader = new(readStream);
|
||||
string? line;
|
||||
bool headerSkipped = false;
|
||||
ReadOnlySpan<char> span;
|
||||
int first = 0;
|
||||
int second = 0;
|
||||
int third = 0;
|
||||
ulong parsedP = 0UL;
|
||||
ReadOnlySpan<char> detailedSpan = default;
|
||||
ReadOnlySpan<char> passedAllTestsSpan = default;
|
||||
bool detailed = false;
|
||||
bool passedAllTests = false;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
if (!headerSkipped)
|
||||
{
|
||||
headerSkipped = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
span = line.AsSpan();
|
||||
first = span.IndexOf(',');
|
||||
if (first < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
parsedP = Utf8CliParser.ParseUInt64(span[..first]);
|
||||
span = span[(first + 1)..];
|
||||
second = span.IndexOf(',');
|
||||
if (second < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
span = span[(second + 1)..];
|
||||
third = span.IndexOf(',');
|
||||
if (third < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
detailedSpan = span[..third];
|
||||
passedAllTestsSpan = span[(third + 1)..];
|
||||
|
||||
if (Utf8CliParser.TryParseBoolean(detailedSpan, out detailed) && Utf8CliParser.TryParseBoolean(passedAllTestsSpan, out passedAllTests))
|
||||
{
|
||||
lineProcessorAction(parsedP, detailed, passedAllTests);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Move this to a new static class CalculationResultsFile under IO namespace / folder. Rename it to BuildFileName.
|
||||
private static string BuildResultsFileName(bool bitInc, int threads, int block, GpuKernelType kernelType, bool useLucasFlag, bool useDivisorFlag, bool useByDivisorFlag, bool mersenneOnGpu, bool useOrder, bool useGcd, NttBackend nttBackend, int gpuPrimeThreads, int llSlice, int gpuScanBatch, ulong warmupLimit, ModReductionMode reduction, string mersenneDevice, string primesDevice, string orderDevice)
|
||||
{
|
||||
string inc = bitInc ? "bit" : "add";
|
||||
string mers = useDivisorFlag
|
||||
? "divisor"
|
||||
: (useLucasFlag
|
||||
? "lucas"
|
||||
: (useByDivisorFlag
|
||||
? "bydivisor"
|
||||
: (kernelType == GpuKernelType.Pow2Mod ? "pow2mod" : "incremental")));
|
||||
string ntt = nttBackend == NttBackend.Staged ? "staged" : "reference";
|
||||
string red = reduction switch { ModReductionMode.Mont64 => "mont64", ModReductionMode.Barrett128 => "barrett128", ModReductionMode.GpuUInt128 => "uint128", _ => "auto" };
|
||||
string order = useOrder ? "order-on" : "order-off";
|
||||
string gcd = useGcd ? "gcd-on" : "gcd-off";
|
||||
Span<char> initialBuffer = stackalloc char[256];
|
||||
var builder = new PooledValueStringBuilder(initialBuffer);
|
||||
try
|
||||
{
|
||||
builder.Append("even_perfect_bit_scan_inc-");
|
||||
builder.Append(inc);
|
||||
builder.Append("_thr-");
|
||||
builder.Append(threads);
|
||||
builder.Append("_blk-");
|
||||
builder.Append(block);
|
||||
builder.Append("_mers-");
|
||||
builder.Append(mers);
|
||||
builder.Append("_mersdev-");
|
||||
builder.Append(mersenneDevice);
|
||||
builder.Append("_ntt-");
|
||||
builder.Append(ntt);
|
||||
builder.Append("_red-");
|
||||
builder.Append(red);
|
||||
builder.Append("_primesdev-");
|
||||
builder.Append(primesDevice);
|
||||
builder.Append('_');
|
||||
builder.Append(order);
|
||||
builder.Append("_orderdev-");
|
||||
builder.Append(orderDevice);
|
||||
builder.Append('_');
|
||||
builder.Append(gcd);
|
||||
builder.Append("_gputh-");
|
||||
builder.Append(gpuPrimeThreads);
|
||||
builder.Append("_llslice-");
|
||||
builder.Append(llSlice);
|
||||
builder.Append("_scanb-");
|
||||
builder.Append(gpuScanBatch);
|
||||
builder.Append("_warm-");
|
||||
builder.Append(warmupLimit);
|
||||
builder.Append(".csv");
|
||||
return builder.ToString();
|
||||
}
|
||||
finally
|
||||
{
|
||||
builder.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void OnPrimeCandidateConfirmed()
|
||||
{
|
||||
@@ -784,7 +613,7 @@ internal static class Program
|
||||
}
|
||||
|
||||
// Fast residue-based composite check for p using small primes
|
||||
if (!_cliArguments.UseDivisor && !_cliArguments.UseByDivisor && IsCompositeByResidues(p))
|
||||
if (!_cliArguments.UseDivisor && !_cliArguments.UseByDivisor && CandidatesCalculator.IsCompositeByResidues(p))
|
||||
{
|
||||
searchedMersenne = false;
|
||||
detailedCheck = false;
|
||||
@@ -807,6 +636,14 @@ internal static class Program
|
||||
}
|
||||
|
||||
searchedMersenne = true;
|
||||
if (_cliArguments.UseDivisor)
|
||||
{
|
||||
bool divisorsExhausted;
|
||||
bool isPrime = _divisorTester!.IsPrime(p, _divisor, divisorCyclesSearchLimit, out divisorsExhausted);
|
||||
detailedCheck = divisorsExhausted;
|
||||
return isPrime;
|
||||
}
|
||||
|
||||
if (_cliArguments.UseByDivisor)
|
||||
{
|
||||
// Windowed pow2mod kernel handles by-divisor scans.
|
||||
@@ -817,33 +654,4 @@ internal static class Program
|
||||
return detailedCheck;
|
||||
}
|
||||
|
||||
// TODO: Move this together with the tracker to existing CandidatesCalculator class in the Candidates folder.
|
||||
// Use ModResidueTracker with a small set of primes to pre-filter composite p.
|
||||
private static bool IsCompositeByResidues(ulong p)
|
||||
{
|
||||
var tracker = PResidue.Value!;
|
||||
// TODO: Integrate the divisor-cycle cache here so the small-prime sweep reuses precomputed remainders instead of
|
||||
// running MergeOrAppend for every candidate and missing the cycle-accelerated early exits.
|
||||
tracker.BeginMerge(p);
|
||||
// Use the small prime list from PerfectNumbers.Core to the extent of sqrt(p)
|
||||
var primes = PrimesGenerator.SmallPrimes;
|
||||
var primesPow2 = PrimesGenerator.SmallPrimesPow2;
|
||||
int len = primes.Length;
|
||||
int primeIndex = 0;
|
||||
bool divisible;
|
||||
for (; primeIndex < len; primeIndex++)
|
||||
{
|
||||
if (primesPow2[primeIndex] > p)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (tracker.MergeOrAppend(p, primes[primeIndex], out divisible) && divisible)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user