diff --git a/EvenPerfectBitScanner.Benchmarks/Mod10UInt128Benchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod10UInt128Benchmarks.cs new file mode 100644 index 0000000..b9d7f46 --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod10UInt128Benchmarks.cs @@ -0,0 +1,52 @@ +using BenchmarkDotNet.Attributes; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[MemoryDiagnoser] +public class Mod10UInt128Benchmarks +{ + [Params(3UL, 8191UL, 131071UL, 2147483647UL)] + public UInt128 Value { get; set; } + + [Benchmark(Baseline = true)] + public UInt128 StandardModulo() + { + return Value % 10UL; + } + + [Benchmark] + public ulong ModWithHighLowModulo() + { + // Split and fold under mod 10: 2^64 ≡ 6 (mod 10) + ulong value = (ulong)Value; + ulong high = value >> 64; + ulong low = value; + ulong highRem = high.Mod10(); + ulong lowRem = low.Mod10(); + ulong combined = lowRem + highRem * 6UL; + return combined.Mod10(); + } + + [Benchmark] + public ulong ModWithLoopModulo() + { + UInt128 value = Value; + UInt128 zero = UInt128.Zero; + if (value == zero) + return 0UL; + + ulong result = (ulong)value; + UInt128 high = value >> 64; + + while (high != zero) + { + // 2^64 ≡ 6 (mod 10) + result = (result + (ulong)high * 6UL).Mod10(); + high >>= 64; + } + + return result.Mod10(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod10Benchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod10ULongBenchmarks.cs similarity index 73% rename from EvenPerfectBitScanner.Benchmarks/Mod10Benchmarks.cs rename to EvenPerfectBitScanner.Benchmarks/Mod10ULongBenchmarks.cs index 90a18d6..04e9cde 100644 --- a/EvenPerfectBitScanner.Benchmarks/Mod10Benchmarks.cs +++ b/EvenPerfectBitScanner.Benchmarks/Mod10ULongBenchmarks.cs @@ -5,7 +5,7 @@ using PerfectNumbers.Core; namespace EvenPerfectBitScanner.Benchmarks; [MemoryDiagnoser] -public class Mod10Benchmarks +public class Mod10ULongBenchmarks { [Params(3UL, 8191UL, 131071UL, 2147483647UL)] public ulong Value { get; set; } @@ -13,14 +13,7 @@ public class Mod10Benchmarks [Benchmark(Baseline = true)] public ulong LegacyModulo() { - ulong value = Value; - UInt128 zero = UInt128.Zero; - if (value == zero) - { - return 0UL; - } - - return value % 10UL; + return Value % 10UL; } [Benchmark] diff --git a/EvenPerfectBitScanner.Benchmarks/Mod10_8_5_3StepsBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod10_8_5_3StepsBenchmarks.cs new file mode 100644 index 0000000..4934cb0 --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod10_8_5_3StepsBenchmarks.cs @@ -0,0 +1,33 @@ +using System.Numerics; +using BenchmarkDotNet.Attributes; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[MemoryDiagnoser] +public class Mod10_8_5_3StepsBenchmarks +{ + [Params(3UL, 8191UL, 131071UL, 2147483647UL)] + public ulong Value { get; set; } + + [Benchmark(Baseline = true)] + public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) LegacyModulo() + { + UInt128 value = Value; + ulong mod10 = (ulong)(value % 10UL) % 10UL; + ulong mod8 = (ulong)(value % 8UL); + ulong mod3 = (ulong)(value % 3UL); + ulong mod5 = (ulong)(value % 5UL); + return (mod10, mod8, mod3, mod5); + } + + [Benchmark] + public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) ModMethodModulo() + { + Value.Mod10_8_5_3(out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3); + // Step residues for q += 2*p + mod10 = mod10.Mod10(); + return (mod10, mod8, mod3, mod5); + } +} + diff --git a/EvenPerfectBitScanner/Program.cs b/EvenPerfectBitScanner/Program.cs index d7049d1..d6c3d50 100644 --- a/EvenPerfectBitScanner/Program.cs +++ b/EvenPerfectBitScanner/Program.cs @@ -1060,7 +1060,7 @@ internal static class Program // Optional: RLE blacklist and binary-threshold filters on p (safe only when configured) if (p <= _rleHardMaxP) { - if (!_rleOnlyLast7 || (p % 10UL) == 7UL) + if (!_rleOnlyLast7 || p.Mod10() == 7UL) { if (RleBlacklist.IsLoaded() && RleBlacklist.Matches(p)) { diff --git a/PerfectNumbers.Core/Cpu/MersenneNumberResidueCpuTester.cs b/PerfectNumbers.Core/Cpu/MersenneNumberResidueCpuTester.cs index 9d912c5..d919835 100644 --- a/PerfectNumbers.Core/Cpu/MersenneNumberResidueCpuTester.cs +++ b/PerfectNumbers.Core/Cpu/MersenneNumberResidueCpuTester.cs @@ -20,10 +20,8 @@ public class MersenneNumberResidueCpuTester // Current residues q.Mod10_8_5_3(out ulong r10, out ulong r8, out ulong r5, out ulong r3); // Step residues for q += 2*p - ulong step10 = (ulong)(twoP % 10UL) % 10UL; - ulong step8 = (ulong)(twoP % 8UL); - ulong step3 = (ulong)(twoP % 3UL); - ulong step5 = (ulong)(twoP % 5UL); + twoP.Mod10_8_5_3(out ulong step10, out ulong step8, out ulong step5, out ulong step3); + step10 = step10.Mod10(); // Allowed last-digit sets for q depending on last digit of M_p // lastIsSeven == true => allow {7,9} diff --git a/PerfectNumbers.Core/Gpu/GpuKernelPool.cs b/PerfectNumbers.Core/Gpu/GpuKernelPool.cs index de8b935..3b91635 100644 --- a/PerfectNumbers.Core/Gpu/GpuKernelPool.cs +++ b/PerfectNumbers.Core/Gpu/GpuKernelPool.cs @@ -201,8 +201,8 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne { ulong idx = (ulong)index.X; // residue automaton - ulong step10 = ((exponent % 10UL) << 1) % 10UL; - ulong r10 = q0m10 + (step10 * idx) % 10UL; + ulong step10 = (exponent.Mod10() << 1).Mod10(); + ulong r10 = q0m10 + (step10 * idx).Mod10(); r10 -= (r10 >= 10UL) ? 10UL : 0UL; bool shouldCheck = r10 != 5UL; // skip q ≡ 5 (mod 10); other residues handled by r8/r3/r5 if (shouldCheck) @@ -358,7 +358,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne ArrayView1D smallPrimesPow2LastSeven) { ulong idx = (ulong)index.X; - ulong r10 = ra.Q0M10 + (ra.Step10 * idx) % 10UL; r10 -= (r10 >= 10UL) ? 10UL : 0UL; + ulong r10 = ra.Q0M10 + (ra.Step10 * idx).Mod10(); r10 -= (r10 >= 10UL) ? 10UL : 0UL; bool shouldCheck = r10 != 5UL; if (shouldCheck) { @@ -489,7 +489,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne ResidueAutomatonArgs ra, ArrayView found, ArrayView1D smallCycles) { ulong idx = (ulong)index.X; - ulong r10 = ra.Q0M10 + (ra.Step10 * idx) % 10UL; r10 -= (r10 >= 10UL) ? 10UL : 0UL; + ulong r10 = ra.Q0M10 + (ra.Step10 * idx).Mod10(); r10 -= (r10 >= 10UL) ? 10UL : 0UL; bool shouldCheck = r10 != 5UL; if (shouldCheck) { @@ -573,7 +573,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne ResidueAutomatonArgs ra, ArrayView found, ArrayView1D smallCycles) { ulong idx = (ulong)index.X; - ulong r10 = ra.Q0M10 + (ra.Step10 * idx) % 10UL; r10 -= (r10 >= 10UL) ? 10UL : 0UL; + ulong r10 = ra.Q0M10 + (ra.Step10 * idx).Mod10(); r10 -= (r10 >= 10UL) ? 10UL : 0UL; bool shouldCheck = r10 != 5UL; if (shouldCheck) { diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs index 9c09e7f..b2f639b 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs @@ -23,7 +23,7 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u var pow2Kernel = gpuLease.Pow2ModKernel; var incKernel = gpuLease.IncrementalKernel; - ulong step10 = ((exponent % 10UL) << 1) % 10UL; + ulong step10 = (exponent.Mod10() << 1).Mod10(); ulong step8 = ((exponent & 7UL) << 1) & 7UL; ulong step3 = ((exponent % 3UL) << 1) % 3UL; ulong step5 = ((exponent % 5UL) << 1) % 5UL; diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs index 9ab8350..9e500e5 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs @@ -7,68 +7,68 @@ 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) - { - var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder); - var execution = gpuLease.EnterExecutionScope(); - var accelerator = gpuLease.Accelerator; - var stream = gpuLease.Stream; - 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 + public void Scan(ulong exponent, UInt128 twoP, bool lastIsSeven, UInt128 maxK, ref bool isPrime) + { + var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder); + var execution = gpuLease.EnterExecutionScope(); + var accelerator = gpuLease.Accelerator; + var stream = gpuLease.Stream; + 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 - var kernel = _kernelType switch - { - GpuKernelType.Pow2Mod => gpuLease.Pow2ModOrderKernel, - _ => gpuLease.IncrementalOrderKernel, - }; + var kernel = _kernelType switch + { + GpuKernelType.Pow2Mod => gpuLease.Pow2ModOrderKernel, + _ => gpuLease.IncrementalOrderKernel, + }; - var foundBuffer = accelerator.Allocate1D(1); - UInt128 exponent128 = exponent; - exponent128.Mod10_8_5_3(out ulong exponentMod10, out ulong exponentMod8, out ulong exponentMod5, out ulong exponentMod3); - ulong step10 = (exponentMod10 << 1) % 10UL; - ulong step8 = (exponentMod8 << 1) & 7UL; - ulong step3 = (exponentMod3 << 1) % 3UL; - ulong step5 = (exponentMod5 << 1) % 5UL; - try - { - UInt128 remaining; - int currentSize; - int found = 0; - while (kStart <= maxK && Volatile.Read(ref isPrime)) - { - remaining = maxK - kStart + 1UL; - currentSize = remaining > (UInt128)batchSize ? batchSize : (int)remaining; - foundBuffer.MemSetToZero(); - // Precompute residue automaton bases for this batch - UInt128 q0 = twoP * kStart + 1UL; - q0.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3); - var kernelArgs = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5); + var foundBuffer = accelerator.Allocate1D(1); + UInt128 exponent128 = exponent; + exponent128.Mod10_8_5_3(out ulong exponentMod10, out ulong exponentMod8, out ulong exponentMod5, out ulong exponentMod3); + ulong step10 = (exponentMod10 << 1).Mod10(); + ulong step8 = (exponentMod8 << 1) & 7UL; + ulong step3 = (exponentMod3 << 1) % 3UL; + ulong step5 = (exponentMod5 << 1) % 5UL; + try + { + UInt128 remaining; + int currentSize; + int found = 0; + while (kStart <= maxK && Volatile.Read(ref isPrime)) + { + remaining = maxK - kStart + 1UL; + currentSize = remaining > (UInt128)batchSize ? batchSize : (int)remaining; + foundBuffer.MemSetToZero(); + // Precompute residue automaton bases for this batch + UInt128 q0 = twoP * kStart + 1UL; + q0.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3); + var kernelArgs = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5); - // Ensure device has small cycles table for early rejections in order kernels - var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator); - kernel(stream, currentSize, exponent, (GpuUInt128)twoP, (GpuUInt128)kStart, last, divMul, - kernelArgs, foundBuffer.View, smallCyclesView); + // Ensure device has small cycles table for early rejections in order kernels + var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator); + kernel(stream, currentSize, exponent, (GpuUInt128)twoP, (GpuUInt128)kStart, last, divMul, + kernelArgs, foundBuffer.View, smallCyclesView); - stream.Synchronize(); - foundBuffer.View.CopyToCPU(ref found, 1); - if (found != 0) - { - Volatile.Write(ref isPrime, false); - break; - } + stream.Synchronize(); + foundBuffer.View.CopyToCPU(ref found, 1); + if (found != 0) + { + Volatile.Write(ref isPrime, false); + break; + } - kStart += (UInt128)currentSize; - } - } - finally - { - foundBuffer.Dispose(); - execution.Dispose(); - gpuLease.Dispose(); - } + kStart += (UInt128)currentSize; + } + } + finally + { + foundBuffer.Dispose(); + execution.Dispose(); + gpuLease.Dispose(); + } - } + } } diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs index ac1efe1..d44cc73 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs @@ -24,7 +24,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder) UInt128 kStart = 1UL; byte last = lastIsSeven ? (byte)1 : (byte)0; var kernel = gpuLease.Pow2ModKernel; - ulong step10 = ((exponent % 10UL) << 1) % 10UL; + ulong step10 = (exponent.Mod10() << 1).Mod10(); ulong step8 = ((exponent & 7UL) << 1) & 7UL; ulong step3 = ((exponent % 3UL) << 1) % 3UL; ulong step5 = ((exponent % 5UL) << 1) % 5UL; diff --git a/PerfectNumbers.Core/ModuloAutomata.cs b/PerfectNumbers.Core/ModuloAutomata.cs index 2b8fede..b264cd6 100644 --- a/PerfectNumbers.Core/ModuloAutomata.cs +++ b/PerfectNumbers.Core/ModuloAutomata.cs @@ -30,12 +30,12 @@ public sealed class MersenneResidueAutomaton _currentQ = _step + 1UL; // start at k = 1 // init residues - Mod10R = Mod10(_currentQ); + Mod10R = _currentQ.Mod10(); Mod8R = Mod8(_currentQ); Mod3R = Mod3(_currentQ); Mod5R = Mod5(_currentQ); - _step10 = Mod10((ulong)(_step % 10UL)); + _step10 = _step.Mod10().Mod10(); _step8 = (ulong)(_step & 7UL); // % 8 _step3 = Mod3(_step); _step5 = Mod5(_step); @@ -72,21 +72,6 @@ public sealed class MersenneResidueAutomaton Mod5R = r5; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong Mod10(UInt128 value) - { - // Split and fold under mod 10: 2^64 ≡ 6 (mod 10) - ulong high = (ulong)(value >> 64); - ulong low = (ulong)value; - ulong highRem = Mod10(high); - ulong lowRem = Mod10(low); - ulong combined = lowRem + highRem * 6UL; - return Mod10(combined); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong Mod10(ulong value) => value % 10UL; - [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong Mod8(UInt128 value) => (ulong)value & 7UL; @@ -196,8 +181,8 @@ public sealed class Ending7Automaton [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong AlignTo7(ulong start) { - ulong r = start % 10UL; - ulong add = (7UL + 10UL - r) % 10UL; + ulong r = start.Mod10(); + ulong add = (7UL + 10UL - r).Mod10(); return start + add; } diff --git a/PerfectNumbers.Core/PrimeTester.cs b/PerfectNumbers.Core/PrimeTester.cs index 8651119..8e9fde4 100644 --- a/PerfectNumbers.Core/PrimeTester.cs +++ b/PerfectNumbers.Core/PrimeTester.cs @@ -59,7 +59,7 @@ public sealed class PrimeTester(bool useInternal = false) } else { - if ((n % 10UL) == 1UL && SharesFactorWithMaxExponent(n)) + if (n.Mod10() == 1UL && SharesFactorWithMaxExponent(n)) { result = false; } @@ -266,7 +266,7 @@ public sealed class PrimeTester(bool useInternal = false) return; } - if ((n % 10UL) == 1UL) + if (n.Mod10() == 1UL) { // Early reject special GCD heuristic with floor(log2 n) ulong m = 63UL - (ulong)ILGPU.Algorithms.XMath.LeadingZeroCount(n); diff --git a/PerfectNumbers.Core/PrimesGenerator.cs b/PerfectNumbers.Core/PrimesGenerator.cs index 0ec6ed1..afe2408 100644 --- a/PerfectNumbers.Core/PrimesGenerator.cs +++ b/PerfectNumbers.Core/PrimesGenerator.cs @@ -5,109 +5,109 @@ namespace PerfectNumbers.Core; public static class PrimesGenerator { -public static readonly uint[] SmallPrimes; -public static readonly ulong[] SmallPrimesPow2; -public static readonly uint[] SmallPrimesLastOne; -public static readonly ulong[] SmallPrimesPow2LastOne; -public static readonly uint[] SmallPrimesLastSeven; -public static readonly ulong[] SmallPrimesPow2LastSeven; + public static readonly uint[] SmallPrimes; + public static readonly ulong[] SmallPrimesPow2; + public static readonly uint[] SmallPrimesLastOne; + public static readonly ulong[] SmallPrimesPow2LastOne; + public static readonly uint[] SmallPrimesLastSeven; + public static readonly ulong[] SmallPrimesPow2LastSeven; -static PrimesGenerator() -{ -BuildSmallPrimes( -out SmallPrimes, -out SmallPrimesPow2, -out SmallPrimesLastOne, -out SmallPrimesPow2LastOne, -out SmallPrimesLastSeven, -out SmallPrimesPow2LastSeven); -} + static PrimesGenerator() + { + BuildSmallPrimes( + out SmallPrimes, + out SmallPrimesPow2, + out SmallPrimesLastOne, + out SmallPrimesPow2LastOne, + out SmallPrimesLastSeven, + out SmallPrimesPow2LastSeven); + } -private static void BuildSmallPrimes( -out uint[] all, -out ulong[] allPow2, -out uint[] lastOne, -out ulong[] lastOnePow2, -out uint[] lastSeven, -out ulong[] lastSevenPow2) -{ -uint target = PerfectNumberConstants.PrimesLimit; -int targetInt = checked((int)target); -all = new uint[targetInt]; -allPow2 = new ulong[targetInt]; -lastOne = new uint[targetInt]; -lastOnePow2 = new ulong[targetInt]; -lastSeven = new uint[targetInt]; -lastSevenPow2 = new ulong[targetInt]; + private static void BuildSmallPrimes( + out uint[] all, + out ulong[] allPow2, + out uint[] lastOne, + out ulong[] lastOnePow2, + out uint[] lastSeven, + out ulong[] lastSevenPow2) + { + uint target = PerfectNumberConstants.PrimesLimit; + int targetInt = checked((int)target); + all = new uint[targetInt]; + allPow2 = new ulong[targetInt]; + lastOne = new uint[targetInt]; + lastOnePow2 = new ulong[targetInt]; + lastSeven = new uint[targetInt]; + lastSevenPow2 = new ulong[targetInt]; -List primes = new(targetInt * 4 / 3 + 16); -uint candidate = 2U; -int allCount = 0; -int lastOneCount = 0; -int lastSevenCount = 0; + List primes = new(targetInt * 4 / 3 + 16); + uint candidate = 2U; + int allCount = 0; + int lastOneCount = 0; + int lastSevenCount = 0; -while (allCount < targetInt || lastOneCount < targetInt || lastSevenCount < targetInt) -{ -bool isPrime = true; -int primesCount = primes.Count; -for (int i = 0; i < primesCount; i++) -{ -uint p = primes[i]; -if (p * p > candidate) -{ -break; -} + while (allCount < targetInt || lastOneCount < targetInt || lastSevenCount < targetInt) + { + bool isPrime = true; + int primesCount = primes.Count; + for (int i = 0; i < primesCount; i++) + { + uint p = primes[i]; + if (p * p > candidate) + { + break; + } -if (candidate % p == 0U) -{ -isPrime = false; -break; -} -} + if (candidate % p == 0U) + { + isPrime = false; + break; + } + } -if (isPrime) -{ -primes.Add(candidate); -if (allCount < targetInt) -{ -all[allCount] = candidate; -allPow2[allCount] = checked((ulong)candidate * candidate); -allCount++; -} + if (isPrime) + { + primes.Add(candidate); + if (allCount < targetInt) + { + all[allCount] = candidate; + allPow2[allCount] = checked((ulong)candidate * candidate); + allCount++; + } -if (candidate != 2U) -{ -if (lastOneCount < targetInt && IsAllowedForLastOne(candidate)) -{ -lastOne[lastOneCount] = candidate; -lastOnePow2[lastOneCount] = checked((ulong)candidate * candidate); -lastOneCount++; -} + if (candidate != 2U) + { + if (lastOneCount < targetInt && IsAllowedForLastOne(candidate)) + { + lastOne[lastOneCount] = candidate; + lastOnePow2[lastOneCount] = checked((ulong)candidate * candidate); + lastOneCount++; + } -if (lastSevenCount < targetInt && IsAllowedForLastSeven(candidate)) -{ -lastSeven[lastSevenCount] = candidate; -lastSevenPow2[lastSevenCount] = checked((ulong)candidate * candidate); -lastSevenCount++; -} -} -} + if (lastSevenCount < targetInt && IsAllowedForLastSeven(candidate)) + { + lastSeven[lastSevenCount] = candidate; + lastSevenPow2[lastSevenCount] = checked((ulong)candidate * candidate); + lastSevenCount++; + } + } + } -candidate = candidate == 2U ? 3U : candidate + 2U; -} -} + candidate = candidate == 2U ? 3U : candidate + 2U; + } + } -[MethodImpl(MethodImplOptions.AggressiveInlining)] -private static bool IsAllowedForLastOne(uint prime) -{ -uint mod10 = prime % 10U; -return mod10 == 1U || mod10 == 3U || mod10 == 9U || prime == 7U || prime == 11U; -} + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsAllowedForLastOne(uint prime) + { + uint mod10 = prime % 10U; + return mod10 == 1U || mod10 == 3U || mod10 == 9U || prime == 7U || prime == 11U; + } -[MethodImpl(MethodImplOptions.AggressiveInlining)] -private static bool IsAllowedForLastSeven(uint prime) -{ -uint mod10 = prime % 10U; -return mod10 == 3U || mod10 == 7U || mod10 == 9U || prime == 11U; -} + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsAllowedForLastSeven(uint prime) + { + uint mod10 = prime % 10U; + return mod10 == 3U || mod10 == 7U || mod10 == 9U || prime == 11U; + } } diff --git a/PerfectNumbers.Core/UInt128Extensions.cs b/PerfectNumbers.Core/UInt128Extensions.cs index ba144c2..5a375b9 100644 --- a/PerfectNumbers.Core/UInt128Extensions.cs +++ b/PerfectNumbers.Core/UInt128Extensions.cs @@ -215,11 +215,11 @@ public static class UInt128Extensions while (high != zero) { // 2^64 ≡ 6 (mod 10) - result = (result + (ulong)high * 6UL) % 10UL; + result = (result + (ulong)high * 6UL).Mod10(); high >>= 64; } - return result % 10UL; + return result.Mod10(); } [MethodImpl(MethodImplOptions.AggressiveInlining)]