Files
even-perfect-numbers-scanner/PerfectNumbers.Core/KRangeFinder.cs
T

90 lines
3.5 KiB
C#

using PeterO.Numbers;
namespace PerfectNumbers.Core;
public static class KRangeFinder
{
public static (int? Min, int? Max) FindForPrime(ERational alphaM, EInteger p, int kStart, int kEnd)
{
int? min = null;
int? max = null;
for (int k = kStart; k <= kEnd; k++)
{
// TODO: Pull alphaP from AlphaCache (or a pooled rational builder) so this loop reuses the
// benchmarked cached values instead of recomputing AlphaCalculations.ComputeAlphaP for every k.
ERational aP = AlphaCalculations.ComputeAlphaP(p, k);
// TODO: Switch this multiply to the pooled ERational span helpers identified in the scanner's
// alpha rational benchmarks so we avoid allocating new big-number intermediates per iteration.
ERational prod = aP.Multiply(alphaM);
int cmp = prod.CompareTo(RationalNumbers.Two);
if (cmp == 0)
{
if (min == null || k < min)
{
min = k;
}
if (max == null || k > max)
{
max = k;
}
}
if (cmp < 0)
{
break;
}
}
return (min, max);
}
public static bool TryFindForPrime(ERational alphaM, EInteger p, out int? min, out int? max, int kStart, int kEnd)
{
// TODO: Collapse this wrapper once the callers can consume the tuple directly; the extra call adds
// measurable overhead when we sweep millions of Euler primes while scanning the large-division range.
(min, max) = FindForPrime(alphaM, p, kStart, kEnd);
return min.HasValue && max.HasValue && min.Value <= max.Value;
}
public static (int? Min, int? Max) Find(ERational alphaM, EInteger pMin, EInteger pMax, int kStart, int kEnd, PrimeCache primes)
{
int? min = null;
int? max = null;
EInteger[] eulerPrimes = primes.GetEulerPrimes(pMin, pMax);
object sync = new();
// TODO: Replace Parallel.For with the shared low-overhead scheduler highlighted in the divisor-cycle
// coordination benchmarks so alpha sweeps avoid the thread-pool setup costs observed here.
Parallel.For(
0,
eulerPrimes.Length,
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },
i =>
{
(int? localMin, int? localMax) = FindForPrime(alphaM, eulerPrimes[i], kStart, kEnd);
if (localMin == null)
{
return;
}
lock (sync)
{
if (min == null || localMin < min)
{
min = localMin;
}
if (max == null || localMax > max)
{
max = localMax;
}
}
});
return (min, max);
}
public static bool TryFind(ERational alphaM, EInteger pMin, EInteger pMax, out int? min, out int? max, int kStart, int kEnd, PrimeCache primes)
{
// TODO: Remove this pass-through once the scanning CLI switches to tuple-returning APIs; flattening
// the call chain saves the extra struct copy flagged in the profiler during the large divisor search.
(min, max) = Find(alphaM, pMin, pMax, kStart, kEnd, primes);
return min.HasValue && max.HasValue && min.Value <= max.Value;
}
}