80 lines
2.4 KiB
C#
80 lines
2.4 KiB
C#
using System.Runtime.CompilerServices;
|
|
using System.Threading;
|
|
using EvenPerfectBitScanner.Candidates;
|
|
using Open.Numeric.Primes;
|
|
|
|
namespace EvenPerfectBitScanner.Candidates.Transforms;
|
|
|
|
internal static class CandidateAddPrimesTransform
|
|
{
|
|
private static readonly Optimized PrimeIterator = new();
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
internal static ulong Transform(ulong value, ref ulong remainder, ref bool limitReached)
|
|
{
|
|
ulong originalRemainder = remainder;
|
|
ulong addRemainder = remainder;
|
|
ulong candidate = value;
|
|
ulong primeCandidate = value;
|
|
bool advanceAdd = true;
|
|
bool advancePrime = true;
|
|
ulong diff;
|
|
ulong nextPrime;
|
|
|
|
while (true)
|
|
{
|
|
if (advanceAdd)
|
|
{
|
|
diff = addRemainder.GetNextAddDiff();
|
|
if (candidate > ulong.MaxValue - diff)
|
|
{
|
|
Volatile.Write(ref limitReached, true);
|
|
remainder = originalRemainder;
|
|
return candidate;
|
|
}
|
|
|
|
candidate += diff;
|
|
addRemainder += diff;
|
|
// Skip the Mod6 lookup: benchmarked `%` stays ahead for these prime increments.
|
|
// benchmarked fastest remainder updates instead of looping subtraction.
|
|
while (addRemainder >= 6UL)
|
|
{
|
|
addRemainder -= 6UL;
|
|
}
|
|
}
|
|
|
|
if (advancePrime)
|
|
{
|
|
try
|
|
{
|
|
nextPrime = PrimeIterator.Next(in primeCandidate);
|
|
}
|
|
catch (InvalidOperationException)
|
|
{
|
|
Volatile.Write(ref limitReached, true);
|
|
remainder = originalRemainder;
|
|
return value;
|
|
}
|
|
|
|
if (nextPrime <= primeCandidate)
|
|
{
|
|
Volatile.Write(ref limitReached, true);
|
|
remainder = originalRemainder;
|
|
return value;
|
|
}
|
|
|
|
primeCandidate = nextPrime;
|
|
}
|
|
|
|
if (candidate == primeCandidate)
|
|
{
|
|
remainder = addRemainder;
|
|
return candidate;
|
|
}
|
|
|
|
advanceAdd = candidate < primeCandidate;
|
|
advancePrime = candidate > primeCandidate;
|
|
}
|
|
}
|
|
}
|