Refine heuristic prime tester integration
This commit is contained in:
1 parent
21d645fcf0
commit
1e5eadac34
10 files changed
+186
-263
No files matched your search
@@ -71,7 +71,6 @@ public sealed class HeuristicPrimeTester
|
||||
public readonly bool RequiresCycleComputation = !hasCycleLengthHint;
|
||||
}
|
||||
|
||||
public static bool EnableHeuristicPrimeTesting { get; set; } = true;
|
||||
|
||||
public static int HeuristicGpuDivisorBatchSize { get; set; } = 4_096;
|
||||
|
||||
@@ -82,30 +81,25 @@ public sealed class HeuristicPrimeTester
|
||||
|
||||
public static HeuristicPrimeTester Exclusive => _tester ??= new();
|
||||
|
||||
public bool IsPrime(ulong n, CancellationToken ct)
|
||||
public bool IsPrimeCpu(ulong n, CancellationToken ct)
|
||||
{
|
||||
if (!EnableHeuristicPrimeTesting)
|
||||
ct.ThrowIfCancellationRequested();
|
||||
|
||||
byte nMod10 = (byte)n.Mod10();
|
||||
ulong sqrtLimit = ComputeHeuristicSqrt(n);
|
||||
|
||||
if (sqrtLimit < 3UL)
|
||||
{
|
||||
return PrimeTester.IsPrimeInternal(n, ct);
|
||||
return EvaluateWithOpenNumericFallback(n);
|
||||
}
|
||||
|
||||
return HeuristicIsPrimeCpu(n);
|
||||
bool includeGroupB = UseHeuristicGroupBTrialDivision;
|
||||
return HeuristicTrialDivisionCpu(n, sqrtLimit, nMod10, includeGroupB);
|
||||
}
|
||||
|
||||
public bool IsPrime(ulong n)
|
||||
public bool IsPrimeCpu(ulong n)
|
||||
{
|
||||
return IsPrime(n, CancellationToken.None);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsPrimeGpu(ulong n, CancellationToken ct)
|
||||
{
|
||||
if (!EnableHeuristicPrimeTesting)
|
||||
{
|
||||
return PrimeTester.Exclusive.IsPrimeGpu(n, ct);
|
||||
}
|
||||
|
||||
return IsPrimeGpu(n);
|
||||
return IsPrimeCpu(n, CancellationToken.None);
|
||||
}
|
||||
|
||||
public bool IsPrimeGpu(ulong n)
|
||||
@@ -117,45 +111,9 @@ public sealed class HeuristicPrimeTester
|
||||
|
||||
public bool IsPrimeGpu(ulong n, ulong sqrtLimit, byte nMod10)
|
||||
{
|
||||
if (!EnableHeuristicPrimeTesting)
|
||||
{
|
||||
return PrimeTester.Exclusive.IsPrimeGpu(n, CancellationToken.None);
|
||||
}
|
||||
|
||||
return HeuristicIsPrimeGpuCore(n, sqrtLimit, nMod10);
|
||||
}
|
||||
|
||||
public bool HeuristicIsPrimeGpu(ulong n)
|
||||
{
|
||||
return IsPrimeGpu(n);
|
||||
}
|
||||
|
||||
public static bool HeuristicIsPrimeCpu(ulong n)
|
||||
{
|
||||
return HeuristicIsPrimeCpu(n, 0UL, (byte)n.Mod10());
|
||||
}
|
||||
|
||||
public static bool HeuristicIsPrimeCpu(ulong n, ulong sqrtLimit, byte nMod10)
|
||||
{
|
||||
if (!EnableHeuristicPrimeTesting)
|
||||
{
|
||||
return PrimeTester.IsPrimeInternal(n, CancellationToken.None);
|
||||
}
|
||||
|
||||
if (sqrtLimit == 0UL)
|
||||
{
|
||||
sqrtLimit = ComputeHeuristicSqrt(n);
|
||||
}
|
||||
|
||||
if (sqrtLimit < 3UL)
|
||||
{
|
||||
return EvaluateWithOpenNumericFallback(n);
|
||||
}
|
||||
|
||||
bool includeGroupB = UseHeuristicGroupBTrialDivision;
|
||||
return HeuristicTrialDivisionCpu(n, sqrtLimit, nMod10, includeGroupB);
|
||||
}
|
||||
|
||||
private bool HeuristicIsPrimeGpuCore(ulong n, ulong sqrtLimit, byte nMod10)
|
||||
{
|
||||
// TODO: Is this condition ever met on the execution path in EvenPerfectBitScanner?
|
||||
@@ -169,11 +127,6 @@ public sealed class HeuristicPrimeTester
|
||||
return HeuristicTrialDivisionCpu(n, sqrtLimit, nMod10, includeGroupB: false);
|
||||
}
|
||||
|
||||
if (GpuContextPool.ForceCpu)
|
||||
{
|
||||
return HeuristicIsPrimeCpu(n, sqrtLimit, nMod10);
|
||||
}
|
||||
|
||||
bool compositeDetected = HeuristicTrialDivisionGpuDetectsDivisor(n, sqrtLimit, nMod10);
|
||||
return !compositeDetected;
|
||||
}
|
||||
@@ -304,6 +257,7 @@ public sealed class HeuristicPrimeTester
|
||||
|
||||
|
||||
|
||||
|
||||
private bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong sqrtLimit, byte nMod10)
|
||||
{
|
||||
int batchCapacity = Math.Max(1, HeuristicGpuDivisorBatchSize);
|
||||
@@ -311,194 +265,165 @@ public sealed class HeuristicPrimeTester
|
||||
var divisorPool = ThreadStaticPools.UlongPool;
|
||||
var hitPool = ThreadStaticPools.BytePool;
|
||||
|
||||
ulong[]? divisorArray = null;
|
||||
byte[]? hitFlags = null;
|
||||
ulong[] divisorArray = divisorPool.Rent(batchCapacity);
|
||||
byte[] hitFlags = hitPool.Rent(batchCapacity);
|
||||
|
||||
var limiter = GpuPrimeWorkLimiter.Acquire();
|
||||
var gpu = PrimeTester.PrimeTesterGpuContextPool.Rent();
|
||||
var accelerator = gpu.Accelerator;
|
||||
var state = PrimeTester.GpuKernelState.GetOrCreate(accelerator);
|
||||
var scratch = state.RentScratch(batchCapacity, accelerator);
|
||||
|
||||
bool compositeDetected = false;
|
||||
int count = 0;
|
||||
|
||||
try
|
||||
lock (gpu.ExecutionLock)
|
||||
{
|
||||
divisorArray = divisorPool.Rent(batchCapacity);
|
||||
hitFlags = hitPool.Rent(batchCapacity);
|
||||
var input = scratch.Input;
|
||||
var output = scratch.Output;
|
||||
|
||||
var accelerator = gpu.Accelerator;
|
||||
var state = PrimeTester.GpuKernelState.GetOrCreate(accelerator);
|
||||
|
||||
lock (gpu.ExecutionLock)
|
||||
bool ProcessBatch(int length)
|
||||
{
|
||||
var scratch = state.RentScratch(batchCapacity, accelerator);
|
||||
try
|
||||
if (length <= 0)
|
||||
{
|
||||
int count = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProcessBatch(int length)
|
||||
input.View.CopyFromCPU(ref divisorArray[0], length);
|
||||
state.HeuristicTrialDivisionKernel(length, input.View, n, output.View);
|
||||
accelerator.Synchronize();
|
||||
output.View.CopyToCPU(ref hitFlags[0], length);
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
if (hitFlags[i] == 0)
|
||||
{
|
||||
if (length <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
scratch.Input.View.CopyFromCPU(ref divisorArray![0], length);
|
||||
state.HeuristicTrialDivisionKernel(length, scratch.Input.View, n, scratch.Output.View);
|
||||
accelerator.Synchronize();
|
||||
scratch.Output.View.CopyToCPU(ref hitFlags![0], length);
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
if (hitFlags![i] == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ulong divisor = divisorArray![i];
|
||||
if (divisor > 1UL && n % divisor == 0UL)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool QueueDivisor(ulong divisor)
|
||||
ulong divisor = divisorArray[i];
|
||||
if (divisor > 1UL && n % divisor == 0UL)
|
||||
{
|
||||
divisorArray![count] = divisor;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ReadOnlySpan<int> groupADivisors = HeuristicPrimeSieves.GroupADivisors;
|
||||
int groupAIndex = 0;
|
||||
ReadOnlySpan<byte> endingOrder = GetGroupBEndingOrder(nMod10);
|
||||
Span<int> indices = endingOrder.Length <= 8
|
||||
? stackalloc int[endingOrder.Length]
|
||||
: new int[endingOrder.Length];
|
||||
|
||||
bool groupAHasMore = groupAIndex < groupADivisors.Length && (ulong)groupADivisors[groupAIndex] <= sqrtLimit;
|
||||
bool groupBHasMore = !endingOrder.IsEmpty && HasGroupBCandidates(endingOrder, indices, sqrtLimit);
|
||||
|
||||
while (!compositeDetected && (groupAHasMore || groupBHasMore))
|
||||
{
|
||||
if (groupAHasMore)
|
||||
{
|
||||
int processed = 0;
|
||||
while (processed < interleaveBatchSize && groupAIndex < groupADivisors.Length)
|
||||
{
|
||||
ulong divisor = (ulong)groupADivisors[groupAIndex];
|
||||
if (divisor > sqrtLimit)
|
||||
{
|
||||
groupAIndex = groupADivisors.Length;
|
||||
groupAHasMore = false;
|
||||
break;
|
||||
}
|
||||
|
||||
divisorArray[count] = divisor;
|
||||
count++;
|
||||
|
||||
if (count == batchCapacity)
|
||||
{
|
||||
if (ProcessBatch(count))
|
||||
{
|
||||
return true;
|
||||
compositeDetected = true;
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
groupAIndex++;
|
||||
processed++;
|
||||
}
|
||||
|
||||
ReadOnlySpan<int> groupADivisors = HeuristicPrimeSieves.GroupADivisors;
|
||||
int groupAIndex = 0;
|
||||
ReadOnlySpan<byte> endingOrder = GetGroupBEndingOrder(nMod10);
|
||||
Span<int> indices = endingOrder.Length <= 8
|
||||
? stackalloc int[endingOrder.Length]
|
||||
: new int[endingOrder.Length];
|
||||
|
||||
bool groupAHasMore = groupAIndex < groupADivisors.Length && (ulong)groupADivisors[groupAIndex] <= sqrtLimit;
|
||||
bool groupBHasMore = !endingOrder.IsEmpty && HasGroupBCandidates(endingOrder, indices, sqrtLimit);
|
||||
|
||||
while (!compositeDetected && (groupAHasMore || groupBHasMore))
|
||||
if (groupAIndex >= groupADivisors.Length)
|
||||
{
|
||||
if (groupAHasMore)
|
||||
{
|
||||
int processed = 0;
|
||||
while (processed < interleaveBatchSize && groupAIndex < groupADivisors.Length)
|
||||
{
|
||||
ulong divisor = (ulong)groupADivisors[groupAIndex];
|
||||
if (divisor > sqrtLimit)
|
||||
{
|
||||
groupAIndex = groupADivisors.Length;
|
||||
groupAHasMore = false;
|
||||
break;
|
||||
}
|
||||
|
||||
compositeDetected = QueueDivisor(divisor);
|
||||
if (compositeDetected)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
groupAIndex++;
|
||||
processed++;
|
||||
}
|
||||
|
||||
if (compositeDetected)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (groupAIndex >= groupADivisors.Length)
|
||||
{
|
||||
groupAHasMore = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
groupAHasMore = (ulong)groupADivisors[groupAIndex] <= sqrtLimit;
|
||||
if (!groupAHasMore)
|
||||
{
|
||||
groupAIndex = groupADivisors.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (groupBHasMore)
|
||||
{
|
||||
int processed = 0;
|
||||
while (processed < interleaveBatchSize)
|
||||
{
|
||||
if (!TrySelectNextGroupBDivisor(endingOrder, indices, sqrtLimit, out ulong divisor))
|
||||
{
|
||||
groupBHasMore = false;
|
||||
break;
|
||||
}
|
||||
|
||||
compositeDetected = QueueDivisor(divisor);
|
||||
if (compositeDetected)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
processed++;
|
||||
}
|
||||
|
||||
if (compositeDetected)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (groupBHasMore)
|
||||
{
|
||||
groupBHasMore = HasGroupBCandidates(endingOrder, indices, sqrtLimit);
|
||||
}
|
||||
}
|
||||
groupAHasMore = false;
|
||||
}
|
||||
|
||||
if (!compositeDetected && count > 0)
|
||||
else
|
||||
{
|
||||
compositeDetected = ProcessBatch(count);
|
||||
groupAHasMore = (ulong)groupADivisors[groupAIndex] <= sqrtLimit;
|
||||
if (!groupAHasMore)
|
||||
{
|
||||
groupAIndex = groupADivisors.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
||||
if (!compositeDetected && groupBHasMore)
|
||||
{
|
||||
state.ReturnScratch(scratch);
|
||||
int processed = 0;
|
||||
while (processed < interleaveBatchSize)
|
||||
{
|
||||
if (!TrySelectNextGroupBDivisor(endingOrder, indices, sqrtLimit, out ulong divisor))
|
||||
{
|
||||
groupBHasMore = false;
|
||||
break;
|
||||
}
|
||||
|
||||
divisorArray[count] = divisor;
|
||||
count++;
|
||||
|
||||
if (count == batchCapacity)
|
||||
{
|
||||
if (ProcessBatch(count))
|
||||
{
|
||||
compositeDetected = true;
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
}
|
||||
|
||||
processed++;
|
||||
}
|
||||
|
||||
if (groupBHasMore)
|
||||
{
|
||||
groupBHasMore = HasGroupBCandidates(endingOrder, indices, sqrtLimit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (divisorArray is not null)
|
||||
{
|
||||
divisorPool.Return(divisorArray);
|
||||
}
|
||||
|
||||
if (hitFlags is not null)
|
||||
if (!compositeDetected && count > 0)
|
||||
{
|
||||
hitPool.Return(hitFlags);
|
||||
}
|
||||
if (ProcessBatch(count))
|
||||
{
|
||||
compositeDetected = true;
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
gpu.Dispose();
|
||||
limiter.Dispose();
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Cleanup:
|
||||
state.ReturnScratch(scratch);
|
||||
divisorPool.Return(divisorArray);
|
||||
hitPool.Return(hitFlags);
|
||||
gpu.Dispose();
|
||||
limiter.Dispose();
|
||||
return compositeDetected;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static bool EvaluateWithOpenNumericFallback(ulong n)
|
||||
{
|
||||
return Prime.Numbers.IsPrime(n);
|
||||
|
||||
@@ -394,9 +394,9 @@ public class MersenneDivisorCycles
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Benchmark PrimeTester.IsPrimeInternal against Open.Numeric.Primes on the EvenPerfectBitScanner workloads to confirm the faster option for wide composites.
|
||||
// if (HeuristicPrimeTester.Exclusive.IsPrime(remaining, CancellationToken.None))
|
||||
if (PrimeTester.IsPrimeInternal(remaining, CancellationToken.None))
|
||||
// TODO: Benchmark PrimeTester.IsPrimeCp against Open.Numeric.Primes on the EvenPerfectBitScanner workloads to confirm the faster option for wide composites.
|
||||
// if (HeuristicPrimeTester.Exclusive.IsPrimeCpu(remaining, CancellationToken.None))
|
||||
if (PrimeTester.IsPrimeCp(remaining, CancellationToken.None))
|
||||
{
|
||||
AddFactor(counts, remaining);
|
||||
return true;
|
||||
@@ -928,8 +928,8 @@ public class MersenneDivisorCycles
|
||||
return true;
|
||||
}
|
||||
|
||||
// if (!skipPrimeOrderHeuristic && HeuristicPrimeTester.Exclusive.IsPrime(divisor, CancellationToken.None))
|
||||
if (!skipPrimeOrderHeuristic && PrimeTester.IsPrimeInternal(divisor, CancellationToken.None))
|
||||
// if (!skipPrimeOrderHeuristic && HeuristicPrimeTester.Exclusive.IsPrimeCpu(divisor, CancellationToken.None))
|
||||
if (!skipPrimeOrderHeuristic && PrimeTester.IsPrimeCp(divisor, CancellationToken.None))
|
||||
{
|
||||
ulong computedOrder = PrimeOrderCalculator.Calculate(
|
||||
divisor,
|
||||
|
||||
@@ -25,8 +25,8 @@ public static class MersennePrimeFactorTester
|
||||
return false;
|
||||
}
|
||||
|
||||
// if (!HeuristicPrimeTester.Exclusive.IsPrime(q, ct))
|
||||
if (!PrimeTester.IsPrimeInternal(q, ct))
|
||||
// if (!HeuristicPrimeTester.Exclusive.IsPrimeCpu(q, ct))
|
||||
if (!PrimeTester.IsPrimeCp(q, ct))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -234,8 +234,8 @@ public static class MersennePrimeFactorTester
|
||||
continue;
|
||||
}
|
||||
|
||||
// if (HeuristicPrimeTester.Exclusive.IsPrime(m, ct))
|
||||
if (PrimeTester.IsPrimeInternal(m, ct))
|
||||
// if (HeuristicPrimeTester.Exclusive.IsPrimeCpu(m, ct))
|
||||
if (PrimeTester.IsPrimeCp(m, ct))
|
||||
{
|
||||
dict[m] = 1;
|
||||
continue;
|
||||
|
||||
@@ -516,7 +516,7 @@ internal static partial class PrimeOrderCalculator
|
||||
return false;
|
||||
}
|
||||
|
||||
// if (!PrimeTester.IsPrimeInternal(orderFactors.Cofactor, CancellationToken.None))
|
||||
// if (!PrimeTester.IsPrimeCp(orderFactors.Cofactor, CancellationToken.None))
|
||||
if (!orderFactors.CofactorIsPrime)
|
||||
{
|
||||
return false;
|
||||
@@ -1123,8 +1123,8 @@ internal static partial class PrimeOrderCalculator
|
||||
// }
|
||||
|
||||
// bool isPrime = primeTester.HeuristicIsPrimeGpu(composite);
|
||||
// bool isPrime = PrimeTester.IsPrimeInternal(composite, CancellationToken.None);
|
||||
bool isPrime = PrimeTester.IsPrimeInternal(composite, CancellationToken.None);
|
||||
// bool isPrime = PrimeTester.IsPrimeCp(composite, CancellationToken.None);
|
||||
bool isPrime = PrimeTester.IsPrimeCp(composite, CancellationToken.None);
|
||||
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite);
|
||||
|
||||
if (isPrime)
|
||||
@@ -1186,8 +1186,8 @@ internal static partial class PrimeOrderCalculator
|
||||
if (!entry.HasKnownPrimality)
|
||||
{
|
||||
// bool isPrime = primeTester.HeuristicIsPrimeGpu(composite);
|
||||
// bool isPrime = PrimeTester.IsPrimeInternal(composite, CancellationToken.None);
|
||||
bool isPrime = PrimeTester.IsPrimeInternal(composite, CancellationToken.None);
|
||||
// bool isPrime = PrimeTester.IsPrimeCp(composite, CancellationToken.None);
|
||||
bool isPrime = PrimeTester.IsPrimeCp(composite, CancellationToken.None);
|
||||
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite);
|
||||
|
||||
entry = entry.WithPrimality(isPrime);
|
||||
@@ -1219,8 +1219,8 @@ internal static partial class PrimeOrderCalculator
|
||||
else
|
||||
{
|
||||
// cofactorIsPrime = primeTester.HeuristicIsPrimeGpu(cofactor);
|
||||
// cofactorIsPrime = PrimeTester.IsPrimeInternal(cofactor, CancellationToken.None);
|
||||
cofactorIsPrime = PrimeTester.IsPrimeInternal(cofactor, CancellationToken.None);
|
||||
// cofactorIsPrime = PrimeTester.IsPrimeCp(cofactor, CancellationToken.None);
|
||||
cofactorIsPrime = PrimeTester.IsPrimeCp(cofactor, CancellationToken.None);
|
||||
// cofactorIsPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(cofactor);
|
||||
}
|
||||
|
||||
@@ -1752,8 +1752,8 @@ internal static partial class PrimeOrderCalculator
|
||||
PrimeTester primeTester = PrimeTester.Exclusive;
|
||||
|
||||
// bool isPrime = primeTester.HeuristicIsPrimeGpu(value);
|
||||
// bool isPrime = PrimeTester.IsPrimeInternal(value, CancellationToken.None);
|
||||
bool isPrime = PrimeTester.IsPrimeInternal(value, CancellationToken.None);
|
||||
// bool isPrime = PrimeTester.IsPrimeCp(value, CancellationToken.None);
|
||||
bool isPrime = PrimeTester.IsPrimeCp(value, CancellationToken.None);
|
||||
// bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(value);
|
||||
if (!knownComposite && isPrime)
|
||||
{
|
||||
@@ -1765,8 +1765,8 @@ internal static partial class PrimeOrderCalculator
|
||||
ulong quotient = value / factor;
|
||||
|
||||
// isPrime = primeTester.HeuristicIsPrimeGpu(factor);
|
||||
// isPrime = PrimeTester.IsPrimeInternal(factor, CancellationToken.None);
|
||||
isPrime = PrimeTester.IsPrimeInternal(factor, CancellationToken.None);
|
||||
// isPrime = PrimeTester.IsPrimeCp(factor, CancellationToken.None);
|
||||
isPrime = PrimeTester.IsPrimeCp(factor, CancellationToken.None);
|
||||
// isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(factor);
|
||||
if (isPrime)
|
||||
{
|
||||
@@ -1797,8 +1797,8 @@ internal static partial class PrimeOrderCalculator
|
||||
else
|
||||
{
|
||||
// isPrime = primeTester.HeuristicIsPrimeGpu(quotient);
|
||||
// isPrime = PrimeTester.IsPrimeInternal(quotient, CancellationToken.None);
|
||||
isPrime = PrimeTester.IsPrimeInternal(quotient, CancellationToken.None);
|
||||
// isPrime = PrimeTester.IsPrimeCp(quotient, CancellationToken.None);
|
||||
isPrime = PrimeTester.IsPrimeCp(quotient, CancellationToken.None);
|
||||
// isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(quotient);
|
||||
}
|
||||
|
||||
|
||||
@@ -955,8 +955,8 @@ internal static partial class PrimeOrderCalculator
|
||||
{
|
||||
if (value <= ulong.MaxValue)
|
||||
{
|
||||
// return HeuristicPrimeTester.Exclusive.IsPrime((ulong)value, CancellationToken.None);
|
||||
return PrimeTester.IsPrimeInternal((ulong)value, CancellationToken.None);
|
||||
// return HeuristicPrimeTester.Exclusive.IsPrimeCpu((ulong)value, CancellationToken.None);
|
||||
return PrimeTester.IsPrimeCp((ulong)value, CancellationToken.None);
|
||||
}
|
||||
|
||||
if ((value & UInt128.One) == UInt128.Zero)
|
||||
|
||||
@@ -24,29 +24,27 @@ public sealed class PrimeTester
|
||||
|
||||
public bool IsPrime(ulong n, CancellationToken ct)
|
||||
{
|
||||
// EvenPerfectBitScanner feeds monotonically increasing odd exponents that start well above the
|
||||
// small-prime cutoffs and already exclude multiples of five. These guards remain for targeted
|
||||
// tests and diagnostics so ad-hoc callers can still probe small or even values when needed.
|
||||
if (n <= 1UL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// bool isTwo = n == 2UL;
|
||||
if (n == 2UL)
|
||||
{
|
||||
throw new InvalidOperationException("PrimeTester.IsPrime encountered the sentinel input 2.");
|
||||
}
|
||||
|
||||
bool isOdd = (n & 1UL) != 0UL;
|
||||
// ulong mod5 = n % 5UL;
|
||||
// bool divisibleByFive = n > 5UL && mod5 == 0UL;
|
||||
bool result = isOdd;
|
||||
|
||||
bool result = /*n >= 2UL && (isTwo || */ (n == 2UL || isOdd) /*) && !divisibleByFive*/;
|
||||
|
||||
// Production scans always fall through to trial division because n is already > 5 by the time this
|
||||
// path executes. Keep the guard so synthetic callers that hammer tiny inputs can skip the loop.
|
||||
bool requiresTrialDivision = result /*&& n >= 7UL && !isTwo*/ && n >= 7UL;
|
||||
bool requiresTrialDivision = result && n >= 7UL;
|
||||
|
||||
if (requiresTrialDivision)
|
||||
{
|
||||
bool sharesMaxExponentFactor = n.Mod10() == 1UL && SharesFactorWithMaxExponent(n);
|
||||
result &= !sharesMaxExponentFactor;
|
||||
// EvenPerfectBitScanner streams exponents starting at 136,279,841, so the Mod10/GCD guard never fires on the
|
||||
// production path. Leave the logic commented out as instrumentation for diagnostic builds.
|
||||
// bool sharesMaxExponentFactor = n.Mod10() == 1UL && SharesFactorWithMaxExponent(n);
|
||||
// result &= !sharesMaxExponentFactor;
|
||||
|
||||
if (result)
|
||||
{
|
||||
@@ -95,10 +93,10 @@ public sealed class PrimeTester
|
||||
bool gpuReportedPrime = !forceCpu && !belowGpuRange && outFlags[0] != 0;
|
||||
bool requiresCpuFallback = forceCpu || belowGpuRange || !gpuReportedPrime;
|
||||
|
||||
return requiresCpuFallback ? IsPrimeInternal(n, ct) : true;
|
||||
return requiresCpuFallback ? IsPrimeCp(n, ct) : true;
|
||||
}
|
||||
|
||||
public static bool IsPrimeInternal(ulong n, CancellationToken ct)
|
||||
public static bool IsPrimeCp(ulong n, CancellationToken ct)
|
||||
{
|
||||
// Preserve the legacy entry point for callers that bypass the thread-local caches.
|
||||
return Exclusive.IsPrime(n, ct);
|
||||
|
||||
Reference in new issue
Block a user