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);