From ca866ffc5b4d0014de9ba42cf00b2dc27d9c3751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Mon, 22 Sep 2025 19:26:07 +0200 Subject: [PATCH 1/4] Reapply modulo helpers and inline fast remainders --- .../Mod10UInt128Benchmarks.cs | 16 +-- .../Mod10UIntBenchmarks.cs | 26 +++++ .../Mod10_8_5_3Benchmarks.cs | 59 +++++----- .../Mod10_8_5_3StepsBenchmarks.cs | 2 +- .../Mod11UInt128Benchmarks.cs | 27 +++++ .../Mod11UIntBenchmarks.cs | 26 +++++ .../Mod11ULongBenchmarks.cs | 26 +++++ .../Mod11_7_5_3Benchmarks.cs | 32 ++++++ .../Mod128UInt128Benchmarks.cs | 27 +++++ .../Mod128UIntBenchmarks.cs | 26 +++++ .../Mod128ULongBenchmarks.cs | 26 +++++ .../Mod3UInt128Benchmarks.cs | 27 +++++ .../Mod3UIntBenchmarks.cs | 26 +++++ .../Mod3ULongBenchmarks.cs | 26 +++++ .../Mod5UInt128Benchmarks.cs | 27 +++++ .../Mod5UIntBenchmarks.cs | 26 +++++ .../Mod5ULongBenchmarks.cs | 26 +++++ .../Mod6ComparisonBenchmarks.cs | 81 ++++++++++++++ .../Mod6UInt128Benchmarks.cs | 27 +++++ .../Mod6UIntBenchmarks.cs | 26 +++++ .../Mod6ULongBenchmarks.cs | 43 ++++++++ .../Mod7UInt128Benchmarks.cs | 27 +++++ .../Mod7UIntBenchmarks.cs | 26 +++++ .../Mod7ULongBenchmarks.cs | 26 +++++ .../Mod8UInt128Benchmarks.cs | 27 +++++ .../Mod8UIntBenchmarks.cs | 26 +++++ .../Mod8ULongBenchmarks.cs | 26 +++++ EvenPerfectBitScanner.Benchmarks/Program.cs | 1 + EvenPerfectBitScanner/Program.cs | 4 +- .../UInt128ExtensionsTests.cs | 8 ++ .../UIntExtensionsTests.cs | 11 ++ .../ULongExtensionsTests.cs | 26 +++++ .../Cpu/MersenneNumberLucasLehmerCpuTester.cs | 16 +-- .../Cpu/MersenneNumberResidueCpuTester.cs | 2 +- .../Gpu/MersenneNumberLucasLehmerGpuTester.cs | 8 +- .../Gpu/MersenneNumberOrderGpuTester.cs | 6 +- PerfectNumbers.Core/MersenneNumberTester.cs | 14 +-- PerfectNumbers.Core/PerfectNumbersMath.cs | 21 ---- PerfectNumbers.Core/PrimeTester.cs | 8 +- PerfectNumbers.Core/PrimesGenerator.cs | 4 +- PerfectNumbers.Core/UInt128Extensions.cs | 20 +++- PerfectNumbers.Core/UIntExtensions.cs | 101 ++++++++++-------- PerfectNumbers.Core/ULongExtensions.cs | 80 +++++++++++++- 43 files changed, 982 insertions(+), 134 deletions(-) create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod10UIntBenchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod11UInt128Benchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod11UIntBenchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod11ULongBenchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod11_7_5_3Benchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod128UInt128Benchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod128UIntBenchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod128ULongBenchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod3UInt128Benchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod3UIntBenchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod3ULongBenchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod5UInt128Benchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod5UIntBenchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod5ULongBenchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod6ComparisonBenchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod6UInt128Benchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod6UIntBenchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod6ULongBenchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod7UInt128Benchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod7UIntBenchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod7ULongBenchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod8UInt128Benchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod8UIntBenchmarks.cs create mode 100644 EvenPerfectBitScanner.Benchmarks/Mod8ULongBenchmarks.cs delete mode 100644 PerfectNumbers.Core/PerfectNumbersMath.cs diff --git a/EvenPerfectBitScanner.Benchmarks/Mod10UInt128Benchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod10UInt128Benchmarks.cs index b9d7f46..ff414ea 100644 --- a/EvenPerfectBitScanner.Benchmarks/Mod10UInt128Benchmarks.cs +++ b/EvenPerfectBitScanner.Benchmarks/Mod10UInt128Benchmarks.cs @@ -22,10 +22,10 @@ public class Mod10UInt128Benchmarks 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(); + ulong highRem = high % 10UL; + ulong lowRem = low % 10UL; + ulong combined = lowRem + highRem * 6UL; + return combined % 10UL; } [Benchmark] @@ -42,11 +42,11 @@ public class Mod10UInt128Benchmarks while (high != zero) { // 2^64 ≡ 6 (mod 10) - result = (result + (ulong)high * 6UL).Mod10(); - high >>= 64; - } + result = (result + (ulong)high * 6UL) % 10UL; + high >>= 64; + } - return result.Mod10(); + return result % 10UL; } } diff --git a/EvenPerfectBitScanner.Benchmarks/Mod10UIntBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod10UIntBenchmarks.cs new file mode 100644 index 0000000..ccfb1e9 --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod10UIntBenchmarks.cs @@ -0,0 +1,26 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)] +[MemoryDiagnoser] +public class Mod10UIntBenchmarks +{ + [Params(10U, 2047U, 65535U, 2147483647U)] + public uint Value { get; set; } + + [Benchmark(Baseline = true)] + public uint ModuloOperator() + { + return Value % 10U; + } + + [Benchmark] + public uint ExtensionMethod() + { + return Value.Mod10(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod10_8_5_3Benchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod10_8_5_3Benchmarks.cs index cd00023..99aa38d 100644 --- a/EvenPerfectBitScanner.Benchmarks/Mod10_8_5_3Benchmarks.cs +++ b/EvenPerfectBitScanner.Benchmarks/Mod10_8_5_3Benchmarks.cs @@ -11,37 +11,44 @@ public class Mod10_8_5_3Benchmarks public ulong Value { get; set; } [Benchmark(Baseline = true)] + public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) ModuloOperator() + { + ulong value = Value; + return (value % 10UL, value % 8UL, value % 3UL, value % 5UL); + } + + [Benchmark] public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) LegacyModulo() { - ulong mod10, mod8, mod3, mod5; - UInt128 zero = UInt128.Zero; - if (Value == zero) - { - mod3 = 0UL; - mod5 = 0UL; - mod8 = 0UL; - mod10 = 0UL; - return (mod10, mod8, mod3, mod5); - } + ulong mod10, mod8, mod3, mod5; + UInt128 zero = UInt128.Zero; + if (Value == zero) + { + mod3 = 0UL; + mod5 = 0UL; + mod8 = 0UL; + mod10 = 0UL; + return (mod10, mod8, mod3, mod5); + } - UInt128 high = Value >> 64; - ulong result = Value; - mod8 = result & 7UL; - // 2^64 ≡ 1 (mod 3) - ulong modRem = (result % 3UL) + ((ulong)high % 3UL); - mod3 = modRem >= 3UL ? modRem - 3UL : modRem; - // 2^64 ≡ 1 (mod 5) - modRem = (result % 5UL) + ((ulong)high % 5UL); - mod5 = modRem >= 5UL ? modRem - 5UL : modRem; + UInt128 high = Value >> 64; + ulong result = Value; + mod8 = result & 7UL; + // 2^64 ≡ 1 (mod 3) + ulong modRem = (result % 3UL) + ((ulong)high % 3UL); + mod3 = modRem >= 3UL ? modRem - 3UL : modRem; + // 2^64 ≡ 1 (mod 5) + modRem = (result % 5UL) + ((ulong)high % 5UL); + mod5 = modRem >= 5UL ? modRem - 5UL : modRem; - while (high != zero) - { - // 2^64 ≡ 6 (mod 10) - result = (result + (ulong)high * 6UL) % 10UL; - high >>= 64; - } + while (high != zero) + { + // 2^64 ≡ 6 (mod 10) + result = (result + (ulong)high * 6UL) % 10UL; + high >>= 64; + } - mod10 = result % 10UL; + mod10 = result % 10UL; return (mod10, mod8, mod3, mod5); } diff --git a/EvenPerfectBitScanner.Benchmarks/Mod10_8_5_3StepsBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod10_8_5_3StepsBenchmarks.cs index 4934cb0..da08b13 100644 --- a/EvenPerfectBitScanner.Benchmarks/Mod10_8_5_3StepsBenchmarks.cs +++ b/EvenPerfectBitScanner.Benchmarks/Mod10_8_5_3StepsBenchmarks.cs @@ -26,7 +26,7 @@ public class Mod10_8_5_3StepsBenchmarks { 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(); + mod10 %= 10UL; return (mod10, mod8, mod3, mod5); } } diff --git a/EvenPerfectBitScanner.Benchmarks/Mod11UInt128Benchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod11UInt128Benchmarks.cs new file mode 100644 index 0000000..635f814 --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod11UInt128Benchmarks.cs @@ -0,0 +1,27 @@ +using System.Numerics; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)] +[MemoryDiagnoser] +public class Mod11UInt128Benchmarks +{ + [Params(11UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 255UL)] + public UInt128 Value { get; set; } + + [Benchmark(Baseline = true)] + public ulong ModuloOperator() + { + return (ulong)(Value % 11UL); + } + + [Benchmark] + public ulong ExtensionMethod() + { + return Value.Mod11(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod11UIntBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod11UIntBenchmarks.cs new file mode 100644 index 0000000..f16e36b --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod11UIntBenchmarks.cs @@ -0,0 +1,26 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)] +[MemoryDiagnoser] +public class Mod11UIntBenchmarks +{ + [Params(11U, 2047U, 65535U, 2147483647U)] + public uint Value { get; set; } + + [Benchmark(Baseline = true)] + public uint ModuloOperator() + { + return Value % 11U; + } + + [Benchmark] + public uint ExtensionMethod() + { + return Value.Mod11(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod11ULongBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod11ULongBenchmarks.cs new file mode 100644 index 0000000..1c450ce --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod11ULongBenchmarks.cs @@ -0,0 +1,26 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)] +[MemoryDiagnoser] +public class Mod11ULongBenchmarks +{ + [Params(11UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 127UL)] + public ulong Value { get; set; } + + [Benchmark(Baseline = true)] + public ulong ModuloOperator() + { + return Value % 11UL; + } + + [Benchmark] + public ulong ExtensionMethod() + { + return Value.Mod11(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod11_7_5_3Benchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod11_7_5_3Benchmarks.cs new file mode 100644 index 0000000..599d1fa --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod11_7_5_3Benchmarks.cs @@ -0,0 +1,32 @@ +using BenchmarkDotNet.Attributes; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[MemoryDiagnoser] +public class Mod11_7_5_3Benchmarks +{ + [Params(3UL, 8191UL, 131071UL, 2147483647UL)] + public ulong Value { get; set; } + + [Benchmark(Baseline = true)] + public (ulong Mod11, ulong Mod7, ulong Mod5, ulong Mod3) ModuloOperator() + { + ulong value = Value; + return (value % 11UL, value % 7UL, value % 5UL, value % 3UL); + } + + [Benchmark] + public (ulong Mod11, ulong Mod7, ulong Mod5, ulong Mod3) ExtensionMethods() + { + ulong value = Value; + return (value.Mod11(), value.Mod7(), value.Mod5(), value.Mod3()); + } + + [Benchmark] + public (ulong Mod11, ulong Mod7, ulong Mod5, ulong Mod3) CombinedMethod() + { + Value.Mod11_7_5_3(out ulong mod11, out ulong mod7, out ulong mod5, out ulong mod3); + return (mod11, mod7, mod5, mod3); + } +} diff --git a/EvenPerfectBitScanner.Benchmarks/Mod128UInt128Benchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod128UInt128Benchmarks.cs new file mode 100644 index 0000000..f50e7d0 --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod128UInt128Benchmarks.cs @@ -0,0 +1,27 @@ +using System.Numerics; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)] +[MemoryDiagnoser] +public class Mod128UInt128Benchmarks +{ + [Params(128UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 511UL)] + public UInt128 Value { get; set; } + + [Benchmark(Baseline = true)] + public ulong ModuloOperator() + { + return (ulong)(Value % 128UL); + } + + [Benchmark] + public ulong ExtensionMethod() + { + return Value.Mod128(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod128UIntBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod128UIntBenchmarks.cs new file mode 100644 index 0000000..a98d7da --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod128UIntBenchmarks.cs @@ -0,0 +1,26 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)] +[MemoryDiagnoser] +public class Mod128UIntBenchmarks +{ + [Params(128U, 2047U, 65535U, 2147483647U)] + public uint Value { get; set; } + + [Benchmark(Baseline = true)] + public uint ModuloOperator() + { + return Value % 128U; + } + + [Benchmark] + public uint ExtensionMethod() + { + return Value.Mod128(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod128ULongBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod128ULongBenchmarks.cs new file mode 100644 index 0000000..414245b --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod128ULongBenchmarks.cs @@ -0,0 +1,26 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)] +[MemoryDiagnoser] +public class Mod128ULongBenchmarks +{ + [Params(128UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 255UL)] + public ulong Value { get; set; } + + [Benchmark(Baseline = true)] + public ulong ModuloOperator() + { + return Value % 128UL; + } + + [Benchmark] + public ulong ExtensionMethod() + { + return Value.Mod128(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod3UInt128Benchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod3UInt128Benchmarks.cs new file mode 100644 index 0000000..ec6a28c --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod3UInt128Benchmarks.cs @@ -0,0 +1,27 @@ +using System.Numerics; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)] +[MemoryDiagnoser] +public class Mod3UInt128Benchmarks +{ + [Params(3UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 17UL)] + public UInt128 Value { get; set; } + + [Benchmark(Baseline = true)] + public ulong ModuloOperator() + { + return (ulong)(Value % 3UL); + } + + [Benchmark] + public ulong ExtensionMethod() + { + return Value.Mod3(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod3UIntBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod3UIntBenchmarks.cs new file mode 100644 index 0000000..bd81c8d --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod3UIntBenchmarks.cs @@ -0,0 +1,26 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)] +[MemoryDiagnoser] +public class Mod3UIntBenchmarks +{ + [Params(3U, 2047U, 65535U, 2147483647U)] + public uint Value { get; set; } + + [Benchmark(Baseline = true)] + public uint ModuloOperator() + { + return Value % 3U; + } + + [Benchmark] + public uint ExtensionMethod() + { + return Value.Mod3(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod3ULongBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod3ULongBenchmarks.cs new file mode 100644 index 0000000..b96b41d --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod3ULongBenchmarks.cs @@ -0,0 +1,26 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)] +[MemoryDiagnoser] +public class Mod3ULongBenchmarks +{ + [Params(3UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 17UL)] + public ulong Value { get; set; } + + [Benchmark(Baseline = true)] + public ulong ModuloOperator() + { + return Value % 3UL; + } + + [Benchmark] + public ulong ExtensionMethod() + { + return Value.Mod3(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod5UInt128Benchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod5UInt128Benchmarks.cs new file mode 100644 index 0000000..d56a0ab --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod5UInt128Benchmarks.cs @@ -0,0 +1,27 @@ +using System.Numerics; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)] +[MemoryDiagnoser] +public class Mod5UInt128Benchmarks +{ + [Params(5UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 31UL)] + public UInt128 Value { get; set; } + + [Benchmark(Baseline = true)] + public ulong ModuloOperator() + { + return (ulong)(Value % 5UL); + } + + [Benchmark] + public ulong ExtensionMethod() + { + return Value.Mod5(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod5UIntBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod5UIntBenchmarks.cs new file mode 100644 index 0000000..758ba40 --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod5UIntBenchmarks.cs @@ -0,0 +1,26 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)] +[MemoryDiagnoser] +public class Mod5UIntBenchmarks +{ + [Params(5U, 2047U, 65535U, 2147483647U)] + public uint Value { get; set; } + + [Benchmark(Baseline = true)] + public uint ModuloOperator() + { + return Value % 5U; + } + + [Benchmark] + public uint ExtensionMethod() + { + return Value.Mod5(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod5ULongBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod5ULongBenchmarks.cs new file mode 100644 index 0000000..d156306 --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod5ULongBenchmarks.cs @@ -0,0 +1,26 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)] +[MemoryDiagnoser] +public class Mod5ULongBenchmarks +{ + [Params(5UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 31UL)] + public ulong Value { get; set; } + + [Benchmark(Baseline = true)] + public ulong ModuloOperator() + { + return Value % 5UL; + } + + [Benchmark] + public ulong ExtensionMethod() + { + return Value.Mod5(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod6ComparisonBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod6ComparisonBenchmarks.cs new file mode 100644 index 0000000..fc9ea39 --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod6ComparisonBenchmarks.cs @@ -0,0 +1,81 @@ +using System.Numerics; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 3)] +[MemoryDiagnoser] +public class Mod6ComparisonBenchmarks +{ + private const int Iterations = 10_000_000; + private const ulong Divisor = 6UL; + private static readonly ulong FastDivMul = (ulong)(((UInt128)1 << 64) / Divisor); + + [Params(3UL, 131071UL, ulong.MaxValue - 1024UL)] + public ulong StartValue { get; set; } + + [Benchmark(Baseline = true, OperationsPerInvoke = Iterations)] + public ulong ModuloOperator() + { + ulong checksum = 0UL; + ulong value = StartValue; + + for (int i = 0; i < Iterations; i++) + { + checksum += value % Divisor; + value += (ulong)((i & 15) + 1); + } + + return checksum; + } + + [Benchmark(OperationsPerInvoke = Iterations)] + public ulong DivisionBased() + { + ulong checksum = 0UL; + ulong value = StartValue; + + for (int i = 0; i < Iterations; i++) + { + ulong quotient = value / Divisor; + checksum += value - quotient * Divisor; + value += (ulong)((i & 15) + 1); + } + + return checksum; + } + + [Benchmark(OperationsPerInvoke = Iterations)] + public ulong FastDivHigh() + { + ulong checksum = 0UL; + ulong value = StartValue; + + for (int i = 0; i < Iterations; i++) + { + ulong quotient = value.FastDiv64(Divisor, FastDivMul); + checksum += value - quotient * Divisor; + value += (ulong)((i & 15) + 1); + } + + return checksum; + } + + [Benchmark(OperationsPerInvoke = Iterations)] + public ulong ExtensionMethod() + { + ulong checksum = 0UL; + ulong value = StartValue; + + for (int i = 0; i < Iterations; i++) + { + checksum += value.Mod6(); + value += (ulong)((i & 15) + 1); + } + + return checksum; + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod6UInt128Benchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod6UInt128Benchmarks.cs new file mode 100644 index 0000000..4e03ac7 --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod6UInt128Benchmarks.cs @@ -0,0 +1,27 @@ +using System.Numerics; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)] +[MemoryDiagnoser] +public class Mod6UInt128Benchmarks +{ + [Params(6UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 63UL)] + public UInt128 Value { get; set; } + + [Benchmark(Baseline = true)] + public ulong ModuloOperator() + { + return (ulong)(Value % 6UL); + } + + [Benchmark] + public ulong ExtensionMethod() + { + return Value.Mod6(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod6UIntBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod6UIntBenchmarks.cs new file mode 100644 index 0000000..765f8e1 --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod6UIntBenchmarks.cs @@ -0,0 +1,26 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)] +[MemoryDiagnoser] +public class Mod6UIntBenchmarks +{ + [Params(6U, 2047U, 65535U, 2147483647U)] + public uint Value { get; set; } + + [Benchmark(Baseline = true)] + public uint ModuloOperator() + { + return Value % 6U; + } + + [Benchmark] + public uint ExtensionMethod() + { + return Value.Mod6(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod6ULongBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod6ULongBenchmarks.cs new file mode 100644 index 0000000..b8af8e7 --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod6ULongBenchmarks.cs @@ -0,0 +1,43 @@ +using System.Numerics; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 3)] +[MemoryDiagnoser] +public class Mod6ULongBenchmarks +{ + private static readonly ulong Divisor = 6UL; + private static readonly ulong FastDivMul = (ulong)(((UInt128)1 << 64) / Divisor); + + [Params(3UL, 131071UL, ulong.MaxValue)] + public ulong Value { get; set; } + + [Benchmark(Baseline = true)] + public ulong ModuloOperator() + { + return Value % Divisor; + } + + [Benchmark] + public ulong DivisionBased() + { + ulong quotient = Value / Divisor; + return Value - quotient * Divisor; + } + + [Benchmark] + public ulong FastDivHigh() + { + ulong quotient = Value.FastDiv64(Divisor, FastDivMul); + return Value - quotient * Divisor; + } + + [Benchmark] + public ulong ExtensionMethod() + { + return Value.Mod6(); + } +} diff --git a/EvenPerfectBitScanner.Benchmarks/Mod7UInt128Benchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod7UInt128Benchmarks.cs new file mode 100644 index 0000000..cb8aae9 --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod7UInt128Benchmarks.cs @@ -0,0 +1,27 @@ +using System.Numerics; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)] +[MemoryDiagnoser] +public class Mod7UInt128Benchmarks +{ + [Params(7UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 127UL)] + public UInt128 Value { get; set; } + + [Benchmark(Baseline = true)] + public ulong ModuloOperator() + { + return (ulong)(Value % 7UL); + } + + [Benchmark] + public ulong ExtensionMethod() + { + return Value.Mod7(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod7UIntBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod7UIntBenchmarks.cs new file mode 100644 index 0000000..4d8faac --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod7UIntBenchmarks.cs @@ -0,0 +1,26 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)] +[MemoryDiagnoser] +public class Mod7UIntBenchmarks +{ + [Params(7U, 2047U, 65535U, 2147483647U)] + public uint Value { get; set; } + + [Benchmark(Baseline = true)] + public uint ModuloOperator() + { + return Value % 7U; + } + + [Benchmark] + public uint ExtensionMethod() + { + return Value.Mod7(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod7ULongBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod7ULongBenchmarks.cs new file mode 100644 index 0000000..b59bdd4 --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod7ULongBenchmarks.cs @@ -0,0 +1,26 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)] +[MemoryDiagnoser] +public class Mod7ULongBenchmarks +{ + [Params(7UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 63UL)] + public ulong Value { get; set; } + + [Benchmark(Baseline = true)] + public ulong ModuloOperator() + { + return Value % 7UL; + } + + [Benchmark] + public ulong ExtensionMethod() + { + return Value.Mod7(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod8UInt128Benchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod8UInt128Benchmarks.cs new file mode 100644 index 0000000..ee2c7cf --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod8UInt128Benchmarks.cs @@ -0,0 +1,27 @@ +using System.Numerics; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)] +[MemoryDiagnoser] +public class Mod8UInt128Benchmarks +{ + [Params(8UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 255UL)] + public UInt128 Value { get; set; } + + [Benchmark(Baseline = true)] + public ulong ModuloOperator() + { + return (ulong)(Value % 8UL); + } + + [Benchmark] + public ulong ExtensionMethod() + { + return Value.Mod8(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod8UIntBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod8UIntBenchmarks.cs new file mode 100644 index 0000000..72e0e4a --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod8UIntBenchmarks.cs @@ -0,0 +1,26 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)] +[MemoryDiagnoser] +public class Mod8UIntBenchmarks +{ + [Params(8U, 2047U, 65535U, 2147483647U)] + public uint Value { get; set; } + + [Benchmark(Baseline = true)] + public uint ModuloOperator() + { + return Value % 8U; + } + + [Benchmark] + public uint ExtensionMethod() + { + return Value.Mod8(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Mod8ULongBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Mod8ULongBenchmarks.cs new file mode 100644 index 0000000..6af8ff5 --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/Mod8ULongBenchmarks.cs @@ -0,0 +1,26 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)] +[MemoryDiagnoser] +public class Mod8ULongBenchmarks +{ + [Params(8UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 127UL)] + public ulong Value { get; set; } + + [Benchmark(Baseline = true)] + public ulong ModuloOperator() + { + return Value % 8UL; + } + + [Benchmark] + public ulong ExtensionMethod() + { + return Value.Mod8(); + } +} + diff --git a/EvenPerfectBitScanner.Benchmarks/Program.cs b/EvenPerfectBitScanner.Benchmarks/Program.cs index 7779cdc..fd14baf 100644 --- a/EvenPerfectBitScanner.Benchmarks/Program.cs +++ b/EvenPerfectBitScanner.Benchmarks/Program.cs @@ -1,3 +1,4 @@ +using System; using BenchmarkDotNet.Running; namespace EvenPerfectBitScanner.Benchmarks; diff --git a/EvenPerfectBitScanner/Program.cs b/EvenPerfectBitScanner/Program.cs index d32d991..1e04e95 100644 --- a/EvenPerfectBitScanner/Program.cs +++ b/EvenPerfectBitScanner/Program.cs @@ -519,7 +519,7 @@ internal static class Program RleBlacklist.Load(_rleBlacklistPath!); } - ulong remainder = currentP % 6UL; + ulong remainder = currentP % 6UL; if (currentP == InitialP && string.IsNullOrEmpty(filterFile)) { // bool passedAllTests = IsEvenPerfectCandidate(InitialP, out bool searchedMersenne, out bool detailedCheck); @@ -1595,7 +1595,7 @@ internal static class Program // Optional: RLE blacklist and binary-threshold filters on p (safe only when configured) if (p <= _rleHardMaxP) { - if (!_rleOnlyLast7 || p.Mod10() == 7UL) + if (!_rleOnlyLast7 || p % 10UL == 7UL) { if (RleBlacklist.IsLoaded() && RleBlacklist.Matches(p)) { diff --git a/PerfectNumbers.Core.Tests/UInt128Extensions/UInt128ExtensionsTests.cs b/PerfectNumbers.Core.Tests/UInt128Extensions/UInt128ExtensionsTests.cs index 22beea2..80916da 100644 --- a/PerfectNumbers.Core.Tests/UInt128Extensions/UInt128ExtensionsTests.cs +++ b/PerfectNumbers.Core.Tests/UInt128Extensions/UInt128ExtensionsTests.cs @@ -201,6 +201,14 @@ public class UInt128ExtensionsTests value.Mod3().Should().Be((ulong)(value % 3)); } + [Fact] + [Trait("Category", "Fast")] + public void Mod6_matches_operator() + { + UInt128 value = (UInt128.One << 100) + 9876543210UL; + value.Mod6().Should().Be((ulong)(value % 6)); + } + [Fact] [Trait("Category", "Fast")] public void Mod5_matches_operator() diff --git a/PerfectNumbers.Core.Tests/UIntExtensionsTests.cs b/PerfectNumbers.Core.Tests/UIntExtensionsTests.cs index 48d6ae1..7a221a1 100644 --- a/PerfectNumbers.Core.Tests/UIntExtensionsTests.cs +++ b/PerfectNumbers.Core.Tests/UIntExtensionsTests.cs @@ -27,6 +27,17 @@ public class UIntExtensionsTests value.Mod5().Should().Be(value % 5U); } + [Theory] + [Trait("Category", "Fast")] + [InlineData(0U)] + [InlineData(1U)] + [InlineData(1234567890U)] + [InlineData(uint.MaxValue)] + public void Mod6_matches_operator(uint value) + { + value.Mod6().Should().Be(value % 6U); + } + [Theory] [Trait("Category", "Fast")] [InlineData(0U)] diff --git a/PerfectNumbers.Core.Tests/ULongExtensionsTests.cs b/PerfectNumbers.Core.Tests/ULongExtensionsTests.cs index bd67e46..7bf7e60 100644 --- a/PerfectNumbers.Core.Tests/ULongExtensionsTests.cs +++ b/PerfectNumbers.Core.Tests/ULongExtensionsTests.cs @@ -41,6 +41,16 @@ public class ULongExtensionsTests value.Mod5().Should().Be(value % 5UL); } + [Theory] + [Trait("Category", "Fast")] + [InlineData(0UL)] + [InlineData(ulong.MaxValue)] + [InlineData(1234567890123456789UL)] + public void Mod6_matches_operator(ulong value) + { + value.Mod6().Should().Be(value % 6UL); + } + [Theory] [Trait("Category", "Fast")] [InlineData(0UL)] @@ -101,6 +111,22 @@ public class ULongExtensionsTests step3.Should().Be(expected3); } + [Theory] + [Trait("Category", "Fast")] + [InlineData(0UL)] + [InlineData(1UL)] + [InlineData(1234567890123456789UL)] + [InlineData(ulong.MaxValue)] + public void Mod11_7_5_3_matches_operator(ulong value) + { + value.Mod11_7_5_3(out ulong mod11, out ulong mod7, out ulong mod5, out ulong mod3); + + mod11.Should().Be(value % 11UL); + mod7.Should().Be(value % 7UL); + mod5.Should().Be(value % 5UL); + mod3.Should().Be(value % 3UL); + } + [Theory] [Trait("Category", "Fast")] [InlineData(2UL, true)] diff --git a/PerfectNumbers.Core/Cpu/MersenneNumberLucasLehmerCpuTester.cs b/PerfectNumbers.Core/Cpu/MersenneNumberLucasLehmerCpuTester.cs index ec4b827..bbc5785 100644 --- a/PerfectNumbers.Core/Cpu/MersenneNumberLucasLehmerCpuTester.cs +++ b/PerfectNumbers.Core/Cpu/MersenneNumberLucasLehmerCpuTester.cs @@ -8,12 +8,16 @@ public class MersenneNumberLucasLehmerCpuTester // - If 3 | p and p != 3, then 7 | M_p -> composite. // - If p ≡ 1 (mod 4) and p shares a factor with (p-1), reject fast. // - If p is divisible by divisors specific to numbers ending with 1 or 7, reject fast. - if ( - ((exponent % 3UL) == 0UL && exponent != 3UL) || - ((exponent % 5UL) == 0UL && exponent != 5UL) || - ((exponent % 7UL) == 0UL && exponent != 7UL) || - ((exponent % 11UL) == 0UL && exponent != 11UL) - ) + ulong mod3 = exponent.Mod3(); + ulong mod5 = exponent.Mod5(); + ulong mod7 = exponent.Mod7(); + ulong mod11 = exponent.Mod11(); + if ( + (mod3 == 0UL && exponent != 3UL) || + (mod5 == 0UL && exponent != 5UL) || + (mod7 == 0UL && exponent != 7UL) || + (mod11 == 0UL && exponent != 11UL) + ) { return false; } diff --git a/PerfectNumbers.Core/Cpu/MersenneNumberResidueCpuTester.cs b/PerfectNumbers.Core/Cpu/MersenneNumberResidueCpuTester.cs index d919835..2e13805 100644 --- a/PerfectNumbers.Core/Cpu/MersenneNumberResidueCpuTester.cs +++ b/PerfectNumbers.Core/Cpu/MersenneNumberResidueCpuTester.cs @@ -21,7 +21,7 @@ public class MersenneNumberResidueCpuTester q.Mod10_8_5_3(out ulong r10, out ulong r8, out ulong r5, out ulong r3); // Step residues for q += 2*p twoP.Mod10_8_5_3(out ulong step10, out ulong step8, out ulong step5, out ulong step3); - step10 = step10.Mod10(); + step10 %= 10UL; // Allowed last-digit sets for q depending on last digit of M_p // lastIsSeven == true => allow {7,9} diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberLucasLehmerGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberLucasLehmerGpuTester.cs index f169ce7..f978eaa 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberLucasLehmerGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberLucasLehmerGpuTester.cs @@ -51,7 +51,7 @@ public class MersenneNumberLucasLehmerGpuTester // Early rejections aligned with incremental/order sieves, but safe for small p: // - If 3 | p and p != 3, then 7 | M_p -> composite. // - If p ≡ 1 (mod 4) and p shares a factor with (p-1), reject fast. - if ((exponent % 3UL) == 0UL && exponent != 3UL) + if (exponent % 3UL == 0UL && exponent != 3UL) { return false; } @@ -548,7 +548,7 @@ public class MersenneNumberLucasLehmerGpuTester } } - int topBits = (int)(exponent % 128UL); + int topBits = (int)exponent.Mod128(); if (topBits == 0) { topBits = 128; @@ -672,7 +672,7 @@ public class MersenneNumberLucasLehmerGpuTester } } - int topBits = (int)(exponent % 128UL); + int topBits = (int)exponent.Mod128(); if (topBits == 0) { topBits = 128; @@ -803,7 +803,7 @@ public class MersenneNumberLucasLehmerGpuTester vLow.Add(vHigh); // Mask to keep only p low bits and fold the carry bits (v >> p) - int topBits = (int)(p % 128UL); + int topBits = (int)p.Mod128(); if (topBits == 0) { topBits = 128; diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs index 9e500e5..c4dedd9 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs @@ -27,10 +27,10 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO 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 step10 = (exponentMod10 << 1) % 10UL; ulong step8 = (exponentMod8 << 1) & 7UL; - ulong step3 = (exponentMod3 << 1) % 3UL; - ulong step5 = (exponentMod5 << 1) % 5UL; + ulong step3 = (exponentMod3 << 1) % 3UL; + ulong step5 = (exponentMod5 << 1) % 5UL; try { UInt128 remaining; diff --git a/PerfectNumbers.Core/MersenneNumberTester.cs b/PerfectNumbers.Core/MersenneNumberTester.cs index c6833fe..a3b7c1c 100644 --- a/PerfectNumbers.Core/MersenneNumberTester.cs +++ b/PerfectNumbers.Core/MersenneNumberTester.cs @@ -67,7 +67,7 @@ public sealed class MersenneNumberTester( for (; k <= limit; k++) { q = twoP * k + UInt128.One; - remainder = q.Mod10(); + remainder = (ulong)(q % 10UL); bool shouldCheck = lastIsSeven ? remainder == 7UL || remainder == 9UL || remainder == 3UL : remainder == 1UL || remainder == 3UL || remainder == 9UL; @@ -75,12 +75,12 @@ public sealed class MersenneNumberTester( // Early modular filters for odd order and small-prime rejections if (shouldCheck) { - ulong mod8 = q.Mod8(); + ulong mod8 = (ulong)(q % 8UL); if (mod8 != 1UL && mod8 != 7UL) { continue; } - else if (q.Mod3() == 0UL || q.Mod5() == 0UL) + else if ((ulong)(q % 3UL) == 0UL || (ulong)(q % 5UL) == 0UL) { continue; } @@ -106,7 +106,7 @@ public sealed class MersenneNumberTester( for (; k2 <= limit; k2++) { q2 = twoP * k2 + 1UL; - remainder2 = q2.Mod10(); + remainder2 = (ulong)(q2 % 10UL); bool shouldCheck2 = lastIsSeven ? remainder2 == 7UL || remainder2 == 9UL || remainder2 == 3UL : remainder2 == 1UL || remainder2 == 3UL || remainder2 == 9UL; @@ -114,12 +114,12 @@ public sealed class MersenneNumberTester( // Early modular filters for odd order and small-prime rejections if (shouldCheck2) { - ulong mod8 = q2.Mod8(); + ulong mod8 = (ulong)(q2 % 8UL); if (mod8 != 1UL && mod8 != 7UL) { shouldCheck2 = false; } - else if (q2.Mod3() == 0UL || q2.Mod5() == 0UL) + else if ((ulong)(q2 % 3UL) == 0UL || (ulong)(q2 % 5UL) == 0UL) { shouldCheck2 = false; } @@ -203,7 +203,7 @@ public sealed class MersenneNumberTester( { // Safe early rejections using known orders for tiny primes: // 7 | M_p iff 3 | p. Avoid rejecting p == 3 where M_3 == 7 is prime. - if ((exponent % 3UL) == 0UL && exponent != 3UL) + if (exponent % 3UL == 0UL && exponent != 3UL) { return false; } diff --git a/PerfectNumbers.Core/PerfectNumbersMath.cs b/PerfectNumbers.Core/PerfectNumbersMath.cs deleted file mode 100644 index 3b22c50..0000000 --- a/PerfectNumbers.Core/PerfectNumbersMath.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace PerfectNumbers.Core; - -public static class PerfectNumbersMath -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint FastRemainder3(uint value) - { - uint quotient = (uint)(((ulong)value * 0xAAAAAAABUL) >> 33); - return value - quotient * 3U; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint FastRemainder5(uint value) - { - uint quotient = (uint)(((ulong)value * 0xCCCCCCCDUL) >> 34); - return value - quotient * 5U; - } - -} \ No newline at end of file diff --git a/PerfectNumbers.Core/PrimeTester.cs b/PerfectNumbers.Core/PrimeTester.cs index 8e9fde4..0351167 100644 --- a/PerfectNumbers.Core/PrimeTester.cs +++ b/PerfectNumbers.Core/PrimeTester.cs @@ -53,13 +53,13 @@ public sealed class PrimeTester(bool useInternal = false) { result = false; } - else if (n > 5UL && (n % 5UL) == 0UL) + else if (n > 5UL && n % 5UL == 0UL) { result = false; } else { - if (n.Mod10() == 1UL && SharesFactorWithMaxExponent(n)) + if (n % 10UL == 1UL && SharesFactorWithMaxExponent(n)) { result = false; } @@ -260,13 +260,13 @@ public sealed class PrimeTester(bool useInternal = false) return; } - if ((n & 1UL) == 0UL || (n > 5UL && (n % 5UL) == 0UL)) + if ((n & 1UL) == 0UL || (n > 5UL && n % 5UL == 0UL)) { results[index] = 0; return; } - if (n.Mod10() == 1UL) + if (n % 10UL == 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 afe2408..5714122 100644 --- a/PerfectNumbers.Core/PrimesGenerator.cs +++ b/PerfectNumbers.Core/PrimesGenerator.cs @@ -100,14 +100,14 @@ public static class PrimesGenerator [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsAllowedForLastOne(uint prime) { - uint mod10 = prime % 10U; + 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; + 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 d3b3bd0..ecfde88 100644 --- a/PerfectNumbers.Core/UInt128Extensions.cs +++ b/PerfectNumbers.Core/UInt128Extensions.cs @@ -135,8 +135,8 @@ public static class UInt128Extensions mod8 = (ulong)value & 7UL; - uint mod5Value = PerfectNumbersMath.FastRemainder5(byteSum); - uint mod3Value = PerfectNumbersMath.FastRemainder3(byteSum); + uint mod5Value = byteSum.Mod5(); + uint mod3Value = byteSum.Mod3(); mod10 = (mod8 & 1UL) == 0UL ? mod5Value switch @@ -238,6 +238,22 @@ public static class UInt128Extensions return remainder >= 5UL ? remainder - 5UL : remainder; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong Mod6(this UInt128 value) + { + ulong mod3 = value.Mod3(); + ulong key = (mod3 << 1) | ((ulong)value & 1UL); + return key switch + { + 0UL => 0UL, + 1UL => 3UL, + 2UL => 4UL, + 3UL => 1UL, + 4UL => 2UL, + _ => 5UL, + }; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong Mod7(this UInt128 value) { diff --git a/PerfectNumbers.Core/UIntExtensions.cs b/PerfectNumbers.Core/UIntExtensions.cs index 8884f01..a43e191 100644 --- a/PerfectNumbers.Core/UIntExtensions.cs +++ b/PerfectNumbers.Core/UIntExtensions.cs @@ -4,57 +4,74 @@ namespace PerfectNumbers.Core; public static class UIntExtensions { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint Mod3(this uint value) => PerfectNumbersMath.FastRemainder3(value); + private static readonly byte[] Mod6Lookup = { 0, 3, 4, 1, 2, 5 }; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint Mod5(this uint value) => PerfectNumbersMath.FastRemainder5(value); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Mod3(this uint value) + { + uint quotient = (uint)(((ulong)value * 0xAAAAAAABUL) >> 33); + return value - quotient * 3U; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint Mod7(this uint value) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Mod5(this uint value) + { + uint quotient = (uint)(((ulong)value * 0xCCCCCCCDUL) >> 34); + return value - quotient * 5U; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Mod6(this uint value) + { + uint key = (value.Mod3() << 1) | (value & 1U); + return Mod6Lookup[(int)key]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Mod7(this uint value) + { + uint remainder = 0U; + uint temp = value; + + while (temp != 0U) { - uint remainder = 0U; - uint temp = value; - - while (temp != 0U) - { - remainder += temp & 7U; - temp >>= 3; - if (remainder >= 7U) - { - remainder -= 7U; - } - } - - return remainder >= 7U ? remainder - 7U : remainder; + remainder += temp & 7U; + temp >>= 3; + if (remainder >= 7U) + { + remainder -= 7U; + } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint Mod8(this uint value) => value & 7U; + return remainder >= 7U ? remainder - 7U : remainder; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint Mod10(this uint value) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Mod8(this uint value) => value & 7U; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Mod10(this uint value) + { + return value - (uint)(((ulong)value * 0xCCCCCCCDUL) >> 35) * 10U; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Mod11(this uint value) + { + uint remainder = 0U; + uint temp = value; + + while (temp != 0U) { - return value - (uint)(((ulong)value * 0xCCCCCCCDUL) >> 35) * 10U; + remainder += temp & 1023U; + temp >>= 10; + remainder -= 11U * (remainder / 11U); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint Mod11(this uint value) - { - uint remainder = 0U; - uint temp = value; + return remainder; + } - while (temp != 0U) - { - remainder += temp & 1023U; - temp >>= 10; - remainder -= 11U * (remainder / 11U); - } - - return remainder; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint Mod128(this uint value) => value & 127U; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Mod128(this uint value) => value & 127U; } diff --git a/PerfectNumbers.Core/ULongExtensions.cs b/PerfectNumbers.Core/ULongExtensions.cs index 5326e43..eea9e9f 100644 --- a/PerfectNumbers.Core/ULongExtensions.cs +++ b/PerfectNumbers.Core/ULongExtensions.cs @@ -57,7 +57,11 @@ public static class ULongExtensions return q; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static readonly byte[] Mod6Lookup = { 0, 3, 4, 1, 2, 5 }; + private static readonly byte[] Mod7ByteCoefficients = { 1, 4, 2, 1, 4, 2, 1, 4 }; + private static readonly byte[] Mod11ByteCoefficients = { 1, 3, 9, 5, 4, 1, 3, 9 }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Mod10_8_5_3(this ulong value, out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3) { ulong temp = value; @@ -72,8 +76,8 @@ public static class ULongExtensions mod8 = value & 7UL; - uint mod5Value = PerfectNumbersMath.FastRemainder5(byteSum); - uint mod3Value = PerfectNumbersMath.FastRemainder3(byteSum); + uint mod5Value = byteSum.Mod5(); + uint mod3Value = byteSum.Mod3(); mod10 = (mod8 & 1UL) == 0UL ? mod5Value switch @@ -97,6 +101,35 @@ public static class ULongExtensions mod3 = mod3Value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Mod11_7_5_3(this ulong value, out ulong mod11, out ulong mod7, out ulong mod5, out ulong mod3) + { + uint mod3Accumulator = 0U; + uint mod5Accumulator = 0U; + uint mod7Accumulator = 0U; + uint mod11Accumulator = 0U; + ulong temp = value; + int index = 0; + + do + { + byte current = (byte)temp; + mod3Accumulator += current; + mod5Accumulator += current; + mod7Accumulator += (uint)(current * Mod7ByteCoefficients[index]); + mod11Accumulator += (uint)(current * Mod11ByteCoefficients[index]); + + temp >>= 8; + index++; + } + while (temp != 0UL); + + mod3 = mod3Accumulator.Mod3(); + mod5 = mod5Accumulator.Mod5(); + mod7 = ReduceMod7(mod7Accumulator); + mod11 = ReduceMod11(mod11Accumulator); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Mod10_8_5_3Steps(this ulong value, out ulong step10, out ulong step8, out ulong step5, out ulong step3) { @@ -171,7 +204,7 @@ public static class ULongExtensions sum += (uint)((value >> 32) & 0xFFFFUL); sum += (uint)((value >> 48) & 0xFFFFUL); - return PerfectNumbersMath.FastRemainder3(sum); + return sum.Mod3(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -182,7 +215,14 @@ public static class ULongExtensions sum += (uint)((value >> 32) & 0xFFFFUL); sum += (uint)((value >> 48) & 0xFFFFUL); - return PerfectNumbersMath.FastRemainder5(sum); + return sum.Mod5(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong Mod6(this ulong value) + { + ulong key = (value.Mod3() << 1) | (value & 1UL); + return Mod6Lookup[(int)key]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -218,6 +258,36 @@ public static class ULongExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong Mod128(this ulong value) => value & 127UL; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong ReduceMod7(uint value) + { + while (value >= 7U) + { + value = (value >> 3) + (value & 7U); + if (value >= 7U && value < 14U) + { + value -= 7U; + } + } + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong ReduceMod11(uint value) + { + while (value >= 11U) + { + value = (value & 15U) + ((value >> 4) * 5U); + if (value >= 11U && value < 22U) + { + value -= 11U; + } + } + + return value; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static UInt128 Mul64(this ulong a, ulong b) => ((UInt128)a.MulHigh(b) << 64) | (UInt128)(a * b); From 3598f88aaf9b5cd6aa7cad97eee380ae851464e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Mon, 22 Sep 2025 19:54:50 +0200 Subject: [PATCH 2/4] Replace small-modulo uses with optimized helpers --- EvenPerfectBitScanner/Program.cs | 4 +-- .../Cpu/MersenneNumberResidueCpuTester.cs | 2 +- PerfectNumbers.Core/Gpu/GpuKernelPool.cs | 36 +++++++++++-------- .../Gpu/MersenneNumberIncrementalGpuTester.cs | 4 +-- .../Gpu/MersenneNumberLucasLehmerGpuTester.cs | 2 +- .../Gpu/MersenneNumberOrderGpuTester.cs | 8 ++--- PerfectNumbers.Core/MersenneDivisorCycles.cs | 6 ++-- PerfectNumbers.Core/MersenneNumberTester.cs | 14 ++++---- PerfectNumbers.Core/PrimeTester.cs | 8 ++--- 9 files changed, 46 insertions(+), 38 deletions(-) diff --git a/EvenPerfectBitScanner/Program.cs b/EvenPerfectBitScanner/Program.cs index 1e04e95..55a9508 100644 --- a/EvenPerfectBitScanner/Program.cs +++ b/EvenPerfectBitScanner/Program.cs @@ -519,7 +519,7 @@ internal static class Program RleBlacklist.Load(_rleBlacklistPath!); } - ulong remainder = currentP % 6UL; + ulong remainder = currentP.Mod6(); if (currentP == InitialP && string.IsNullOrEmpty(filterFile)) { // bool passedAllTests = IsEvenPerfectCandidate(InitialP, out bool searchedMersenne, out bool detailedCheck); @@ -1595,7 +1595,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 2e13805..0946177 100644 --- a/PerfectNumbers.Core/Cpu/MersenneNumberResidueCpuTester.cs +++ b/PerfectNumbers.Core/Cpu/MersenneNumberResidueCpuTester.cs @@ -21,7 +21,7 @@ public class MersenneNumberResidueCpuTester q.Mod10_8_5_3(out ulong r10, out ulong r8, out ulong r5, out ulong r3); // Step residues for q += 2*p twoP.Mod10_8_5_3(out ulong step10, out ulong step8, out ulong step5, out ulong step3); - step10 %= 10UL; + 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 3b91635..81a3cb4 100644 --- a/PerfectNumbers.Core/Gpu/GpuKernelPool.cs +++ b/PerfectNumbers.Core/Gpu/GpuKernelPool.cs @@ -200,8 +200,10 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne ArrayView1D smallCycles) { ulong idx = (ulong)index.X; - // residue automaton - ulong step10 = (exponent.Mod10() << 1).Mod10(); + ulong idxMod3 = idx.Mod3(); + ulong idxMod5 = idx.Mod5(); + // residue automaton + 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 @@ -215,16 +217,16 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne } else { - ulong step3 = ((exponent % 3UL) << 1) % 3UL; - ulong r3 = q0m3 + (step3 * (idx % 3UL)); r3 -= (r3 >= 3UL) ? 3UL : 0UL; + ulong step3 = (exponent.Mod3() << 1).Mod3(); + ulong r3 = q0m3 + (step3 * idxMod3); r3 -= (r3 >= 3UL) ? 3UL : 0UL; if (r3 == 0UL) { shouldCheck = false; } else { - ulong step5Loc = ((exponent % 5UL) << 1) % 5UL; - ulong r5 = q0m5 + (step5Loc * (idx % 5UL)); if (r5 >= 5UL) r5 -= 5UL; + ulong step5Loc = (exponent.Mod5() << 1).Mod5(); + ulong r5 = q0m5 + (step5Loc * idxMod5); if (r5 >= 5UL) r5 -= 5UL; if (r5 == 0UL) { shouldCheck = false; @@ -358,6 +360,8 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne ArrayView1D smallPrimesPow2LastSeven) { ulong idx = (ulong)index.X; + ulong idxMod3 = idx.Mod3(); + ulong idxMod5 = idx.Mod5(); ulong r10 = ra.Q0M10 + (ra.Step10 * idx).Mod10(); r10 -= (r10 >= 10UL) ? 10UL : 0UL; bool shouldCheck = r10 != 5UL; if (shouldCheck) @@ -369,7 +373,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne } else { - ulong r3 = ra.Q0M3 + (ra.Step3 * (idx % 3UL)); + ulong r3 = ra.Q0M3 + (ra.Step3 * idxMod3); if (r3 >= 6UL) r3 -= 6UL; if (r3 >= 3UL) r3 -= 3UL; if (r3 == 0UL) @@ -378,7 +382,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne } else { - ulong r5 = ra.Q0M5 + (ra.Step5 * (idx % 5UL)); + ulong r5 = ra.Q0M5 + (ra.Step5 * idxMod5); if (r5 >= 15UL) r5 -= 15UL; if (r5 >= 10UL) r5 -= 10UL; if (r5 >= 5UL) r5 -= 5UL; @@ -489,7 +493,9 @@ 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).Mod10(); r10 -= (r10 >= 10UL) ? 10UL : 0UL; + ulong idxMod3 = idx.Mod3(); + ulong idxMod5 = idx.Mod5(); + ulong r10 = ra.Q0M10 + (ra.Step10 * idx).Mod10(); r10 -= (r10 >= 10UL) ? 10UL : 0UL; bool shouldCheck = r10 != 5UL; if (shouldCheck) { @@ -500,7 +506,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne } else { - ulong r3 = ra.Q0M3 + (ra.Step3 * (idx % 3UL)); + ulong r3 = ra.Q0M3 + (ra.Step3 * idxMod3); if (r3 >= 6UL) r3 -= 6UL; if (r3 >= 3UL) r3 -= 3UL; if (r3 == 0UL) @@ -509,7 +515,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne } else { - ulong r5 = ra.Q0M5 + (ra.Step5 * (idx % 5UL)); + ulong r5 = ra.Q0M5 + (ra.Step5 * idxMod5); if (r5 >= 15UL) r5 -= 15UL; if (r5 >= 10UL) r5 -= 10UL; if (r5 >= 5UL) r5 -= 5UL; @@ -573,7 +579,9 @@ 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).Mod10(); r10 -= (r10 >= 10UL) ? 10UL : 0UL; + ulong idxMod3 = idx.Mod3(); + ulong idxMod5 = idx.Mod5(); + ulong r10 = ra.Q0M10 + (ra.Step10 * idx).Mod10(); r10 -= (r10 >= 10UL) ? 10UL : 0UL; bool shouldCheck = r10 != 5UL; if (shouldCheck) { @@ -584,7 +592,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne } else { - ulong r3 = ra.Q0M3 + (ra.Step3 * (idx % 3UL)); + ulong r3 = ra.Q0M3 + (ra.Step3 * idxMod3); if (r3 >= 6UL) r3 -= 6UL; if (r3 >= 3UL) r3 -= 3UL; if (r3 == 0UL) @@ -593,7 +601,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne } else { - ulong r5 = ra.Q0M5 + (ra.Step5 * (idx % 5UL)); + ulong r5 = ra.Q0M5 + (ra.Step5 * idxMod5); if (r5 >= 15UL) r5 -= 15UL; if (r5 >= 10UL) r5 -= 10UL; if (r5 >= 5UL) r5 -= 5UL; diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs index b2f639b..076f6f8 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs @@ -25,8 +25,8 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u var incKernel = gpuLease.IncrementalKernel; ulong step10 = (exponent.Mod10() << 1).Mod10(); ulong step8 = ((exponent & 7UL) << 1) & 7UL; - ulong step3 = ((exponent % 3UL) << 1) % 3UL; - ulong step5 = ((exponent % 5UL) << 1) % 5UL; + ulong step3 = (exponent.Mod3() << 1).Mod3(); + ulong step5 = (exponent.Mod5() << 1).Mod5(); GpuUInt128 twoPGpu = (GpuUInt128)twoP; var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator); ResiduePrimeViews primeViews = default; diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberLucasLehmerGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberLucasLehmerGpuTester.cs index f978eaa..0bdca4c 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberLucasLehmerGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberLucasLehmerGpuTester.cs @@ -51,7 +51,7 @@ public class MersenneNumberLucasLehmerGpuTester // Early rejections aligned with incremental/order sieves, but safe for small p: // - If 3 | p and p != 3, then 7 | M_p -> composite. // - If p ≡ 1 (mod 4) and p shares a factor with (p-1), reject fast. - if (exponent % 3UL == 0UL && exponent != 3UL) + if (exponent.Mod3() == 0UL && exponent != 3UL) { return false; } diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs index c4dedd9..0d1e738 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs @@ -27,10 +27,10 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO 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; + ulong step10 = (exponentMod10 << 1).Mod10(); + ulong step8 = (exponentMod8 << 1) & 7UL; + ulong step3 = (exponentMod3 << 1).Mod3(); + ulong step5 = (exponentMod5 << 1).Mod5(); try { UInt128 remaining; diff --git a/PerfectNumbers.Core/MersenneDivisorCycles.cs b/PerfectNumbers.Core/MersenneDivisorCycles.cs index d8dfc3f..edf7930 100644 --- a/PerfectNumbers.Core/MersenneDivisorCycles.cs +++ b/PerfectNumbers.Core/MersenneDivisorCycles.cs @@ -189,7 +189,7 @@ public class MersenneDivisorCycles for (int i = 0; i < localCycleIndex; i++) { (d, cycle) = localCycles[i]; - if (d % 3UL == 0UL || d % 5UL == 0UL || d % 7UL == 0UL || d % 11UL == 0UL) + if (d.Mod3() == 0UL || d.Mod5() == 0UL || d.Mod7() == 0UL || d.Mod11() == 0UL) continue; writer.Write(d); @@ -249,7 +249,7 @@ public class MersenneDivisorCycles { while (skipCount > 0L && start <= maxDivisor) { - if ((start & 1UL) == 0UL || start % 3UL == 0UL || start % 5UL == 0UL || start % 7UL == 0UL || start % 11UL == 0UL) + if ((start & 1UL) == 0UL || start.Mod3() == 0UL || start.Mod5() == 0UL || start.Mod7() == 0UL || start.Mod11() == 0UL) { start++; continue; @@ -296,7 +296,7 @@ public class MersenneDivisorCycles { d = divisors[i]; - if (d % 3UL == 0UL || d % 5UL == 0UL || d % 7UL == 0UL || d % 11UL == 0UL) + if (d.Mod3() == 0UL || d.Mod5() == 0UL || d.Mod7() == 0UL || d.Mod11() == 0UL) continue; writer.Write(d); diff --git a/PerfectNumbers.Core/MersenneNumberTester.cs b/PerfectNumbers.Core/MersenneNumberTester.cs index a3b7c1c..8f9fe6b 100644 --- a/PerfectNumbers.Core/MersenneNumberTester.cs +++ b/PerfectNumbers.Core/MersenneNumberTester.cs @@ -67,7 +67,7 @@ public sealed class MersenneNumberTester( for (; k <= limit; k++) { q = twoP * k + UInt128.One; - remainder = (ulong)(q % 10UL); + remainder = q.Mod10(); bool shouldCheck = lastIsSeven ? remainder == 7UL || remainder == 9UL || remainder == 3UL : remainder == 1UL || remainder == 3UL || remainder == 9UL; @@ -75,12 +75,12 @@ public sealed class MersenneNumberTester( // Early modular filters for odd order and small-prime rejections if (shouldCheck) { - ulong mod8 = (ulong)(q % 8UL); + ulong mod8 = q.Mod8(); if (mod8 != 1UL && mod8 != 7UL) { continue; } - else if ((ulong)(q % 3UL) == 0UL || (ulong)(q % 5UL) == 0UL) + else if (q.Mod3() == 0UL || q.Mod5() == 0UL) { continue; } @@ -106,7 +106,7 @@ public sealed class MersenneNumberTester( for (; k2 <= limit; k2++) { q2 = twoP * k2 + 1UL; - remainder2 = (ulong)(q2 % 10UL); + remainder2 = q2.Mod10(); bool shouldCheck2 = lastIsSeven ? remainder2 == 7UL || remainder2 == 9UL || remainder2 == 3UL : remainder2 == 1UL || remainder2 == 3UL || remainder2 == 9UL; @@ -114,12 +114,12 @@ public sealed class MersenneNumberTester( // Early modular filters for odd order and small-prime rejections if (shouldCheck2) { - ulong mod8 = (ulong)(q2 % 8UL); + ulong mod8 = q2.Mod8(); if (mod8 != 1UL && mod8 != 7UL) { shouldCheck2 = false; } - else if ((ulong)(q2 % 3UL) == 0UL || (ulong)(q2 % 5UL) == 0UL) + else if (q2.Mod3() == 0UL || q2.Mod5() == 0UL) { shouldCheck2 = false; } @@ -203,7 +203,7 @@ public sealed class MersenneNumberTester( { // Safe early rejections using known orders for tiny primes: // 7 | M_p iff 3 | p. Avoid rejecting p == 3 where M_3 == 7 is prime. - if (exponent % 3UL == 0UL && exponent != 3UL) + if (exponent.Mod3() == 0UL && exponent != 3UL) { return false; } diff --git a/PerfectNumbers.Core/PrimeTester.cs b/PerfectNumbers.Core/PrimeTester.cs index 0351167..fa962d8 100644 --- a/PerfectNumbers.Core/PrimeTester.cs +++ b/PerfectNumbers.Core/PrimeTester.cs @@ -53,13 +53,13 @@ public sealed class PrimeTester(bool useInternal = false) { result = false; } - else if (n > 5UL && n % 5UL == 0UL) + else if (n > 5UL && n.Mod5() == 0UL) { result = false; } else { - if (n % 10UL == 1UL && SharesFactorWithMaxExponent(n)) + if (n.Mod10() == 1UL && SharesFactorWithMaxExponent(n)) { result = false; } @@ -260,13 +260,13 @@ public sealed class PrimeTester(bool useInternal = false) return; } - if ((n & 1UL) == 0UL || (n > 5UL && n % 5UL == 0UL)) + if ((n & 1UL) == 0UL || (n > 5UL && n.Mod5() == 0UL)) { results[index] = 0; 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); From 3b8a10a20bd70ba37ec4d347f856b4d936a8cce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Mon, 22 Sep 2025 19:54:54 +0200 Subject: [PATCH 3/4] Inline single-use modulo helpers --- .../Cpu/MersenneNumberLucasLehmerCpuTester.cs | 24 +++++++-------- PerfectNumbers.Core/PrimesGenerator.cs | 30 +++++++++++-------- PerfectNumbers.Core/UInt128Extensions.cs | 6 ++-- PerfectNumbers.Core/UIntExtensions.cs | 3 +- PerfectNumbers.Core/ULongExtensions.cs | 5 ++-- 5 files changed, 33 insertions(+), 35 deletions(-) diff --git a/PerfectNumbers.Core/Cpu/MersenneNumberLucasLehmerCpuTester.cs b/PerfectNumbers.Core/Cpu/MersenneNumberLucasLehmerCpuTester.cs index bbc5785..93dddb5 100644 --- a/PerfectNumbers.Core/Cpu/MersenneNumberLucasLehmerCpuTester.cs +++ b/PerfectNumbers.Core/Cpu/MersenneNumberLucasLehmerCpuTester.cs @@ -4,20 +4,16 @@ public class MersenneNumberLucasLehmerCpuTester { public bool IsPrime(ulong exponent) { - // Early rejections aligned with incremental/order sieves, but safe for small p: - // - If 3 | p and p != 3, then 7 | M_p -> composite. - // - If p ≡ 1 (mod 4) and p shares a factor with (p-1), reject fast. - // - If p is divisible by divisors specific to numbers ending with 1 or 7, reject fast. - ulong mod3 = exponent.Mod3(); - ulong mod5 = exponent.Mod5(); - ulong mod7 = exponent.Mod7(); - ulong mod11 = exponent.Mod11(); - if ( - (mod3 == 0UL && exponent != 3UL) || - (mod5 == 0UL && exponent != 5UL) || - (mod7 == 0UL && exponent != 7UL) || - (mod11 == 0UL && exponent != 11UL) - ) + // Early rejections aligned with incremental/order sieves, but safe for small p: + // - If 3 | p and p != 3, then 7 | M_p -> composite. + // - If p ≡ 1 (mod 4) and p shares a factor with (p-1), reject fast. + // - If p is divisible by divisors specific to numbers ending with 1 or 7, reject fast. + if ( + (exponent.Mod3() == 0UL && exponent != 3UL) || + (exponent.Mod5() == 0UL && exponent != 5UL) || + (exponent.Mod7() == 0UL && exponent != 7UL) || + (exponent.Mod11() == 0UL && exponent != 11UL) + ) { return false; } diff --git a/PerfectNumbers.Core/PrimesGenerator.cs b/PerfectNumbers.Core/PrimesGenerator.cs index 5714122..a51e555 100644 --- a/PerfectNumbers.Core/PrimesGenerator.cs +++ b/PerfectNumbers.Core/PrimesGenerator.cs @@ -97,17 +97,23 @@ public static class PrimesGenerator } } - [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) + { + return (prime % 10U) switch + { + 1U or 3U or 9U => true, + _ => 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) + { + return (prime % 10U) switch + { + 3U or 7U or 9U => true, + _ => prime == 11U, + }; + } } diff --git a/PerfectNumbers.Core/UInt128Extensions.cs b/PerfectNumbers.Core/UInt128Extensions.cs index ecfde88..904d84c 100644 --- a/PerfectNumbers.Core/UInt128Extensions.cs +++ b/PerfectNumbers.Core/UInt128Extensions.cs @@ -241,9 +241,7 @@ public static class UInt128Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong Mod6(this UInt128 value) { - ulong mod3 = value.Mod3(); - ulong key = (mod3 << 1) | ((ulong)value & 1UL); - return key switch + return ((value.Mod3() << 1) | ((ulong)value & 1UL)) switch { 0UL => 0UL, 1UL => 3UL, @@ -318,4 +316,4 @@ public static class UInt128Extensions return result; } -} \ No newline at end of file +} diff --git a/PerfectNumbers.Core/UIntExtensions.cs b/PerfectNumbers.Core/UIntExtensions.cs index a43e191..d17afa0 100644 --- a/PerfectNumbers.Core/UIntExtensions.cs +++ b/PerfectNumbers.Core/UIntExtensions.cs @@ -23,8 +23,7 @@ public static class UIntExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Mod6(this uint value) { - uint key = (value.Mod3() << 1) | (value & 1U); - return Mod6Lookup[(int)key]; + return Mod6Lookup[(int)(((value.Mod3() << 1) | (value & 1U)))]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/PerfectNumbers.Core/ULongExtensions.cs b/PerfectNumbers.Core/ULongExtensions.cs index eea9e9f..a9f6837 100644 --- a/PerfectNumbers.Core/ULongExtensions.cs +++ b/PerfectNumbers.Core/ULongExtensions.cs @@ -221,8 +221,7 @@ public static class ULongExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong Mod6(this ulong value) { - ulong key = (value.Mod3() << 1) | (value & 1UL); - return Mod6Lookup[(int)key]; + return Mod6Lookup[(int)(((value.Mod3() << 1) | (value & 1UL)))]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -647,4 +646,4 @@ public static class ULongExtensions return false; } -} \ No newline at end of file +} From 762d205e864e5c85defc0eb1731cc90c88d94ab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Mon, 22 Sep 2025 19:54:59 +0200 Subject: [PATCH 4/4] Reuse local variables across modulo helpers --- PerfectNumbers.Core/UInt128Extensions.cs | 64 ++++----- PerfectNumbers.Core/UIntExtensions.cs | 46 +++++-- PerfectNumbers.Core/ULongExtensions.cs | 165 +++++++++++++++-------- 3 files changed, 178 insertions(+), 97 deletions(-) diff --git a/PerfectNumbers.Core/UInt128Extensions.cs b/PerfectNumbers.Core/UInt128Extensions.cs index 904d84c..99fda8a 100644 --- a/PerfectNumbers.Core/UInt128Extensions.cs +++ b/PerfectNumbers.Core/UInt128Extensions.cs @@ -121,44 +121,48 @@ public static class UInt128Extensions } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Mod10_8_5_3(this UInt128 value, out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3) - { - UInt128 temp = value; - uint byteSum = 0U; + public static void Mod10_8_5_3(this UInt128 value, out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3) + { + UInt128 temp = UInt128.Zero; + uint byteSum = 0U; + byte current = 0; + uint accumulator = 0U; - do - { - byteSum += (byte)temp; - temp >>= 8; - } - while (temp != UInt128.Zero); + temp = value; + do + { + current = (byte)temp; + byteSum += current; + temp >>= 8; + } + while (temp != UInt128.Zero); - mod8 = (ulong)value & 7UL; - - uint mod5Value = byteSum.Mod5(); - uint mod3Value = byteSum.Mod3(); + mod8 = (ulong)value & 7UL; + accumulator = byteSum.Mod5(); + mod5 = accumulator; mod10 = (mod8 & 1UL) == 0UL - ? mod5Value switch + ? accumulator switch { 0U => 0UL, 1U => 6UL, - 2U => 2UL, - 3U => 8UL, - _ => 4UL, - } - : mod5Value switch - { - 0U => 5UL, - 1U => 1UL, - 2U => 7UL, - 3U => 3UL, - _ => 9UL, - }; + 2U => 2UL, + 3U => 8UL, + _ => 4UL, + } + : accumulator switch + { + 0U => 5UL, + 1U => 1UL, + 2U => 7UL, + 3U => 3UL, + _ => 9UL, + }; - mod5 = mod5Value; - mod3 = mod3Value; - } + // Reusing accumulator to hold the mod 3 reduction now that the mod 5 value has been stored. + accumulator = byteSum.Mod3(); + mod3 = accumulator; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static UInt128 Pow(this UInt128 value, ulong exponent) diff --git a/PerfectNumbers.Core/UIntExtensions.cs b/PerfectNumbers.Core/UIntExtensions.cs index d17afa0..3706e18 100644 --- a/PerfectNumbers.Core/UIntExtensions.cs +++ b/PerfectNumbers.Core/UIntExtensions.cs @@ -9,15 +9,29 @@ public static class UIntExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Mod3(this uint value) { - uint quotient = (uint)(((ulong)value * 0xAAAAAAABUL) >> 33); - return value - quotient * 3U; + ulong wideValue = 0UL; + uint quotient = 0U; + uint remainder = 0U; + + wideValue = (ulong)value * 0xAAAAAAABUL; + quotient = (uint)(wideValue >> 33); + remainder = value - quotient * 3U; + + return remainder; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Mod5(this uint value) { - uint quotient = (uint)(((ulong)value * 0xCCCCCCCDUL) >> 34); - return value - quotient * 5U; + ulong wideValue = 0UL; + uint quotient = 0U; + uint remainder = 0U; + + wideValue = (ulong)value * 0xCCCCCCCDUL; + quotient = (uint)(wideValue >> 34); + remainder = value - quotient * 5U; + + return remainder; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -30,11 +44,14 @@ public static class UIntExtensions public static uint Mod7(this uint value) { uint remainder = 0U; - uint temp = value; + uint temp = 0U; + uint chunk = 0U; + temp = value; while (temp != 0U) { - remainder += temp & 7U; + chunk = temp & 7U; + remainder += chunk; temp >>= 3; if (remainder >= 7U) { @@ -51,18 +68,29 @@ public static class UIntExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Mod10(this uint value) { - return value - (uint)(((ulong)value * 0xCCCCCCCDUL) >> 35) * 10U; + ulong wideValue = 0UL; + uint quotient = 0U; + uint remainder = 0U; + + wideValue = (ulong)value * 0xCCCCCCCDUL; + quotient = (uint)(wideValue >> 35); + remainder = value - quotient * 10U; + + return remainder; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Mod11(this uint value) { uint remainder = 0U; - uint temp = value; + uint temp = 0U; + uint chunk = 0U; + temp = value; while (temp != 0U) { - remainder += temp & 1023U; + chunk = temp & 1023U; + remainder += chunk; temp >>= 10; remainder -= 11U * (remainder / 11U); } diff --git a/PerfectNumbers.Core/ULongExtensions.cs b/PerfectNumbers.Core/ULongExtensions.cs index a9f6837..61fa6f4 100644 --- a/PerfectNumbers.Core/ULongExtensions.cs +++ b/PerfectNumbers.Core/ULongExtensions.cs @@ -64,23 +64,26 @@ public static class ULongExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Mod10_8_5_3(this ulong value, out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3) { - ulong temp = value; + ulong temp = 0UL; uint byteSum = 0U; + byte current = 0; + uint accumulator = 0U; + temp = value; do { - byteSum += (byte)temp; + current = (byte)temp; + byteSum += current; temp >>= 8; } while (temp != 0UL); mod8 = value & 7UL; - uint mod5Value = byteSum.Mod5(); - uint mod3Value = byteSum.Mod3(); - + accumulator = byteSum.Mod5(); + mod5 = accumulator; mod10 = (mod8 & 1UL) == 0UL - ? mod5Value switch + ? accumulator switch { 0U => 0UL, 1U => 6UL, @@ -88,7 +91,7 @@ public static class ULongExtensions 3U => 8UL, _ => 4UL, } - : mod5Value switch + : accumulator switch { 0U => 5UL, 1U => 1UL, @@ -97,8 +100,9 @@ public static class ULongExtensions _ => 9UL, }; - mod5 = mod5Value; - mod3 = mod3Value; + // Reusing accumulator to hold the mod 3 reduction now that the mod 5 value has been stored. + accumulator = byteSum.Mod3(); + mod3 = accumulator; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -108,12 +112,14 @@ public static class ULongExtensions uint mod5Accumulator = 0U; uint mod7Accumulator = 0U; uint mod11Accumulator = 0U; - ulong temp = value; + ulong temp = 0UL; int index = 0; + byte current = 0; + temp = value; do { - byte current = (byte)temp; + current = (byte)temp; mod3Accumulator += current; mod5Accumulator += current; mod7Accumulator += (uint)(current * Mod7ByteCoefficients[index]); @@ -124,16 +130,30 @@ public static class ULongExtensions } while (temp != 0UL); - mod3 = mod3Accumulator.Mod3(); - mod5 = mod5Accumulator.Mod5(); - mod7 = ReduceMod7(mod7Accumulator); - mod11 = ReduceMod11(mod11Accumulator); + // Reusing mod3Accumulator to store the reduced mod 3 remainder before exposing it. + mod3Accumulator = mod3Accumulator.Mod3(); + // Reusing mod5Accumulator to store the reduced mod 5 remainder before exposing it. + mod5Accumulator = mod5Accumulator.Mod5(); + // Reusing mod7Accumulator to store the reduced mod 7 remainder before exposing it. + mod7Accumulator = (uint)ReduceMod7(mod7Accumulator); + // Reusing mod11Accumulator to store the reduced mod 11 remainder before exposing it. + mod11Accumulator = (uint)ReduceMod11(mod11Accumulator); + + mod3 = mod3Accumulator; + mod5 = mod5Accumulator; + mod7 = mod7Accumulator; + mod11 = mod11Accumulator; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Mod10_8_5_3Steps(this ulong value, out ulong step10, out ulong step8, out ulong step5, out ulong step3) { - value.Mod10_8_5_3(out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3); + ulong mod10 = 0UL; + ulong mod8 = 0UL; + ulong mod5 = 0UL; + ulong mod3 = 0UL; + + value.Mod10_8_5_3(out mod10, out mod8, out mod5, out mod3); step10 = (mod10 << 1).Mod10(); step8 = ((mod8 << 1) & 7UL); @@ -145,35 +165,38 @@ public static class ULongExtensions public static bool IsPrimeCandidate(this ulong n) { int i = 0; - ulong p; - uint[] smallPrimes = PrimesGenerator.SmallPrimes; - ulong[] smallPrimesPow2 = PrimesGenerator.SmallPrimesPow2; - int len = smallPrimes.Length; - for (; i < len; i++) - { - if (smallPrimesPow2[i] > n) - { - break; - } + ulong p = 0UL; + uint[] smallPrimes = PrimesGenerator.SmallPrimes; + ulong[] smallPrimesPow2 = PrimesGenerator.SmallPrimesPow2; + int len = smallPrimes.Length; + for (; i < len; i++) + { + if (smallPrimesPow2[i] > n) + { + break; + } - p = smallPrimes[i]; - if ((n % p) == 0UL) - { - return n == p; - } - } + p = smallPrimes[i]; + if ((n % p) == 0UL) + { + return n == p; + } + } - return true; - } + return true; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong Mod10(this ulong value) { - ulong mod5 = value.Mod5(); + ulong mod5 = 0UL; + ulong result = 0UL; + + mod5 = value.Mod5(); if ((value & 1UL) == 0UL) { - return mod5 switch + result = mod5 switch { 0UL => 0UL, 1UL => 6UL, @@ -182,15 +205,19 @@ public static class ULongExtensions _ => 4UL, }; } - - return mod5 switch + else { - 0UL => 5UL, - 1UL => 1UL, - 2UL => 7UL, - 3UL => 3UL, - _ => 9UL, - }; + result = mod5 switch + { + 0UL => 5UL, + 1UL => 1UL, + 2UL => 7UL, + 3UL => 3UL, + _ => 9UL, + }; + } + + return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -199,10 +226,17 @@ public static class ULongExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong Mod3(this ulong value) { - uint sum = (uint)(value & 0xFFFFUL); - sum += (uint)((value >> 16) & 0xFFFFUL); - sum += (uint)((value >> 32) & 0xFFFFUL); - sum += (uint)((value >> 48) & 0xFFFFUL); + uint sum = 0U; + ulong temp = 0UL; + + temp = value & 0xFFFFUL; + sum = (uint)temp; + temp = (value >> 16) & 0xFFFFUL; + sum += (uint)temp; + temp = (value >> 32) & 0xFFFFUL; + sum += (uint)temp; + temp = (value >> 48) & 0xFFFFUL; + sum += (uint)temp; return sum.Mod3(); } @@ -210,10 +244,17 @@ public static class ULongExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong Mod5(this ulong value) { - uint sum = (uint)(value & 0xFFFFUL); - sum += (uint)((value >> 16) & 0xFFFFUL); - sum += (uint)((value >> 32) & 0xFFFFUL); - sum += (uint)((value >> 48) & 0xFFFFUL); + uint sum = 0U; + ulong temp = 0UL; + + temp = value & 0xFFFFUL; + sum = (uint)temp; + temp = (value >> 16) & 0xFFFFUL; + sum += (uint)temp; + temp = (value >> 32) & 0xFFFFUL; + sum += (uint)temp; + temp = (value >> 48) & 0xFFFFUL; + sum += (uint)temp; return sum.Mod5(); } @@ -227,10 +268,14 @@ public static class ULongExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong Mod7(this ulong value) { - uint low = (uint)value; - uint high = (uint)(value >> 32); + uint low = 0U; + uint high = 0U; + ulong remainder = 0UL; - ulong remainder = low.Mod7() + (ulong)high.Mod7() * 4UL; + low = (uint)value; + high = (uint)(value >> 32); + + remainder = low.Mod7() + (ulong)high.Mod7() * 4UL; while (remainder >= 7UL) { remainder -= 7UL; @@ -242,10 +287,14 @@ public static class ULongExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong Mod11(this ulong value) { - uint low = (uint)value; - uint high = (uint)(value >> 32); + uint low = 0U; + uint high = 0U; + ulong remainder = 0UL; - ulong remainder = low.Mod11() + (ulong)high.Mod11() * 4UL; + low = (uint)value; + high = (uint)(value >> 32); + + remainder = low.Mod11() + (ulong)high.Mod11() * 4UL; while (remainder >= 11UL) { remainder -= 11UL;