Align heuristic prime testers with squared limits
This commit is contained in:
1 parent
5536dc4865
commit
f7a0627ce5
4 files changed
+109
-139
No files matched your search
@@ -19,7 +19,7 @@ public class HeuristicCombinedPrimeTesterTests
|
||||
Span<HeuristicCombinedPrimeTester.HeuristicGroupBSequenceState> groupBState =
|
||||
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);
|
||||
|
||||
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_614UL)]
|
||||
[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;
|
||||
bool nextSquareAbove = (UInt128)(sqrt + 1UL) * (UInt128)(sqrt + 1UL) > (UInt128)value;
|
||||
|
||||
squareBelowOrEqual.Should().BeTrue();
|
||||
nextSquareAbove.Should().BeTrue();
|
||||
limit.Should().Be(value);
|
||||
}
|
||||
}
|
||||
@@ -152,36 +152,36 @@ public sealed class HeuristicCombinedPrimeTester
|
||||
public static bool IsPrimeCpu(ulong n)
|
||||
{
|
||||
byte nMod10 = (byte)n.Mod10();
|
||||
ulong sqrtLimit = ComputeHeuristicSqrt(n);
|
||||
ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n);
|
||||
|
||||
if (sqrtLimit < 3UL)
|
||||
if (maxDivisorSquare < 9UL)
|
||||
{
|
||||
return EvaluateWithOpenNumericFallback(n);
|
||||
}
|
||||
|
||||
return HeuristicTrialDivisionCpu(n, sqrtLimit, nMod10);
|
||||
return HeuristicTrialDivisionCpu(n, maxDivisorSquare, nMod10);
|
||||
}
|
||||
|
||||
public static bool IsPrimeGpu(ulong n)
|
||||
{
|
||||
byte nMod10 = (byte)n.Mod10();
|
||||
ulong sqrtLimit = ComputeHeuristicSqrt(n);
|
||||
return IsPrimeGpu(n, sqrtLimit, nMod10);
|
||||
ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n);
|
||||
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?
|
||||
if (sqrtLimit < 3UL)
|
||||
if (maxDivisorSquare < 9UL)
|
||||
{
|
||||
return EvaluateWithOpenNumericFallback(n);
|
||||
}
|
||||
|
||||
bool compositeDetected = HeuristicTrialDivisionGpuDetectsDivisor(n, sqrtLimit, nMod10);
|
||||
bool compositeDetected = HeuristicTrialDivisionGpuDetectsDivisor(n, maxDivisorSquare, nMod10);
|
||||
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<ulong> combinedDivisorSquares = GetCombinedDivisorSquares(nMod10);
|
||||
@@ -191,7 +191,7 @@ public sealed class HeuristicCombinedPrimeTester
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
ulong divisorSquare = combinedDivisorSquares[i];
|
||||
if (divisorSquare > n)
|
||||
if (divisorSquare > maxDivisorSquare)
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -205,7 +205,7 @@ public sealed class HeuristicCombinedPrimeTester
|
||||
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;
|
||||
var divisorPool = ThreadStaticPools.UlongPool;
|
||||
@@ -243,7 +243,7 @@ public sealed class HeuristicCombinedPrimeTester
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
ulong divisorSquare = combinedDivisorSquares[i];
|
||||
if (divisorSquare > n)
|
||||
if (divisorSquare > maxDivisorSquare)
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -282,29 +282,10 @@ Cleanup:
|
||||
{
|
||||
return Prime.Numbers.IsPrime(n);
|
||||
}
|
||||
|
||||
internal static ulong ComputeHeuristicSqrt(ulong n)
|
||||
{
|
||||
ulong sqrt = (ulong)Math.Sqrt(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;
|
||||
}
|
||||
internal static ulong ComputeHeuristicDivisorSquareLimit(ulong n)
|
||||
{
|
||||
return n;
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<uint> GetCombinedDivisors(byte nMod10)
|
||||
{
|
||||
@@ -514,9 +495,9 @@ Cleanup:
|
||||
};
|
||||
|
||||
[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
|
||||
@@ -597,44 +578,50 @@ Cleanup:
|
||||
}
|
||||
|
||||
internal ref struct HeuristicDivisorEnumerator
|
||||
{
|
||||
private readonly ulong sqrtLimit;
|
||||
private readonly ReadOnlySpan<uint> combinedDivisors;
|
||||
private int index;
|
||||
{
|
||||
private readonly ulong maxDivisorSquare;
|
||||
private readonly ReadOnlySpan<uint> combinedDivisors;
|
||||
private readonly ReadOnlySpan<ulong> combinedDivisorSquares;
|
||||
private int index;
|
||||
|
||||
public HeuristicDivisorEnumerator(ulong sqrtLimit, byte nMod10, Span<HeuristicGroupBSequenceState> groupBBuffer)
|
||||
{
|
||||
this.sqrtLimit = sqrtLimit;
|
||||
_ = groupBBuffer;
|
||||
combinedDivisors = GetCombinedDivisors(nMod10);
|
||||
index = 0;
|
||||
}
|
||||
public HeuristicDivisorEnumerator(ulong maxDivisorSquare, byte nMod10, Span<HeuristicGroupBSequenceState> groupBBuffer)
|
||||
{
|
||||
this.maxDivisorSquare = maxDivisorSquare;
|
||||
_ = groupBBuffer;
|
||||
combinedDivisors = GetCombinedDivisors(nMod10);
|
||||
combinedDivisorSquares = GetCombinedDivisorSquares(nMod10);
|
||||
index = 0;
|
||||
}
|
||||
|
||||
public bool TryGetNext(out HeuristicDivisorCandidate candidate)
|
||||
{
|
||||
while (index < combinedDivisors.Length)
|
||||
{
|
||||
uint entry = combinedDivisors[index++];
|
||||
if (entry > sqrtLimit)
|
||||
{
|
||||
index = combinedDivisors.Length;
|
||||
break;
|
||||
}
|
||||
public bool TryGetNext(out HeuristicDivisorCandidate candidate)
|
||||
{
|
||||
while (index < combinedDivisors.Length)
|
||||
{
|
||||
int currentIndex = index;
|
||||
ulong divisorSquare = combinedDivisorSquares[currentIndex];
|
||||
if (divisorSquare > maxDivisorSquare)
|
||||
{
|
||||
index = combinedDivisors.Length;
|
||||
break;
|
||||
}
|
||||
|
||||
HeuristicDivisorGroup group = ResolveGroup(entry);
|
||||
ulong value = entry;
|
||||
byte ending = (byte)(value % 10UL);
|
||||
ushort residue = (ushort)(value % Wheel210);
|
||||
candidate = new HeuristicDivisorCandidate(value, group, ending, 0, residue);
|
||||
return true;
|
||||
}
|
||||
uint entry = combinedDivisors[currentIndex];
|
||||
index = currentIndex + 1;
|
||||
|
||||
candidate = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
HeuristicDivisorGroup group = ResolveGroup(entry);
|
||||
ulong value = entry;
|
||||
byte ending = (byte)(value % 10UL);
|
||||
ushort residue = (ushort)(value % Wheel210);
|
||||
candidate = new HeuristicDivisorCandidate(value, group, ending, 0, residue);
|
||||
return true;
|
||||
}
|
||||
|
||||
internal ref struct MersenneHeuristicDivisorEnumerator
|
||||
candidate = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal ref struct MersenneHeuristicDivisorEnumerator
|
||||
{
|
||||
private readonly GpuUInt128 step;
|
||||
private readonly GpuUInt128 limit;
|
||||
|
||||
@@ -86,15 +86,15 @@ public sealed class HeuristicPrimeTester
|
||||
ct.ThrowIfCancellationRequested();
|
||||
|
||||
byte nMod10 = (byte)n.Mod10();
|
||||
ulong sqrtLimit = ComputeHeuristicSqrt(n);
|
||||
ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n);
|
||||
|
||||
if (sqrtLimit < 3UL)
|
||||
if (maxDivisorSquare < 9UL)
|
||||
{
|
||||
return EvaluateWithOpenNumericFallback(n);
|
||||
}
|
||||
|
||||
bool includeGroupB = UseHeuristicGroupBTrialDivision;
|
||||
return HeuristicTrialDivisionCpu(n, sqrtLimit, nMod10, includeGroupB);
|
||||
return HeuristicTrialDivisionCpu(n, maxDivisorSquare, nMod10, includeGroupB);
|
||||
}
|
||||
|
||||
public bool IsPrimeCpu(ulong n)
|
||||
@@ -105,33 +105,33 @@ public sealed class HeuristicPrimeTester
|
||||
public bool IsPrimeGpu(ulong n)
|
||||
{
|
||||
byte nMod10 = (byte)n.Mod10();
|
||||
ulong sqrtLimit = ComputeHeuristicSqrt(n);
|
||||
return IsPrimeGpu(n, sqrtLimit, nMod10);
|
||||
ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n);
|
||||
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?
|
||||
if (sqrtLimit < 3UL)
|
||||
if (maxDivisorSquare < 9UL)
|
||||
{
|
||||
return EvaluateWithOpenNumericFallback(n);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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;
|
||||
int interleaveBatchSize = Math.Max(1, HeuristicDivisorInterleaveBatchSize);
|
||||
@@ -145,7 +145,7 @@ public sealed class HeuristicPrimeTester
|
||||
while (processed < interleaveBatchSize && groupAIndex < groupADivisors.Length)
|
||||
{
|
||||
ulong divisor = (ulong)groupADivisors[groupAIndex];
|
||||
if (divisor > sqrtLimit)
|
||||
if (!DivisorWithinSquareLimit(divisor, maxDivisorSquare))
|
||||
{
|
||||
groupAIndex = groupADivisors.Length;
|
||||
break;
|
||||
@@ -165,7 +165,7 @@ public sealed class HeuristicPrimeTester
|
||||
break;
|
||||
}
|
||||
|
||||
if ((ulong)groupADivisors[groupAIndex] > sqrtLimit)
|
||||
if (!DivisorWithinSquareLimit((ulong)groupADivisors[groupAIndex], maxDivisorSquare))
|
||||
{
|
||||
groupAIndex = groupADivisors.Length;
|
||||
break;
|
||||
@@ -185,8 +185,8 @@ public sealed class HeuristicPrimeTester
|
||||
? stackalloc int[endingOrder.Length]
|
||||
: new int[endingOrder.Length];
|
||||
|
||||
bool groupAHasMore = groupAIndex < groupADivisors.Length && (ulong)groupADivisors[groupAIndex] <= sqrtLimit;
|
||||
bool groupBHasMore = HasGroupBCandidates(endingOrder, indices, sqrtLimit);
|
||||
bool groupAHasMore = groupAIndex < groupADivisors.Length && DivisorWithinSquareLimit((ulong)groupADivisors[groupAIndex], maxDivisorSquare);
|
||||
bool groupBHasMore = HasGroupBCandidates(endingOrder, indices, maxDivisorSquare);
|
||||
|
||||
while (groupAHasMore || groupBHasMore)
|
||||
{
|
||||
@@ -196,7 +196,7 @@ public sealed class HeuristicPrimeTester
|
||||
while (processed < interleaveBatchSize && groupAIndex < groupADivisors.Length)
|
||||
{
|
||||
ulong divisor = (ulong)groupADivisors[groupAIndex];
|
||||
if (divisor > sqrtLimit)
|
||||
if (!DivisorWithinSquareLimit(divisor, maxDivisorSquare))
|
||||
{
|
||||
groupAIndex = groupADivisors.Length;
|
||||
groupAHasMore = false;
|
||||
@@ -218,7 +218,7 @@ public sealed class HeuristicPrimeTester
|
||||
}
|
||||
else
|
||||
{
|
||||
groupAHasMore = (ulong)groupADivisors[groupAIndex] <= sqrtLimit;
|
||||
groupAHasMore = DivisorWithinSquareLimit((ulong)groupADivisors[groupAIndex], maxDivisorSquare);
|
||||
if (!groupAHasMore)
|
||||
{
|
||||
groupAIndex = groupADivisors.Length;
|
||||
@@ -231,7 +231,7 @@ public sealed class HeuristicPrimeTester
|
||||
int processed = 0;
|
||||
while (processed < interleaveBatchSize)
|
||||
{
|
||||
if (!TrySelectNextGroupBDivisor(endingOrder, indices, sqrtLimit, out ulong divisor))
|
||||
if (!TrySelectNextGroupBDivisor(endingOrder, indices, maxDivisorSquare, out ulong divisor))
|
||||
{
|
||||
groupBHasMore = false;
|
||||
break;
|
||||
@@ -247,7 +247,7 @@ public sealed class HeuristicPrimeTester
|
||||
|
||||
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 interleaveBatchSize = HeuristicDivisorInterleaveBatchSize;
|
||||
@@ -296,8 +296,8 @@ public sealed class HeuristicPrimeTester
|
||||
? 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);
|
||||
bool groupAHasMore = groupAIndex < groupADivisors.Length && DivisorWithinSquareLimit((ulong)groupADivisors[groupAIndex], maxDivisorSquare);
|
||||
bool groupBHasMore = !endingOrder.IsEmpty && HasGroupBCandidates(endingOrder, indices, maxDivisorSquare);
|
||||
|
||||
while (!compositeDetected && (groupAHasMore || groupBHasMore))
|
||||
{
|
||||
@@ -307,7 +307,7 @@ public sealed class HeuristicPrimeTester
|
||||
while (processed < interleaveBatchSize && groupAIndex < groupADivisors.Length)
|
||||
{
|
||||
ulong divisor = (ulong)groupADivisors[groupAIndex];
|
||||
if (divisor > sqrtLimit)
|
||||
if (!DivisorWithinSquareLimit(divisor, maxDivisorSquare))
|
||||
{
|
||||
groupAIndex = groupADivisors.Length;
|
||||
groupAHasMore = false;
|
||||
@@ -338,7 +338,7 @@ public sealed class HeuristicPrimeTester
|
||||
}
|
||||
else
|
||||
{
|
||||
groupAHasMore = (ulong)groupADivisors[groupAIndex] <= sqrtLimit;
|
||||
groupAHasMore = DivisorWithinSquareLimit((ulong)groupADivisors[groupAIndex], maxDivisorSquare);
|
||||
if (!groupAHasMore)
|
||||
{
|
||||
groupAIndex = groupADivisors.Length;
|
||||
@@ -351,7 +351,7 @@ public sealed class HeuristicPrimeTester
|
||||
int processed = 0;
|
||||
while (processed < interleaveBatchSize)
|
||||
{
|
||||
if (!TrySelectNextGroupBDivisor(endingOrder, indices, sqrtLimit, out ulong divisor))
|
||||
if (!TrySelectNextGroupBDivisor(endingOrder, indices, maxDivisorSquare, out ulong divisor))
|
||||
{
|
||||
groupBHasMore = false;
|
||||
break;
|
||||
@@ -376,7 +376,7 @@ public sealed class HeuristicPrimeTester
|
||||
|
||||
if (groupBHasMore)
|
||||
{
|
||||
groupBHasMore = HasGroupBCandidates(endingOrder, indices, sqrtLimit);
|
||||
groupBHasMore = HasGroupBCandidates(endingOrder, indices, maxDivisorSquare);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -399,28 +399,15 @@ Cleanup:
|
||||
{
|
||||
return Prime.Numbers.IsPrime(n);
|
||||
}
|
||||
|
||||
internal static ulong ComputeHeuristicSqrt(ulong n)
|
||||
internal static ulong ComputeHeuristicDivisorSquareLimit(ulong n)
|
||||
{
|
||||
ulong sqrt = (ulong)Math.Sqrt(n);
|
||||
UInt128 square = (UInt128)sqrt * sqrt;
|
||||
return n;
|
||||
}
|
||||
|
||||
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;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool DivisorWithinSquareLimit(ulong divisor, ulong maxDivisorSquare)
|
||||
{
|
||||
return divisor != 0UL && divisor <= maxDivisorSquare / divisor;
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<byte> GetGroupBEndingOrder(byte nMod10) => nMod10 switch
|
||||
@@ -434,7 +421,7 @@ Cleanup:
|
||||
|
||||
|
||||
[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;
|
||||
int bestEndingIndex = -1;
|
||||
@@ -449,7 +436,7 @@ Cleanup:
|
||||
}
|
||||
|
||||
ulong candidate = (ulong)divisors[index];
|
||||
if (candidate > sqrtLimit)
|
||||
if (!DivisorWithinSquareLimit(candidate, maxDivisorSquare))
|
||||
{
|
||||
indices[i] = divisors.Length;
|
||||
continue;
|
||||
@@ -474,7 +461,7 @@ Cleanup:
|
||||
}
|
||||
|
||||
[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++)
|
||||
{
|
||||
@@ -486,7 +473,7 @@ Cleanup:
|
||||
}
|
||||
|
||||
ulong candidate = (ulong)divisors[index];
|
||||
if (candidate > sqrtLimit)
|
||||
if (!DivisorWithinSquareLimit(candidate, maxDivisorSquare))
|
||||
{
|
||||
indices[i] = divisors.Length;
|
||||
continue;
|
||||
@@ -533,9 +520,9 @@ Cleanup:
|
||||
}
|
||||
|
||||
[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)]
|
||||
@@ -613,20 +600,20 @@ Cleanup:
|
||||
|
||||
internal ref struct HeuristicDivisorEnumerator
|
||||
{
|
||||
private readonly ulong sqrtLimit;
|
||||
private readonly ulong maxDivisorSquare;
|
||||
private int groupAConstantIndex;
|
||||
private ulong groupAWheelCandidate;
|
||||
private int groupAIncrementIndex;
|
||||
private bool groupAWheelActive;
|
||||
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;
|
||||
groupAWheelCandidate = 23UL;
|
||||
groupAIncrementIndex = 0;
|
||||
groupAWheelActive = sqrtLimit >= 23UL;
|
||||
groupAWheelActive = DivisorWithinSquareLimit(23UL, maxDivisorSquare);
|
||||
|
||||
int count = InitializeGroupBStates(nMod10, groupBBuffer);
|
||||
groupBStates = count == 0 ? Span<HeuristicGroupBSequenceState>.Empty : groupBBuffer[..count];
|
||||
@@ -643,7 +630,7 @@ Cleanup:
|
||||
while (groupAConstantIndex < GroupAConstantDivisors.Length)
|
||||
{
|
||||
ulong value = GroupAConstantDivisors[groupAConstantIndex++];
|
||||
if (value > sqrtLimit)
|
||||
if (!DivisorWithinSquareLimit(value, maxDivisorSquare))
|
||||
{
|
||||
groupAConstantIndex = GroupAConstantDivisors.Length;
|
||||
break;
|
||||
@@ -657,7 +644,7 @@ Cleanup:
|
||||
if (groupAWheelActive)
|
||||
{
|
||||
ulong value = groupAWheelCandidate;
|
||||
if (value <= sqrtLimit)
|
||||
if (DivisorWithinSquareLimit(value, maxDivisorSquare))
|
||||
{
|
||||
candidate = new HeuristicDivisorCandidate(value, HeuristicDivisorGroup.GroupAWheel, 3, 0, (ushort)(value % Wheel210));
|
||||
byte increment = GroupAIncrementPattern[groupAIncrementIndex];
|
||||
@@ -691,7 +678,7 @@ Cleanup:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value > sqrtLimit)
|
||||
if (!DivisorWithinSquareLimit(value, maxDivisorSquare))
|
||||
{
|
||||
state.Candidate = ulong.MaxValue;
|
||||
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] 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`**
|
||||
* [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] 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.
|
||||
|
||||
Reference in new issue
Block a user