Use LastDigit enum across Mersenne scanners
This commit is contained in:
1 parent
43c7bafd5d
commit
a83ae56caf
15 files changed
+83
-49
No files matched your search
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Cpu;
|
||||
using Xunit;
|
||||
using UInt128 = System.UInt128;
|
||||
@@ -7,7 +8,7 @@ namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class MersenneNumberIncrementalCpuTesterTests
|
||||
{
|
||||
private static bool LastDigitIsSeven(ulong exponent) => (exponent & 3UL) == 3UL;
|
||||
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
|
||||
[Theory]
|
||||
[InlineData(GpuKernelType.Incremental)]
|
||||
@@ -30,7 +31,7 @@ public class MersenneNumberIncrementalCpuTesterTests
|
||||
private static void RunCase(MersenneNumberIncrementalCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
bool isPrime = true;
|
||||
tester.Scan(exponent, (UInt128)exponent << 1, LastDigitIsSeven(exponent), (UInt128)maxK, ref isPrime);
|
||||
tester.Scan(exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
|
||||
isPrime.Should().Be(expectedPrime);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using Xunit;
|
||||
using UInt128 = System.UInt128;
|
||||
@@ -7,7 +8,7 @@ namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class MersenneNumberIncrementalGpuTesterTests
|
||||
{
|
||||
private static bool LastDigitIsSeven(ulong exponent) => (exponent & 3UL) == 3UL;
|
||||
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
|
||||
[Theory(Skip = "Disabled until the GPU incremental cycle heuristics are ported.")]
|
||||
[InlineData(GpuKernelType.Pow2Mod, false)]
|
||||
@@ -32,7 +33,7 @@ public class MersenneNumberIncrementalGpuTesterTests
|
||||
private static void RunCase(MersenneNumberIncrementalGpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
bool isPrime = true;
|
||||
tester.Scan(exponent, (UInt128)exponent << 1, LastDigitIsSeven(exponent), (UInt128)maxK, ref isPrime);
|
||||
tester.Scan(exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
|
||||
isPrime.Should().Be(expectedPrime);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Cpu;
|
||||
using Xunit;
|
||||
using UInt128 = System.UInt128;
|
||||
@@ -7,7 +8,7 @@ namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class MersenneNumberOrderCpuTesterTests
|
||||
{
|
||||
private static bool LastDigitIsSeven(ulong exponent) => (exponent & 3UL) == 3UL;
|
||||
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
|
||||
[Theory]
|
||||
[InlineData(GpuKernelType.Incremental)]
|
||||
@@ -30,7 +31,7 @@ public class MersenneNumberOrderCpuTesterTests
|
||||
private static void RunCase(MersenneNumberOrderCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
bool isPrime = true;
|
||||
tester.Scan(exponent, (UInt128)exponent << 1, LastDigitIsSeven(exponent), (UInt128)maxK, ref isPrime);
|
||||
tester.Scan(exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
|
||||
isPrime.Should().Be(expectedPrime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Reflection;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using Xunit;
|
||||
using UInt128 = System.UInt128;
|
||||
@@ -8,7 +9,7 @@ namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class MersenneNumberOrderGpuTesterTests
|
||||
{
|
||||
private static bool LastDigitIsSeven(ulong exponent) => (exponent & 3UL) == 3UL;
|
||||
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
|
||||
[Theory]
|
||||
[InlineData(GpuKernelType.Pow2Mod, false)]
|
||||
@@ -64,7 +65,7 @@ public class MersenneNumberOrderGpuTesterTests
|
||||
tester.Scan(
|
||||
exponent,
|
||||
(UInt128)exponent << 1,
|
||||
LastDigitIsSeven(exponent),
|
||||
GetLastDigit(exponent),
|
||||
(UInt128)10UL,
|
||||
ref isPrime);
|
||||
|
||||
@@ -86,7 +87,7 @@ public class MersenneNumberOrderGpuTesterTests
|
||||
private static void RunCase(MersenneNumberOrderGpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
bool isPrime = true;
|
||||
tester.Scan(exponent, (UInt128)exponent << 1, LastDigitIsSeven(exponent), (UInt128)maxK, ref isPrime);
|
||||
tester.Scan(exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
|
||||
isPrime.Should().Be(expectedPrime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Cpu;
|
||||
using Xunit;
|
||||
using UInt128 = System.UInt128;
|
||||
@@ -7,7 +8,7 @@ namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class MersenneNumberResidueCpuTesterTests
|
||||
{
|
||||
private static bool LastDigitIsSeven(ulong exponent) => (exponent & 3UL) == 3UL;
|
||||
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
@@ -24,7 +25,7 @@ public class MersenneNumberResidueCpuTesterTests
|
||||
private static void RunCase(MersenneNumberResidueCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
bool isPrime = true;
|
||||
tester.Scan(exponent, (UInt128)exponent << 1, LastDigitIsSeven(exponent), (UInt128)maxK, ref isPrime);
|
||||
tester.Scan(exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
|
||||
isPrime.Should().Be(expectedPrime);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using Xunit;
|
||||
using UInt128 = System.UInt128;
|
||||
@@ -7,7 +8,7 @@ namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class MersenneNumberResidueGpuTesterTests
|
||||
{
|
||||
private static bool LastDigitIsSeven(ulong exponent) => (exponent & 3UL) == 3UL;
|
||||
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
@@ -27,7 +28,7 @@ public class MersenneNumberResidueGpuTesterTests
|
||||
private static void RunCase(MersenneNumberResidueGpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
bool isPrime = true;
|
||||
tester.Scan(exponent, (UInt128)exponent << 1, LastDigitIsSeven(exponent), (UInt128)maxK, ref isPrime);
|
||||
tester.Scan(exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
|
||||
isPrime.Should().Be(expectedPrime);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ public class MersenneNumberIncrementalCpuTester(GpuKernelType kernelType)
|
||||
private static readonly MersenneDivisorCycles _cycles = MersenneDivisorCycles.Shared;
|
||||
private readonly GpuKernelType _kernelType = kernelType;
|
||||
|
||||
public void Scan(ulong exponent, UInt128 twoP, bool lastIsSeven, UInt128 maxK, ref bool isPrime)
|
||||
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
||||
{
|
||||
UInt128 k = UInt128.One;
|
||||
UInt128 one = k;
|
||||
|
||||
@@ -4,7 +4,7 @@ public class MersenneNumberOrderCpuTester(GpuKernelType kernelType)
|
||||
{
|
||||
private readonly GpuKernelType _kernelType = kernelType;
|
||||
|
||||
public void Scan(ulong exponent, UInt128 twoP, bool lastIsSeven, UInt128 maxK, ref bool isPrime)
|
||||
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
||||
{
|
||||
UInt128 k = 1UL;
|
||||
var auto = new MersenneResidueAutomaton(exponent);
|
||||
@@ -17,7 +17,7 @@ public class MersenneNumberOrderCpuTester(GpuKernelType kernelType)
|
||||
// TODO: When this lookup misses the snapshot, invoke the configured device to compute the
|
||||
// single required cycle on demand without persisting it so the CPU order path retains the
|
||||
// cycle-stepping speedups while honoring the no-extra-cache constraint for large divisors.
|
||||
int allowMask2 = lastIsSeven ? CpuConstants.LastSevenMask10 : CpuConstants.LastOneMask10;
|
||||
int allowMask2 = lastDigit == LastDigit.Seven ? CpuConstants.LastSevenMask10 : CpuConstants.LastOneMask10;
|
||||
bool shouldCheck = ((allowMask2 >> (int)auto.Mod10R) & 1) != 0;
|
||||
if (shouldCheck)
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ public class MersenneNumberResidueCpuTester
|
||||
private ModResidueTracker? _mersenneResidueTracker;
|
||||
|
||||
// CPU residue variant using tracker + unrolled residue updates (no method calls in the loop).
|
||||
public void Scan(ulong exponent, UInt128 twoP, bool lastIsSeven, UInt128 maxK, ref bool isPrime)
|
||||
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
||||
{
|
||||
// Initialize/update Mersenne residue tracker and start a merge walk for ascending q divisors
|
||||
|
||||
@@ -29,9 +29,9 @@ public class MersenneNumberResidueCpuTester
|
||||
step10 = step10.Mod10();
|
||||
|
||||
// Allowed last-digit sets for q depending on last digit of M_p
|
||||
// lastIsSeven == true => allow {7,9}
|
||||
// lastIsSeven == false => allow {1,3,7,9}
|
||||
int allowMask = lastIsSeven ? CpuConstants.LastSevenMask10 : CpuConstants.LastOneMask10;
|
||||
// LastDigit.Seven => allow {7,9}
|
||||
// otherwise => allow {1,3,7,9}
|
||||
int allowMask = lastDigit == LastDigit.Seven ? CpuConstants.LastSevenMask10 : CpuConstants.LastOneMask10;
|
||||
|
||||
// Predeclare temps to avoid redeclaration overhead in the loop
|
||||
UInt128 remaining;
|
||||
|
||||
@@ -12,7 +12,7 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
|
||||
private readonly GpuKernelType _kernelType = kernelType;
|
||||
private readonly bool _useGpuOrder = useGpuOrder;
|
||||
|
||||
public void Scan(ulong exponent, UInt128 twoP, bool lastIsSeven, UInt128 maxK, ref bool isPrime)
|
||||
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
||||
{
|
||||
throw new NotImplementedException($"GPU incremental scanning requires the device cycle heuristics implementation (kernel: {_kernelType}, GPU order: {_useGpuOrder}).");
|
||||
|
||||
@@ -23,7 +23,7 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
|
||||
int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy
|
||||
UInt128 kStart = 1UL;
|
||||
ulong divMul = (ulong)((((UInt128)1 << 64) - UInt128.One) / exponent) + 1UL;
|
||||
byte last = lastIsSeven ? (byte)1 : (byte)0; // ILGPU kernels do not support bool parameters
|
||||
byte last = lastDigit == LastDigit.Seven ? (byte)1 : (byte)0; // ILGPU kernels do not support bool parameters
|
||||
|
||||
var pow2Kernel = gpuLease.Pow2ModKernel;
|
||||
var incKernel = gpuLease.IncrementalKernel;
|
||||
|
||||
@@ -7,7 +7,7 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
|
||||
private readonly GpuKernelType _kernelType = kernelType;
|
||||
private readonly bool _useGpuOrder = useGpuOrder;
|
||||
|
||||
public void Scan(ulong exponent, UInt128 twoP, bool lastIsSeven, UInt128 maxK, ref bool isPrime)
|
||||
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
||||
{
|
||||
var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
|
||||
var execution = gpuLease.EnterExecutionScope();
|
||||
@@ -16,7 +16,7 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
|
||||
int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy and avoids TDR
|
||||
UInt128 kStart = UInt128.One;
|
||||
ulong divMul = (ulong)((((UInt128)1 << 64) - UInt128.One) / exponent) + 1UL;
|
||||
byte last = lastIsSeven ? (byte)1 : (byte)0; // ILGPU kernels do not support bool parameters
|
||||
byte last = lastDigit == LastDigit.Seven ? (byte)1 : (byte)0; // ILGPU kernels do not support bool parameters
|
||||
|
||||
var kernel = _kernelType switch
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
||||
// requested by the caller, skip queuing additional block generation, and keep the snapshot cache untouched.
|
||||
|
||||
// GPU residue variant: check 2^p % q == 1 for q = 2*p*k + 1.
|
||||
public void Scan(ulong exponent, UInt128 twoP, bool lastIsSeven, UInt128 maxK, ref bool isPrime)
|
||||
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
|
||||
{
|
||||
var limiter = GpuPrimeWorkLimiter.Acquire();
|
||||
var gpuLease = RentAccelerator();
|
||||
@@ -46,7 +46,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
||||
|
||||
UInt128 kStart = 1UL;
|
||||
UInt128 limit = maxK + UInt128.One;
|
||||
byte last = lastIsSeven ? (byte)1 : (byte)0;
|
||||
byte last = lastDigit == LastDigit.Seven ? (byte)1 : (byte)0;
|
||||
twoP.Mod10_8_5_3(out ulong step10, out ulong step8, out ulong step5, out ulong step3);
|
||||
step10 = step10.Mod10();
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
public enum LastDigit : byte
|
||||
{
|
||||
One,
|
||||
Three,
|
||||
Five,
|
||||
Seven,
|
||||
Nine,
|
||||
}
|
||||
@@ -58,7 +58,7 @@ public sealed class MersenneNumberTester(
|
||||
}
|
||||
|
||||
UInt128 twoP = (UInt128)exponent << 1; // 2 * p
|
||||
bool lastIsSeven = (exponent & 3UL) == 3UL;
|
||||
LastDigit lastDigit = (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
// CPU path: compute orders directly and cache
|
||||
if (!_useGpuOrder)
|
||||
{
|
||||
@@ -71,7 +71,7 @@ public sealed class MersenneNumberTester(
|
||||
remainder = q.Mod10();
|
||||
// TODO: Replace these repeated Mod10/Mod8/Mod3/Mod5 calls with a residue automaton walk so
|
||||
// order warmups reuse the optimized cycle stepping validated in the residue benchmarks.
|
||||
bool shouldCheck = lastIsSeven
|
||||
bool shouldCheck = lastDigit == LastDigit.Seven
|
||||
? remainder == 7UL || remainder == 9UL || remainder == 3UL
|
||||
: remainder == 1UL || remainder == 3UL || remainder == 9UL;
|
||||
|
||||
@@ -112,7 +112,7 @@ public sealed class MersenneNumberTester(
|
||||
remainder2 = q2.Mod10();
|
||||
// TODO: Reuse the same residue-automaton fast path here so the GPU warmup staging avoids `%` and
|
||||
// branches the benchmarks showed slower than the tracked stepping helper.
|
||||
bool shouldCheck2 = lastIsSeven
|
||||
bool shouldCheck2 = lastDigit == LastDigit.Seven
|
||||
? remainder2 == 7UL || remainder2 == 9UL || remainder2 == 3UL
|
||||
: remainder2 == 1UL || remainder2 == 3UL || remainder2 == 9UL;
|
||||
|
||||
@@ -230,7 +230,7 @@ public sealed class MersenneNumberTester(
|
||||
UInt128 twoP = (UInt128)exponent << 1; // 2 * p
|
||||
// UInt128 maxDivisor = new(ulong.MaxValue, ulong.MaxValue);
|
||||
UInt128 maxK = UInt128Numbers.Two.Pow(exponent) / 2 + 1;
|
||||
bool lastIsSeven = (exponent & 3UL) == 3UL;
|
||||
LastDigit lastDigit = (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
|
||||
// In residue mode the residue scan is the final primality check.
|
||||
if (_useResidue)
|
||||
@@ -242,11 +242,11 @@ public sealed class MersenneNumberTester(
|
||||
|
||||
if (_useGpuScan)
|
||||
{
|
||||
_residueGpuTester!.Scan(exponent, twoP, lastIsSeven, maxK, ref prePrime);
|
||||
_residueGpuTester!.Scan(exponent, twoP, lastDigit, maxK, ref prePrime);
|
||||
}
|
||||
else
|
||||
{
|
||||
_residueCpuTester!.Scan(exponent, twoP, lastIsSeven, maxK, ref prePrime);
|
||||
_residueCpuTester!.Scan(exponent, twoP, lastDigit, maxK, ref prePrime);
|
||||
}
|
||||
|
||||
return prePrime;
|
||||
@@ -258,14 +258,14 @@ public sealed class MersenneNumberTester(
|
||||
// Scan a limited range of k to quickly reject obvious cases.
|
||||
UInt128 twoPPre = (UInt128)exponent << 1;
|
||||
UInt128 maxKPre = (UInt128)PreLucasPreScanK;
|
||||
bool lastIsSevenPre = (exponent & 3UL) == 3UL;
|
||||
LastDigit lastDigitPre = (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
if (_useGpuScan)
|
||||
{
|
||||
_incrementalGpuTester.Scan(exponent, twoPPre, lastIsSevenPre, maxKPre, ref prePrime);
|
||||
_incrementalGpuTester.Scan(exponent, twoPPre, lastDigitPre, maxKPre, ref prePrime);
|
||||
}
|
||||
else
|
||||
{
|
||||
_incrementalCpuTester.Scan(exponent, twoPPre, lastIsSevenPre, maxKPre, ref prePrime);
|
||||
_incrementalCpuTester.Scan(exponent, twoPPre, lastDigitPre, maxKPre, ref prePrime);
|
||||
}
|
||||
|
||||
if (!prePrime)
|
||||
@@ -284,22 +284,22 @@ public sealed class MersenneNumberTester(
|
||||
{
|
||||
if (_useGpuOrder)
|
||||
{
|
||||
_orderGpuTester.Scan(exponent, twoP, lastIsSeven, maxK, ref isPrime);
|
||||
_orderGpuTester.Scan(exponent, twoP, lastDigit, maxK, ref isPrime);
|
||||
}
|
||||
else
|
||||
{
|
||||
_orderCpuTester.Scan(exponent, twoP, lastIsSeven, maxK, ref isPrime);
|
||||
_orderCpuTester.Scan(exponent, twoP, lastDigit, maxK, ref isPrime);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_useGpuScan)
|
||||
{
|
||||
_incrementalGpuTester.Scan(exponent, twoP, lastIsSeven, maxK, ref isPrime);
|
||||
_incrementalGpuTester.Scan(exponent, twoP, lastDigit, maxK, ref isPrime);
|
||||
}
|
||||
else
|
||||
{
|
||||
_incrementalCpuTester.Scan(exponent, twoP, lastIsSeven, maxK, ref isPrime);
|
||||
_incrementalCpuTester.Scan(exponent, twoP, lastDigit, maxK, ref isPrime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
@@ -85,14 +86,16 @@ public static class PrimesGenerator
|
||||
|
||||
if (candidate != 2U)
|
||||
{
|
||||
if (lastOneCount < targetInt && IsAllowedForLastOne(candidate))
|
||||
LastDigit lastDigit = GetLastDigit(candidate);
|
||||
|
||||
if (lastOneCount < targetInt && IsAllowedForLastOne(candidate, lastDigit))
|
||||
{
|
||||
lastOne[lastOneCount] = candidate;
|
||||
lastOnePow2[lastOneCount] = checked((ulong)candidate * candidate);
|
||||
lastOneCount++;
|
||||
}
|
||||
|
||||
if (lastSevenCount < targetInt && IsAllowedForLastSeven(candidate))
|
||||
if (lastSevenCount < targetInt && IsAllowedForLastSeven(candidate, lastDigit))
|
||||
{
|
||||
lastSeven[lastSevenCount] = candidate;
|
||||
lastSevenPow2[lastSevenCount] = checked((ulong)candidate * candidate);
|
||||
@@ -108,25 +111,40 @@ public static class PrimesGenerator
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsAllowedForLastOne(uint prime)
|
||||
private static LastDigit GetLastDigit(uint prime)
|
||||
{
|
||||
// TODO: Swap the `% 10` usage for ULongExtensions.Mod10 so the hot classification path
|
||||
// reuses the benchmarked residue helper instead of repeated divisions.
|
||||
return (prime % 10U) switch
|
||||
{
|
||||
1U or 3U or 9U => true,
|
||||
_ => prime == 7U || prime == 11U,
|
||||
1U => LastDigit.One,
|
||||
3U => LastDigit.Three,
|
||||
5U => LastDigit.Five,
|
||||
7U => LastDigit.Seven,
|
||||
9U => LastDigit.Nine,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(prime)),
|
||||
};
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsAllowedForLastSeven(uint prime)
|
||||
private static bool IsAllowedForLastOne(uint prime, LastDigit lastDigit)
|
||||
{
|
||||
// TODO: Swap the `% 10` usage for ULongExtensions.Mod10 so the hot classification path
|
||||
// reuses the benchmarked residue helper instead of repeated divisions.
|
||||
return lastDigit switch
|
||||
{
|
||||
LastDigit.One or LastDigit.Three or LastDigit.Nine => true,
|
||||
LastDigit.Seven => prime == 7U,
|
||||
_ => prime == 11U,
|
||||
};
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsAllowedForLastSeven(uint prime, LastDigit lastDigit)
|
||||
{
|
||||
// TODO: Route this `% 10` classification through ULongExtensions.Mod10 to match the faster
|
||||
// residue helper used elsewhere in the scanner.
|
||||
return (prime % 10U) switch
|
||||
return lastDigit switch
|
||||
{
|
||||
3U or 7U or 9U => true,
|
||||
LastDigit.Three or LastDigit.Seven or LastDigit.Nine => true,
|
||||
_ => prime == 11U,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user