Align heuristic prime testers with squared limits
This commit is contained in:
1 parent
5536dc4865
commit
f7a0627ce5
4 files changed
+77
-107
No files matched your search
@@ -19,7 +19,7 @@ public class HeuristicCombinedPrimeTesterTests
|
|||||||
Span<HeuristicCombinedPrimeTester.HeuristicGroupBSequenceState> groupBState =
|
Span<HeuristicCombinedPrimeTester.HeuristicGroupBSequenceState> groupBState =
|
||||||
stackalloc HeuristicCombinedPrimeTester.HeuristicGroupBSequenceState[4];
|
stackalloc HeuristicCombinedPrimeTester.HeuristicGroupBSequenceState[4];
|
||||||
|
|
||||||
var enumerator = HeuristicCombinedPrimeTester.CreateHeuristicDivisorEnumerator(500, lastDigit, groupBState);
|
var enumerator = HeuristicCombinedPrimeTester.CreateHeuristicDivisorEnumerator(500UL * 500UL, lastDigit, groupBState);
|
||||||
var results = new List<HeuristicCombinedPrimeTester.HeuristicDivisorCandidate>(32);
|
var results = new List<HeuristicCombinedPrimeTester.HeuristicDivisorCandidate>(32);
|
||||||
|
|
||||||
while (results.Count < 20 && enumerator.TryGetNext(out var candidate))
|
while (results.Count < 20 && enumerator.TryGetNext(out var candidate))
|
||||||
@@ -230,14 +230,10 @@ public class HeuristicCombinedPrimeTesterTests
|
|||||||
[InlineData(18_446_744_073_709_551_615UL)]
|
[InlineData(18_446_744_073_709_551_615UL)]
|
||||||
[InlineData(18_446_744_073_709_551_614UL)]
|
[InlineData(18_446_744_073_709_551_614UL)]
|
||||||
[InlineData(18_446_744_073_709_551_613UL)]
|
[InlineData(18_446_744_073_709_551_613UL)]
|
||||||
public void ComputeHeuristicSqrt_returns_floor_square_root(ulong value)
|
public void ComputeHeuristicDivisorSquareLimit_returns_input_value(ulong value)
|
||||||
{
|
{
|
||||||
ulong sqrt = HeuristicCombinedPrimeTester.ComputeHeuristicSqrt(value);
|
ulong limit = HeuristicCombinedPrimeTester.ComputeHeuristicDivisorSquareLimit(value);
|
||||||
|
|
||||||
bool squareBelowOrEqual = (UInt128)sqrt * sqrt <= (UInt128)value;
|
limit.Should().Be(value);
|
||||||
bool nextSquareAbove = (UInt128)(sqrt + 1UL) * (UInt128)(sqrt + 1UL) > (UInt128)value;
|
|
||||||
|
|
||||||
squareBelowOrEqual.Should().BeTrue();
|
|
||||||
nextSquareAbove.Should().BeTrue();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,36 +152,36 @@ public sealed class HeuristicCombinedPrimeTester
|
|||||||
public static bool IsPrimeCpu(ulong n)
|
public static bool IsPrimeCpu(ulong n)
|
||||||
{
|
{
|
||||||
byte nMod10 = (byte)n.Mod10();
|
byte nMod10 = (byte)n.Mod10();
|
||||||
ulong sqrtLimit = ComputeHeuristicSqrt(n);
|
ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n);
|
||||||
|
|
||||||
if (sqrtLimit < 3UL)
|
if (maxDivisorSquare < 9UL)
|
||||||
{
|
{
|
||||||
return EvaluateWithOpenNumericFallback(n);
|
return EvaluateWithOpenNumericFallback(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
return HeuristicTrialDivisionCpu(n, sqrtLimit, nMod10);
|
return HeuristicTrialDivisionCpu(n, maxDivisorSquare, nMod10);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsPrimeGpu(ulong n)
|
public static bool IsPrimeGpu(ulong n)
|
||||||
{
|
{
|
||||||
byte nMod10 = (byte)n.Mod10();
|
byte nMod10 = (byte)n.Mod10();
|
||||||
ulong sqrtLimit = ComputeHeuristicSqrt(n);
|
ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n);
|
||||||
return IsPrimeGpu(n, sqrtLimit, nMod10);
|
return IsPrimeGpu(n, maxDivisorSquare, nMod10);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsPrimeGpu(ulong n, ulong sqrtLimit, byte nMod10)
|
public static bool IsPrimeGpu(ulong n, ulong maxDivisorSquare, byte nMod10)
|
||||||
{
|
{
|
||||||
// TODO: Is this condition ever met on the execution path in EvenPerfectBitScanner?
|
// TODO: Is this condition ever met on the execution path in EvenPerfectBitScanner?
|
||||||
if (sqrtLimit < 3UL)
|
if (maxDivisorSquare < 9UL)
|
||||||
{
|
{
|
||||||
return EvaluateWithOpenNumericFallback(n);
|
return EvaluateWithOpenNumericFallback(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool compositeDetected = HeuristicTrialDivisionGpuDetectsDivisor(n, sqrtLimit, nMod10);
|
bool compositeDetected = HeuristicTrialDivisionGpuDetectsDivisor(n, maxDivisorSquare, nMod10);
|
||||||
return !compositeDetected;
|
return !compositeDetected;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool HeuristicTrialDivisionCpu(ulong n, ulong sqrtLimit, byte nMod10)
|
private static bool HeuristicTrialDivisionCpu(ulong n, ulong maxDivisorSquare, byte nMod10)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<uint> combinedDivisors = GetCombinedDivisors(nMod10);
|
ReadOnlySpan<uint> combinedDivisors = GetCombinedDivisors(nMod10);
|
||||||
ReadOnlySpan<ulong> combinedDivisorSquares = GetCombinedDivisorSquares(nMod10);
|
ReadOnlySpan<ulong> combinedDivisorSquares = GetCombinedDivisorSquares(nMod10);
|
||||||
@@ -191,7 +191,7 @@ public sealed class HeuristicCombinedPrimeTester
|
|||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
ulong divisorSquare = combinedDivisorSquares[i];
|
ulong divisorSquare = combinedDivisorSquares[i];
|
||||||
if (divisorSquare > n)
|
if (divisorSquare > maxDivisorSquare)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -205,7 +205,7 @@ public sealed class HeuristicCombinedPrimeTester
|
|||||||
return EvaluateWithOpenNumericFallback(n);
|
return EvaluateWithOpenNumericFallback(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong sqrtLimit, byte nMod10)
|
private static bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong maxDivisorSquare, byte nMod10)
|
||||||
{
|
{
|
||||||
int batchCapacity = HeuristicGpuDivisorBatchSize;
|
int batchCapacity = HeuristicGpuDivisorBatchSize;
|
||||||
var divisorPool = ThreadStaticPools.UlongPool;
|
var divisorPool = ThreadStaticPools.UlongPool;
|
||||||
@@ -243,7 +243,7 @@ public sealed class HeuristicCombinedPrimeTester
|
|||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
ulong divisorSquare = combinedDivisorSquares[i];
|
ulong divisorSquare = combinedDivisorSquares[i];
|
||||||
if (divisorSquare > n)
|
if (divisorSquare > maxDivisorSquare)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -282,28 +282,9 @@ Cleanup:
|
|||||||
{
|
{
|
||||||
return Prime.Numbers.IsPrime(n);
|
return Prime.Numbers.IsPrime(n);
|
||||||
}
|
}
|
||||||
|
internal static ulong ComputeHeuristicDivisorSquareLimit(ulong n)
|
||||||
internal static ulong ComputeHeuristicSqrt(ulong n)
|
|
||||||
{
|
{
|
||||||
ulong sqrt = (ulong)Math.Sqrt(n);
|
return n;
|
||||||
UInt128 square = (UInt128)sqrt * sqrt;
|
|
||||||
|
|
||||||
while (square > n)
|
|
||||||
{
|
|
||||||
sqrt--;
|
|
||||||
square = (UInt128)sqrt * sqrt;
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong next = sqrt + 1UL;
|
|
||||||
UInt128 nextSquare = (UInt128)next * next;
|
|
||||||
while (nextSquare <= n)
|
|
||||||
{
|
|
||||||
sqrt = next;
|
|
||||||
next++;
|
|
||||||
nextSquare = (UInt128)next * next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sqrt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ReadOnlySpan<uint> GetCombinedDivisors(byte nMod10)
|
private static ReadOnlySpan<uint> GetCombinedDivisors(byte nMod10)
|
||||||
@@ -514,9 +495,9 @@ Cleanup:
|
|||||||
};
|
};
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal static HeuristicDivisorEnumerator CreateHeuristicDivisorEnumerator(ulong sqrtLimit, byte nMod10, Span<HeuristicGroupBSequenceState> groupBBuffer)
|
internal static HeuristicDivisorEnumerator CreateHeuristicDivisorEnumerator(ulong maxDivisorSquare, byte nMod10, Span<HeuristicGroupBSequenceState> groupBBuffer)
|
||||||
{
|
{
|
||||||
return new HeuristicDivisorEnumerator(sqrtLimit, nMod10, groupBBuffer);
|
return new HeuristicDivisorEnumerator(maxDivisorSquare, nMod10, groupBBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal struct HeuristicGroupBSequenceState
|
internal struct HeuristicGroupBSequenceState
|
||||||
@@ -598,15 +579,17 @@ Cleanup:
|
|||||||
|
|
||||||
internal ref struct HeuristicDivisorEnumerator
|
internal ref struct HeuristicDivisorEnumerator
|
||||||
{
|
{
|
||||||
private readonly ulong sqrtLimit;
|
private readonly ulong maxDivisorSquare;
|
||||||
private readonly ReadOnlySpan<uint> combinedDivisors;
|
private readonly ReadOnlySpan<uint> combinedDivisors;
|
||||||
|
private readonly ReadOnlySpan<ulong> combinedDivisorSquares;
|
||||||
private int index;
|
private int index;
|
||||||
|
|
||||||
public HeuristicDivisorEnumerator(ulong sqrtLimit, byte nMod10, Span<HeuristicGroupBSequenceState> groupBBuffer)
|
public HeuristicDivisorEnumerator(ulong maxDivisorSquare, byte nMod10, Span<HeuristicGroupBSequenceState> groupBBuffer)
|
||||||
{
|
{
|
||||||
this.sqrtLimit = sqrtLimit;
|
this.maxDivisorSquare = maxDivisorSquare;
|
||||||
_ = groupBBuffer;
|
_ = groupBBuffer;
|
||||||
combinedDivisors = GetCombinedDivisors(nMod10);
|
combinedDivisors = GetCombinedDivisors(nMod10);
|
||||||
|
combinedDivisorSquares = GetCombinedDivisorSquares(nMod10);
|
||||||
index = 0;
|
index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -614,13 +597,17 @@ Cleanup:
|
|||||||
{
|
{
|
||||||
while (index < combinedDivisors.Length)
|
while (index < combinedDivisors.Length)
|
||||||
{
|
{
|
||||||
uint entry = combinedDivisors[index++];
|
int currentIndex = index;
|
||||||
if (entry > sqrtLimit)
|
ulong divisorSquare = combinedDivisorSquares[currentIndex];
|
||||||
|
if (divisorSquare > maxDivisorSquare)
|
||||||
{
|
{
|
||||||
index = combinedDivisors.Length;
|
index = combinedDivisors.Length;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint entry = combinedDivisors[currentIndex];
|
||||||
|
index = currentIndex + 1;
|
||||||
|
|
||||||
HeuristicDivisorGroup group = ResolveGroup(entry);
|
HeuristicDivisorGroup group = ResolveGroup(entry);
|
||||||
ulong value = entry;
|
ulong value = entry;
|
||||||
byte ending = (byte)(value % 10UL);
|
byte ending = (byte)(value % 10UL);
|
||||||
|
|||||||
@@ -86,15 +86,15 @@ public sealed class HeuristicPrimeTester
|
|||||||
ct.ThrowIfCancellationRequested();
|
ct.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
byte nMod10 = (byte)n.Mod10();
|
byte nMod10 = (byte)n.Mod10();
|
||||||
ulong sqrtLimit = ComputeHeuristicSqrt(n);
|
ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n);
|
||||||
|
|
||||||
if (sqrtLimit < 3UL)
|
if (maxDivisorSquare < 9UL)
|
||||||
{
|
{
|
||||||
return EvaluateWithOpenNumericFallback(n);
|
return EvaluateWithOpenNumericFallback(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool includeGroupB = UseHeuristicGroupBTrialDivision;
|
bool includeGroupB = UseHeuristicGroupBTrialDivision;
|
||||||
return HeuristicTrialDivisionCpu(n, sqrtLimit, nMod10, includeGroupB);
|
return HeuristicTrialDivisionCpu(n, maxDivisorSquare, nMod10, includeGroupB);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsPrimeCpu(ulong n)
|
public bool IsPrimeCpu(ulong n)
|
||||||
@@ -105,33 +105,33 @@ public sealed class HeuristicPrimeTester
|
|||||||
public bool IsPrimeGpu(ulong n)
|
public bool IsPrimeGpu(ulong n)
|
||||||
{
|
{
|
||||||
byte nMod10 = (byte)n.Mod10();
|
byte nMod10 = (byte)n.Mod10();
|
||||||
ulong sqrtLimit = ComputeHeuristicSqrt(n);
|
ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n);
|
||||||
return IsPrimeGpu(n, sqrtLimit, nMod10);
|
return IsPrimeGpu(n, maxDivisorSquare, nMod10);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsPrimeGpu(ulong n, ulong sqrtLimit, byte nMod10)
|
public bool IsPrimeGpu(ulong n, ulong maxDivisorSquare, byte nMod10)
|
||||||
{
|
{
|
||||||
return HeuristicIsPrimeGpuCore(n, sqrtLimit, nMod10);
|
return HeuristicIsPrimeGpuCore(n, maxDivisorSquare, nMod10);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool HeuristicIsPrimeGpuCore(ulong n, ulong sqrtLimit, byte nMod10)
|
private bool HeuristicIsPrimeGpuCore(ulong n, ulong maxDivisorSquare, byte nMod10)
|
||||||
{
|
{
|
||||||
// TODO: Is this condition ever met on the execution path in EvenPerfectBitScanner?
|
// TODO: Is this condition ever met on the execution path in EvenPerfectBitScanner?
|
||||||
if (sqrtLimit < 3UL)
|
if (maxDivisorSquare < 9UL)
|
||||||
{
|
{
|
||||||
return EvaluateWithOpenNumericFallback(n);
|
return EvaluateWithOpenNumericFallback(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (!UseHeuristicGroupBTrialDivision)
|
// if (!UseHeuristicGroupBTrialDivision)
|
||||||
// {
|
// {
|
||||||
// return HeuristicTrialDivisionCpu(n, sqrtLimit, nMod10, includeGroupB: false);
|
// return HeuristicTrialDivisionCpu(n, maxDivisorSquare, nMod10, includeGroupB: false);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
bool compositeDetected = HeuristicTrialDivisionGpuDetectsDivisor(n, sqrtLimit, nMod10);
|
bool compositeDetected = HeuristicTrialDivisionGpuDetectsDivisor(n, maxDivisorSquare, nMod10);
|
||||||
return !compositeDetected;
|
return !compositeDetected;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool HeuristicTrialDivisionCpu(ulong n, ulong sqrtLimit, byte nMod10, bool includeGroupB)
|
private static bool HeuristicTrialDivisionCpu(ulong n, ulong maxDivisorSquare, byte nMod10, bool includeGroupB)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<int> groupADivisors = HeuristicPrimeSieves.GroupADivisors;
|
ReadOnlySpan<int> groupADivisors = HeuristicPrimeSieves.GroupADivisors;
|
||||||
int interleaveBatchSize = Math.Max(1, HeuristicDivisorInterleaveBatchSize);
|
int interleaveBatchSize = Math.Max(1, HeuristicDivisorInterleaveBatchSize);
|
||||||
@@ -145,7 +145,7 @@ public sealed class HeuristicPrimeTester
|
|||||||
while (processed < interleaveBatchSize && groupAIndex < groupADivisors.Length)
|
while (processed < interleaveBatchSize && groupAIndex < groupADivisors.Length)
|
||||||
{
|
{
|
||||||
ulong divisor = (ulong)groupADivisors[groupAIndex];
|
ulong divisor = (ulong)groupADivisors[groupAIndex];
|
||||||
if (divisor > sqrtLimit)
|
if (!DivisorWithinSquareLimit(divisor, maxDivisorSquare))
|
||||||
{
|
{
|
||||||
groupAIndex = groupADivisors.Length;
|
groupAIndex = groupADivisors.Length;
|
||||||
break;
|
break;
|
||||||
@@ -165,7 +165,7 @@ public sealed class HeuristicPrimeTester
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ulong)groupADivisors[groupAIndex] > sqrtLimit)
|
if (!DivisorWithinSquareLimit((ulong)groupADivisors[groupAIndex], maxDivisorSquare))
|
||||||
{
|
{
|
||||||
groupAIndex = groupADivisors.Length;
|
groupAIndex = groupADivisors.Length;
|
||||||
break;
|
break;
|
||||||
@@ -185,8 +185,8 @@ public sealed class HeuristicPrimeTester
|
|||||||
? stackalloc int[endingOrder.Length]
|
? stackalloc int[endingOrder.Length]
|
||||||
: new int[endingOrder.Length];
|
: new int[endingOrder.Length];
|
||||||
|
|
||||||
bool groupAHasMore = groupAIndex < groupADivisors.Length && (ulong)groupADivisors[groupAIndex] <= sqrtLimit;
|
bool groupAHasMore = groupAIndex < groupADivisors.Length && DivisorWithinSquareLimit((ulong)groupADivisors[groupAIndex], maxDivisorSquare);
|
||||||
bool groupBHasMore = HasGroupBCandidates(endingOrder, indices, sqrtLimit);
|
bool groupBHasMore = HasGroupBCandidates(endingOrder, indices, maxDivisorSquare);
|
||||||
|
|
||||||
while (groupAHasMore || groupBHasMore)
|
while (groupAHasMore || groupBHasMore)
|
||||||
{
|
{
|
||||||
@@ -196,7 +196,7 @@ public sealed class HeuristicPrimeTester
|
|||||||
while (processed < interleaveBatchSize && groupAIndex < groupADivisors.Length)
|
while (processed < interleaveBatchSize && groupAIndex < groupADivisors.Length)
|
||||||
{
|
{
|
||||||
ulong divisor = (ulong)groupADivisors[groupAIndex];
|
ulong divisor = (ulong)groupADivisors[groupAIndex];
|
||||||
if (divisor > sqrtLimit)
|
if (!DivisorWithinSquareLimit(divisor, maxDivisorSquare))
|
||||||
{
|
{
|
||||||
groupAIndex = groupADivisors.Length;
|
groupAIndex = groupADivisors.Length;
|
||||||
groupAHasMore = false;
|
groupAHasMore = false;
|
||||||
@@ -218,7 +218,7 @@ public sealed class HeuristicPrimeTester
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
groupAHasMore = (ulong)groupADivisors[groupAIndex] <= sqrtLimit;
|
groupAHasMore = DivisorWithinSquareLimit((ulong)groupADivisors[groupAIndex], maxDivisorSquare);
|
||||||
if (!groupAHasMore)
|
if (!groupAHasMore)
|
||||||
{
|
{
|
||||||
groupAIndex = groupADivisors.Length;
|
groupAIndex = groupADivisors.Length;
|
||||||
@@ -231,7 +231,7 @@ public sealed class HeuristicPrimeTester
|
|||||||
int processed = 0;
|
int processed = 0;
|
||||||
while (processed < interleaveBatchSize)
|
while (processed < interleaveBatchSize)
|
||||||
{
|
{
|
||||||
if (!TrySelectNextGroupBDivisor(endingOrder, indices, sqrtLimit, out ulong divisor))
|
if (!TrySelectNextGroupBDivisor(endingOrder, indices, maxDivisorSquare, out ulong divisor))
|
||||||
{
|
{
|
||||||
groupBHasMore = false;
|
groupBHasMore = false;
|
||||||
break;
|
break;
|
||||||
@@ -247,7 +247,7 @@ public sealed class HeuristicPrimeTester
|
|||||||
|
|
||||||
if (groupBHasMore)
|
if (groupBHasMore)
|
||||||
{
|
{
|
||||||
groupBHasMore = HasGroupBCandidates(endingOrder, indices, sqrtLimit);
|
groupBHasMore = HasGroupBCandidates(endingOrder, indices, maxDivisorSquare);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,7 +258,7 @@ public sealed class HeuristicPrimeTester
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
private bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong sqrtLimit, byte nMod10)
|
private bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong maxDivisorSquare, byte nMod10)
|
||||||
{
|
{
|
||||||
int batchCapacity = HeuristicGpuDivisorBatchSize;
|
int batchCapacity = HeuristicGpuDivisorBatchSize;
|
||||||
int interleaveBatchSize = HeuristicDivisorInterleaveBatchSize;
|
int interleaveBatchSize = HeuristicDivisorInterleaveBatchSize;
|
||||||
@@ -296,8 +296,8 @@ public sealed class HeuristicPrimeTester
|
|||||||
? stackalloc int[endingOrder.Length]
|
? stackalloc int[endingOrder.Length]
|
||||||
: new int[endingOrder.Length];
|
: new int[endingOrder.Length];
|
||||||
|
|
||||||
bool groupAHasMore = groupAIndex < groupADivisors.Length && (ulong)groupADivisors[groupAIndex] <= sqrtLimit;
|
bool groupAHasMore = groupAIndex < groupADivisors.Length && DivisorWithinSquareLimit((ulong)groupADivisors[groupAIndex], maxDivisorSquare);
|
||||||
bool groupBHasMore = !endingOrder.IsEmpty && HasGroupBCandidates(endingOrder, indices, sqrtLimit);
|
bool groupBHasMore = !endingOrder.IsEmpty && HasGroupBCandidates(endingOrder, indices, maxDivisorSquare);
|
||||||
|
|
||||||
while (!compositeDetected && (groupAHasMore || groupBHasMore))
|
while (!compositeDetected && (groupAHasMore || groupBHasMore))
|
||||||
{
|
{
|
||||||
@@ -307,7 +307,7 @@ public sealed class HeuristicPrimeTester
|
|||||||
while (processed < interleaveBatchSize && groupAIndex < groupADivisors.Length)
|
while (processed < interleaveBatchSize && groupAIndex < groupADivisors.Length)
|
||||||
{
|
{
|
||||||
ulong divisor = (ulong)groupADivisors[groupAIndex];
|
ulong divisor = (ulong)groupADivisors[groupAIndex];
|
||||||
if (divisor > sqrtLimit)
|
if (!DivisorWithinSquareLimit(divisor, maxDivisorSquare))
|
||||||
{
|
{
|
||||||
groupAIndex = groupADivisors.Length;
|
groupAIndex = groupADivisors.Length;
|
||||||
groupAHasMore = false;
|
groupAHasMore = false;
|
||||||
@@ -338,7 +338,7 @@ public sealed class HeuristicPrimeTester
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
groupAHasMore = (ulong)groupADivisors[groupAIndex] <= sqrtLimit;
|
groupAHasMore = DivisorWithinSquareLimit((ulong)groupADivisors[groupAIndex], maxDivisorSquare);
|
||||||
if (!groupAHasMore)
|
if (!groupAHasMore)
|
||||||
{
|
{
|
||||||
groupAIndex = groupADivisors.Length;
|
groupAIndex = groupADivisors.Length;
|
||||||
@@ -351,7 +351,7 @@ public sealed class HeuristicPrimeTester
|
|||||||
int processed = 0;
|
int processed = 0;
|
||||||
while (processed < interleaveBatchSize)
|
while (processed < interleaveBatchSize)
|
||||||
{
|
{
|
||||||
if (!TrySelectNextGroupBDivisor(endingOrder, indices, sqrtLimit, out ulong divisor))
|
if (!TrySelectNextGroupBDivisor(endingOrder, indices, maxDivisorSquare, out ulong divisor))
|
||||||
{
|
{
|
||||||
groupBHasMore = false;
|
groupBHasMore = false;
|
||||||
break;
|
break;
|
||||||
@@ -376,7 +376,7 @@ public sealed class HeuristicPrimeTester
|
|||||||
|
|
||||||
if (groupBHasMore)
|
if (groupBHasMore)
|
||||||
{
|
{
|
||||||
groupBHasMore = HasGroupBCandidates(endingOrder, indices, sqrtLimit);
|
groupBHasMore = HasGroupBCandidates(endingOrder, indices, maxDivisorSquare);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -399,28 +399,15 @@ Cleanup:
|
|||||||
{
|
{
|
||||||
return Prime.Numbers.IsPrime(n);
|
return Prime.Numbers.IsPrime(n);
|
||||||
}
|
}
|
||||||
|
internal static ulong ComputeHeuristicDivisorSquareLimit(ulong n)
|
||||||
internal static ulong ComputeHeuristicSqrt(ulong n)
|
|
||||||
{
|
{
|
||||||
ulong sqrt = (ulong)Math.Sqrt(n);
|
return n;
|
||||||
UInt128 square = (UInt128)sqrt * sqrt;
|
|
||||||
|
|
||||||
while (square > n)
|
|
||||||
{
|
|
||||||
sqrt--;
|
|
||||||
square = (UInt128)sqrt * sqrt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong next = sqrt + 1UL;
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
UInt128 nextSquare = (UInt128)next * next;
|
private static bool DivisorWithinSquareLimit(ulong divisor, ulong maxDivisorSquare)
|
||||||
while (nextSquare <= n)
|
|
||||||
{
|
{
|
||||||
sqrt = next;
|
return divisor != 0UL && divisor <= maxDivisorSquare / divisor;
|
||||||
next++;
|
|
||||||
nextSquare = (UInt128)next * next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sqrt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> GetGroupBEndingOrder(byte nMod10) => nMod10 switch
|
private static ReadOnlySpan<byte> GetGroupBEndingOrder(byte nMod10) => nMod10 switch
|
||||||
@@ -434,7 +421,7 @@ Cleanup:
|
|||||||
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static bool TrySelectNextGroupBDivisor(ReadOnlySpan<byte> endingOrder, Span<int> indices, ulong sqrtLimit, out ulong divisor)
|
private static bool TrySelectNextGroupBDivisor(ReadOnlySpan<byte> endingOrder, Span<int> indices, ulong maxDivisorSquare, out ulong divisor)
|
||||||
{
|
{
|
||||||
ulong bestCandidate = ulong.MaxValue;
|
ulong bestCandidate = ulong.MaxValue;
|
||||||
int bestEndingIndex = -1;
|
int bestEndingIndex = -1;
|
||||||
@@ -449,7 +436,7 @@ Cleanup:
|
|||||||
}
|
}
|
||||||
|
|
||||||
ulong candidate = (ulong)divisors[index];
|
ulong candidate = (ulong)divisors[index];
|
||||||
if (candidate > sqrtLimit)
|
if (!DivisorWithinSquareLimit(candidate, maxDivisorSquare))
|
||||||
{
|
{
|
||||||
indices[i] = divisors.Length;
|
indices[i] = divisors.Length;
|
||||||
continue;
|
continue;
|
||||||
@@ -474,7 +461,7 @@ Cleanup:
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static bool HasGroupBCandidates(ReadOnlySpan<byte> endingOrder, Span<int> indices, ulong sqrtLimit)
|
private static bool HasGroupBCandidates(ReadOnlySpan<byte> endingOrder, Span<int> indices, ulong maxDivisorSquare)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < endingOrder.Length; i++)
|
for (int i = 0; i < endingOrder.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -486,7 +473,7 @@ Cleanup:
|
|||||||
}
|
}
|
||||||
|
|
||||||
ulong candidate = (ulong)divisors[index];
|
ulong candidate = (ulong)divisors[index];
|
||||||
if (candidate > sqrtLimit)
|
if (!DivisorWithinSquareLimit(candidate, maxDivisorSquare))
|
||||||
{
|
{
|
||||||
indices[i] = divisors.Length;
|
indices[i] = divisors.Length;
|
||||||
continue;
|
continue;
|
||||||
@@ -533,9 +520,9 @@ Cleanup:
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal static HeuristicDivisorEnumerator CreateHeuristicDivisorEnumerator(ulong sqrtLimit, byte nMod10, Span<HeuristicGroupBSequenceState> groupBBuffer)
|
internal static HeuristicDivisorEnumerator CreateHeuristicDivisorEnumerator(ulong maxDivisorSquare, byte nMod10, Span<HeuristicGroupBSequenceState> groupBBuffer)
|
||||||
{
|
{
|
||||||
return new HeuristicDivisorEnumerator(sqrtLimit, nMod10, groupBBuffer);
|
return new HeuristicDivisorEnumerator(maxDivisorSquare, nMod10, groupBBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -613,20 +600,20 @@ Cleanup:
|
|||||||
|
|
||||||
internal ref struct HeuristicDivisorEnumerator
|
internal ref struct HeuristicDivisorEnumerator
|
||||||
{
|
{
|
||||||
private readonly ulong sqrtLimit;
|
private readonly ulong maxDivisorSquare;
|
||||||
private int groupAConstantIndex;
|
private int groupAConstantIndex;
|
||||||
private ulong groupAWheelCandidate;
|
private ulong groupAWheelCandidate;
|
||||||
private int groupAIncrementIndex;
|
private int groupAIncrementIndex;
|
||||||
private bool groupAWheelActive;
|
private bool groupAWheelActive;
|
||||||
private Span<HeuristicGroupBSequenceState> groupBStates;
|
private Span<HeuristicGroupBSequenceState> groupBStates;
|
||||||
|
|
||||||
public HeuristicDivisorEnumerator(ulong sqrtLimit, byte nMod10, Span<HeuristicGroupBSequenceState> groupBBuffer)
|
public HeuristicDivisorEnumerator(ulong maxDivisorSquare, byte nMod10, Span<HeuristicGroupBSequenceState> groupBBuffer)
|
||||||
{
|
{
|
||||||
this.sqrtLimit = sqrtLimit;
|
this.maxDivisorSquare = maxDivisorSquare;
|
||||||
groupAConstantIndex = 0;
|
groupAConstantIndex = 0;
|
||||||
groupAWheelCandidate = 23UL;
|
groupAWheelCandidate = 23UL;
|
||||||
groupAIncrementIndex = 0;
|
groupAIncrementIndex = 0;
|
||||||
groupAWheelActive = sqrtLimit >= 23UL;
|
groupAWheelActive = DivisorWithinSquareLimit(23UL, maxDivisorSquare);
|
||||||
|
|
||||||
int count = InitializeGroupBStates(nMod10, groupBBuffer);
|
int count = InitializeGroupBStates(nMod10, groupBBuffer);
|
||||||
groupBStates = count == 0 ? Span<HeuristicGroupBSequenceState>.Empty : groupBBuffer[..count];
|
groupBStates = count == 0 ? Span<HeuristicGroupBSequenceState>.Empty : groupBBuffer[..count];
|
||||||
@@ -643,7 +630,7 @@ Cleanup:
|
|||||||
while (groupAConstantIndex < GroupAConstantDivisors.Length)
|
while (groupAConstantIndex < GroupAConstantDivisors.Length)
|
||||||
{
|
{
|
||||||
ulong value = GroupAConstantDivisors[groupAConstantIndex++];
|
ulong value = GroupAConstantDivisors[groupAConstantIndex++];
|
||||||
if (value > sqrtLimit)
|
if (!DivisorWithinSquareLimit(value, maxDivisorSquare))
|
||||||
{
|
{
|
||||||
groupAConstantIndex = GroupAConstantDivisors.Length;
|
groupAConstantIndex = GroupAConstantDivisors.Length;
|
||||||
break;
|
break;
|
||||||
@@ -657,7 +644,7 @@ Cleanup:
|
|||||||
if (groupAWheelActive)
|
if (groupAWheelActive)
|
||||||
{
|
{
|
||||||
ulong value = groupAWheelCandidate;
|
ulong value = groupAWheelCandidate;
|
||||||
if (value <= sqrtLimit)
|
if (DivisorWithinSquareLimit(value, maxDivisorSquare))
|
||||||
{
|
{
|
||||||
candidate = new HeuristicDivisorCandidate(value, HeuristicDivisorGroup.GroupAWheel, 3, 0, (ushort)(value % Wheel210));
|
candidate = new HeuristicDivisorCandidate(value, HeuristicDivisorGroup.GroupAWheel, 3, 0, (ushort)(value % Wheel210));
|
||||||
byte increment = GroupAIncrementPattern[groupAIncrementIndex];
|
byte increment = GroupAIncrementPattern[groupAIncrementIndex];
|
||||||
@@ -691,7 +678,7 @@ Cleanup:
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value > sqrtLimit)
|
if (!DivisorWithinSquareLimit(value, maxDivisorSquare))
|
||||||
{
|
{
|
||||||
state.Candidate = ulong.MaxValue;
|
state.Candidate = ulong.MaxValue;
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ The plan incorporates the updated divisor-class heuristics (Groups A/B, wheels,
|
|||||||
* [done] The CPU heuristic now operates as a pure trial-division sweep without maintaining per-call summaries, relying on `PrepareHeuristicDivisor` only when downstream consumers request Montgomery data.
|
* [done] The CPU heuristic now operates as a pure trial-division sweep without maintaining per-call summaries, relying on `PrepareHeuristicDivisor` only when downstream consumers request Montgomery data.
|
||||||
* [done] Introduced the temporary `UseHeuristicGroupBTrialDivision` gate so current builds execute Group A locally and then fall back to `Open.Numeric.Primes.Prime.Numbers.IsPrime`, keeping the full Group B implementation available for reactivation.
|
* [done] Introduced the temporary `UseHeuristicGroupBTrialDivision` gate so current builds execute Group A locally and then fall back to `Open.Numeric.Primes.Prime.Numbers.IsPrime`, keeping the full Group B implementation available for reactivation.
|
||||||
7. [done] **Implement `IsPrimeGpu`**
|
7. [done] **Implement `IsPrimeGpu`**
|
||||||
* [done] Mirror CPU structure while batching divisor checks on the accelerator via `HeuristicTrialDivisionGpu`, drawing divisors from `HeuristicPrimeSieves`, reusing caller-provided `sqrtLimit`/`nMod10` inputs, and falling back to CPU when GPUs are disabled.
|
* [done] Mirror CPU structure while batching divisor checks on the accelerator via `HeuristicTrialDivisionGpu`, drawing divisors from `HeuristicPrimeSieves`, reusing caller-provided `maxDivisorSquare`/`nMod10` inputs, and falling back to CPU when GPUs are disabled.
|
||||||
* [done] Added `PrimeTesterKernels.HeuristicTrialDivisionKernel` so GPU batches respect Group A/B ordering while emitting hit flags for early exits.
|
* [done] Added `PrimeTesterKernels.HeuristicTrialDivisionKernel` so 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] 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] 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.
|
||||||
|
|||||||
Reference in new issue
Block a user