14 KiB
EvenPerfectBitScanner --mersenne=bydivisor Flow and Heuristic Prime Tester Plan
1. Execution Flow in --mersenne=bydivisor Mode
The following map captures every branch that executes when the scanner runs with --mersenne=bydivisor, highlighting interactions with prime testing, divisor heuristics, residue reuse, and factor verification.
-
CLI processing and guards
CliArguments.Parsefills_cliArguments, thenMainvalidates thread count, help flag, and shared GPU settings.useByDivisoris set from CLI. If enabled without--filter-p(and not in test mode), the scanner prints "--mersenne=bydivisor requires --filter-p=." and exits early.【F:EvenPerfectBitScanner/Program.cs†L32-L132】
-
Global configuration
- GPU settings (
GpuPrimeWorkLimiter,PrimeTester.GpuBatchSize, transforms) are configured for both CPU and GPU runs. - By-divisor specific state records
_byDivisorStartPrime,_byDivisorTester, and_byDivisorPreviousResultsfor later reuse.【F:EvenPerfectBitScanner/Program.cs†L134-L232】
- GPU settings (
-
Tester initialization
- When
useByDivisoris active,_byDivisorTesteris instantiated as GPU or CPU implementation, withBatchSizeset from--scan-batch.PrimeTester/MersenneNumberTesterpools are not created for this mode because the flow exits before the generic candidate loop.【F:EvenPerfectBitScanner/Program.cs†L200-L232】【F:EvenPerfectBitScanner/Program.cs†L400-L460】
- When
-
Results bootstrap
- The scanner builds the results filename, initializes
CalculationResultHandler, and loads previous results. Previous entries populate_byDivisorPreviousResultsso the by-divisor runner can skip already processed primes.【F:EvenPerfectBitScanner/Program.cs†L232-L318】
- The scanner builds the results filename, initializes
-
Candidate acquisition
byDivisorCandidatesis populated either from deterministic test data (test mode) or by reloading the filter file withCalculationResultsFile.LoadCandidatesWithinRange. Limits (--max-prime) and skipped candidates are reported to the console.【F:EvenPerfectBitScanner/Program.cs†L318-L362】- In contrast to other modes, generic residue filters and candidate queues are bypassed; only the provided prime list is considered.【F:EvenPerfectBitScanner/Program.cs†L318-L362】
-
Previous-result pruning
MersenneNumberDivisorByDivisorTester.Runsorts the input primes, removes entries already present in_byDivisorPreviousResults, applies--start-prime, and converts the remaining list to spans for batch preparation.【F:PerfectNumbers.Core/MersenneNumberDivisorByDivisorTester.cs†L11-L138】
-
Tester configuration per maximum prime
IMersenneNumberDivisorByDivisorTester.ConfigureFromMaxPrimeandPrepareCandidatespre-compute divisor limits for every prime. All primes withallowedMax >= 3are retained (current implementations never filter any out in production because limits are huge).【F:PerfectNumbers.Core/MersenneNumberDivisorByDivisorTester.cs†L140-L201】
-
Residue-sieved divisor scanning (CPU)
MersenneNumberDivisorByDivisorCpuTester.CheckDivisorsinitialises Montgomery stepping data (GpuUInt128), computes rolling residues for mod 10/8/5/3/7/11, and advances divisors by2pwhile applying decimal masks tied to the exponent’s last digit.【F:PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTester.cs†L63-L213】- When a candidate passes residue filters, the CPU session obtains cycle lengths from
DivisorCycleCacheorMersenneDivisorCycles, validates them via Montgomery exponentiation, and reports hits while updating_lastStatusDivisor.【F:PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTester.cs†L213-L318】
-
Residue-sieved divisor scanning (GPU)
MersenneNumberDivisorByDivisorGpuTester.CheckDivisorsreuses the same wheel offsets and residue tests but performs them insideDivisorByDivisorKernels.CheckKernel, writing hits to pooled GPU buffers. CPU fallbacks honour the same residue, decimal mask, and cycle rules when GPU context is unavailable.【F:PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs†L1-L240】【F:PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs†L240-L520】
-
Prime decision path
- By-divisor sessions internally reuse divisor remainders across primes, but they do not call
PrimeTester.IsPrime*. The primality decision depends entirely on divisor scans up to the limit configured for each prime candidate. - If
IsPrimereturnsfalse, the result file records(detailedCheck=true, passedAllTests=false). Whentrue,divisorsExhaustedreports whether the scan reached the configured divisor bound (driving theDetailedCheckflag).【F:PerfectNumbers.Core/MersenneNumberDivisorByDivisorTester.cs†L203-L272】
- By-divisor sessions internally reuse divisor remainders across primes, but they do not call
-
Shutdown
- After
MersenneNumberDivisorByDivisorTester.Runcompletes, the main loop flushes results and exits before any of the generic candidate enumeration logic runs.【F:EvenPerfectBitScanner/Program.cs†L400-L460】
- After
2. Baseline Prime Tester Methods
The heuristics will replace the internal logic of the current GPU-assisted and CPU-only paths. The snapshots below capture the existing implementations before modification (copied verbatim for reference).
public bool IsPrimeGpu(ulong n, CancellationToken ct)
{
bool forceCpu = GpuContextPool.ForceCpu;
Span<ulong> one = stackalloc ulong[1];
Span<byte> outFlags = stackalloc byte[1];
one[0] = n;
outFlags[0] = 0;
if (!forceCpu)
{
IsPrimeBatchGpu(one, outFlags);
}
bool belowGpuRange = n < 31UL;
bool gpuReportedPrime = !forceCpu && !belowGpuRange && outFlags[0] != 0;
bool requiresCpuFallback = forceCpu || belowGpuRange || !gpuReportedPrime;
return requiresCpuFallback ? IsPrimeCpu(n, ct) : true;
}
internal static bool IsPrimeCpu(ulong n, CancellationToken ct)
{
bool isTwo = n == 2UL;
bool isOdd = (n & 1UL) != 0UL;
ulong mod5 = n % 5UL;
bool divisibleByFive = n > 5UL && mod5 == 0UL;
bool result = n >= 2UL && (isTwo || isOdd) && !divisibleByFive;
bool requiresTrialDivision = result && n >= 7UL && !isTwo;
if (requiresTrialDivision)
{
bool sharesMaxExponentFactor = n.Mod10() == 1UL && SharesFactorWithMaxExponent(n);
result &= !sharesMaxExponentFactor;
if (result)
{
var smallPrimeDivisorsLength = PrimesGenerator.SmallPrimes.Length;
uint[] smallPrimeDivisors = PrimesGenerator.SmallPrimes;
ulong[] smallPrimeDivisorsMul = PrimesGenerator.SmallPrimesPow2;
for (int i = 0; i < smallPrimeDivisorsLength; i++)
{
if (smallPrimeDivisorsMul[i] > n)
{
break;
}
if (n % smallPrimeDivisors[i] == 0)
{
result = false;
break;
}
}
}
}
return result;
}
3. Implementation Plan for HeuristicPrimeTester
The plan incorporates the updated divisor-class heuristics (Groups A/B, wheels, residue priorities) and connects them to both CPU and GPU execution, including every by-divisor integration point. Items already completed are marked with [done].
- [done] Snapshot current implementations – preserve
IsPrimeGpuandIsPrimeCpuas references for regression comparisons and migration checkpoints (see Section 2). - [done] Introduce placeholder heuristic entry points – add
IsPrimeCpuandIsPrimeGpuby copying the current internal and GPU methods so subsequent commits can iterate without disrupting legacy behavior. - [done] Design shared heuristic scaffolding
- [done] Added
HeuristicPrimeSievesto precompute 4M-entry Group A/B divisor tables so heuristic prime checks reuse cached sequences without regenerating wheel steps at runtime; the enumerator remains available for residue-driven consumers. - [done] Surface helpers that accept the precomputed
R = ⌊√n⌋andnMod10values from callers via the new overload ofIsPrimeCpu; HeuristicPrimeTester still computes them locally until call sites forward the parameters. - [done] Simplified the heuristic execution path so it runs without cancellation tokens or callback plumbing while keeping the shared enumerator available to other components.
- [done] Added
- Share residue and wheel state with by-divisor sessions
- [done] Extracted the rolling-modulus helpers (mod 10/8/5/3/7/11) and decimal masks from
MersenneNumberDivisorByDivisorCpuTester.CheckDivisorsinto the sharedMersenneDivisorResidueStepperso heuristic prime tests and by-divisor sessions consume the same filters. - [done] Audited how
MersenneCpuDivisorScanSessionadvances residues viaExponentRemainderStepperCpu/CycleRemainderStepperand updated the GPU by-divisor path to reuseMersenneDivisorResidueStepper, keeping CPU/GPU residue stepping aligned. - [done] Publish wheel iterators that can stream candidates directly into
DivisorCycleCache, GPU hit buffers, or CPU Montgomery checks without recomputing offsets by exposingHeuristicPrimeTester.CreateHeuristicDivisorEnumeratorand the reusableHeuristicGroupBSequenceStatebuffer.
- [done] Extracted the rolling-modulus helpers (mod 10/8/5/3/7/11) and decimal masks from
- Expose cycle-preparation helpers
- [done] Extend the heuristic scaffolding so it returns
MontgomeryDivisorDatadescriptors and cycle-length hints for each candidate divisor viaHeuristicDivisorPreparationandPrepareHeuristicDivisor. - Wire these outputs into the existing cycle calculators to avoid duplicate cache lookups inside the CPU/GPU divisor scans.
- [done] Extend the heuristic scaffolding so it returns
- [done] Implement
IsPrimeCpu- Baseline Group A/B enumeration now executes inside
IsPrimeCpuvia the cachedHeuristicPrimeSievestables, computing⌊√n⌋locally and short-circuiting on the first divisor. - [done] The CPU heuristic now operates as a pure trial-division sweep without maintaining per-call summaries, relying on
PrepareHeuristicDivisoronly when downstream consumers request Montgomery data. - [done] Introduced the temporary
UseHeuristicGroupBTrialDivisiongate so current builds execute Group A locally and then fall back toOpen.Numeric.Primes.Prime.Numbers.IsPrime, keeping the full Group B implementation available for reactivation.
- Baseline Group A/B enumeration now executes inside
- [done] Implement
IsPrimeGpu- [done] Mirror CPU structure while batching divisor checks on the accelerator via
HeuristicTrialDivisionGpu, drawing divisors fromHeuristicPrimeSieves, reusing caller-providedsqrtLimit/nMod10inputs, and falling back to CPU when GPUs are disabled. - [done] Added
PrimeTesterKernels.HeuristicTrialDivisionKernelso GPU batches respect Group A/B ordering while emitting hit flags for early exits. - [done] Kept GPU divisibility in standard modular arithmetic while reserving Montgomery transforms for CPU confirmation logic.
- [done] When the temporary Group B gate is disabled, delegate GPU calls to the CPU fallback so Group A coverage remains in place while the accelerator path stays available for future re-enablement.
- [done] Mirror CPU structure while batching divisor checks on the accelerator via
- [done] Refactor by-divisor CPU/GPU testers to reuse heuristics
- [done] Drove
MersenneNumberDivisorByDivisorCpuTester.CheckDivisorsfromHeuristicPrimeTester.CreateMersenneDivisorEnumerator, eliminating the bespoke residue loop and retiring the legacy status counter. - [done] Streamed GPU batches from the same enumerator and Montgomery preparation data, removing the filtered-divisor scratch arrays while continuing to feed the existing kernel and hit bookkeeping.
- [done] Drove
- [done] Route legacy prime checks through heuristics
- [done] Introduce
HeuristicPrimeTesterso heuristic CPU/GPU checks live alongside the legacyPrimeTesterimplementations while callers opt in explicitly. - [done] Maintain legacy paths for benchmarking via
PrimeTester.IsPrimeCpu,PrimeTester.Exclusive.IsPrimeGpu, and the explicitHeuristicPrimeTesterentry points so benchmark harnesses can compare behaviours without runtime switches.
- [done] Introduce
- Validation and benchmarking
- Create targeted unit tests covering boundary cases for Group A/B transitions, wheel enumerations, residue deltas, and the stop-on-hit behaviour.
- Expand GPU test harnesses to assert equivalence between CPU/GPU heuristics on sampled composite and prime inputs and to verify the residue-stream contract with by-divisor sessions.
- Benchmark against historical data (hundreds of thousands of composites) to confirm that heuristic ordering improves early divisor detection and that residue sharing reduces duplicate computation.
- [done] Build BenchmarkDotNet comparisons for
HeuristicPrimeTester.IsPrimeGpu,HeuristicPrimeTester.IsPrimeCpu, andOpen.Numeric.Primes.Prime.Numbers.IsPrimeacross representative ranges (≤100, ≤4,000,000, and ≥138,000,000) before routing heuristic code into production. Implemented inEvenPerfectBitScanner.Benchmarks/PrimeTesterBenchmarks.csto exercise both CPU and GPU heuristic paths alongside the external library.
- Documentation and rollout
- Update README/docs to explain heuristic prerequisites (odd, not divisible by 5) and how configuration toggles affect execution across prime testing and by-divisor scans.
- Provide migration guidance for operators toggling between legacy and heuristic modes and describe how telemetry/metrics change after integration.
- Publish benchmark results in the heuristic specification once measurements are available so the documented guidance reflects the observed CPU/GPU/external comparisons.
4. Remaining Work Summary
- Complete the remaining rollout tasks in steps 10–11, including legacy-call-site integration, validation, and documentation updates.
- Develop comprehensive validation suites (unit tests and performance benchmarks) to compare heuristic behaviour with current implementations across both prime testing and by-divisor divisor scans.
- Document configuration switches, runtime metrics, and operational playbooks once the heuristics are in place.