diff --git a/EvenPerfectBitScanner.Benchmarks/ResidueComputationBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/ResidueComputationBenchmarks.cs index 8e49fc0..4894bcd 100644 --- a/EvenPerfectBitScanner.Benchmarks/ResidueComputationBenchmarks.cs +++ b/EvenPerfectBitScanner.Benchmarks/ResidueComputationBenchmarks.cs @@ -24,13 +24,12 @@ public class ResidueComputationBenchmarks [Benchmark] public (ulong Step10, ulong Step8, ulong Step3, ulong Step5) ModMethodModulo() { - UInt128 exponent128 = Exponent; - exponent128.Mod10_8_5_3(out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3); - ulong step10 = (mod10 << 1) % 10UL; - ulong step8 = (mod8 << 1) & 7UL; - ulong step3 = (mod3 << 1) % 3UL; - ulong step5 = (mod5 << 1) % 5UL; - return (step10, step8, step3, step5); + Exponent.Mod10_8_5_3(out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3); + mod10 = (mod10 << 1) % 10UL; + mod8 = (mod8 << 1) & 7UL; + mod3 = (mod3 << 1) % 3UL; + mod5 = (mod5 << 1) % 5UL; + return (mod10, mod8, mod3, mod5); } } diff --git a/EvenPerfectBitScanner.Benchmarks/UInt128ExtensionsBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/UInt128ExtensionsBenchmarks.cs new file mode 100644 index 0000000..87ab78f --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/UInt128ExtensionsBenchmarks.cs @@ -0,0 +1,55 @@ +using System.Numerics; +using BenchmarkDotNet.Attributes; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[MemoryDiagnoser] +public class UInt128ExtensionsBenchmarks +{ + [Params(3UL, 8191UL, 131071UL, 2147483647UL)] + public ulong Value { get; set; } + + [Benchmark(Baseline = true)] + 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); + } + + 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; + } + + mod10 = result % 10UL; + return (mod10, mod8, mod3, mod5); + } + + [Benchmark] + public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) ModMethodModulo() + { + Value.Mod10_8_5_3(out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3); + return (mod10, mod8, mod3, mod5); + } +} + diff --git a/PerfectNumbers.Core/PerfectNumbersMath.cs b/PerfectNumbers.Core/PerfectNumbersMath.cs new file mode 100644 index 0000000..3b22c50 --- /dev/null +++ b/PerfectNumbers.Core/PerfectNumbersMath.cs @@ -0,0 +1,21 @@ +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/UInt128Extensions.cs b/PerfectNumbers.Core/UInt128Extensions.cs index a55ad1e..ba144c2 100644 --- a/PerfectNumbers.Core/UInt128Extensions.cs +++ b/PerfectNumbers.Core/UInt128Extensions.cs @@ -120,87 +120,73 @@ public static class UInt128Extensions return true; } - [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; + [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; - do - { - byteSum += (uint)(byte)temp; - temp >>= 8; - } - while (temp != UInt128.Zero); + do + { + byteSum += (byte)temp; + temp >>= 8; + } + while (temp != UInt128.Zero); - mod8 = (ulong)value & 7UL; + mod8 = (ulong)value & 7UL; - uint mod5Value = FastRemainder5(byteSum); - uint mod3Value = FastRemainder3(byteSum); + uint mod5Value = PerfectNumbersMath.FastRemainder5(byteSum); + uint mod3Value = PerfectNumbersMath.FastRemainder3(byteSum); - ulong parity = mod8 & 1UL; - mod10 = parity == 0UL - ? mod5Value switch - { - 0U => 0UL, - 1U => 6UL, - 2U => 2UL, - 3U => 8UL, - _ => 4UL, - } - : mod5Value switch - { - 0U => 5UL, - 1U => 1UL, - 2U => 7UL, - 3U => 3UL, - _ => 9UL, - }; + ulong parity = mod8 & 1UL; + mod10 = parity == 0UL + ? mod5Value switch + { + 0U => 0UL, + 1U => 6UL, + 2U => 2UL, + 3U => 8UL, + _ => 4UL, + } + : mod5Value switch + { + 0U => 5UL, + 1U => 1UL, + 2U => 7UL, + 3U => 3UL, + _ => 9UL, + }; - mod5 = mod5Value; - mod3 = mod3Value; - } + mod5 = mod5Value; + mod3 = mod3Value; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint FastRemainder3(uint value) - { - uint quotient = (uint)(((ulong)value * 0xAAAAAAABUL) >> 33); - return value - quotient * 3U; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt128 Pow(this UInt128 value, ulong exponent) + { + UInt128 result = UInt128.One; + UInt128 baseValue = value; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint FastRemainder5(uint value) - { - uint quotient = (uint)(((ulong)value * 0xCCCCCCCDUL) >> 34); - return value - quotient * 5U; - } + while (exponent != 0UL) + { + if ((exponent & 1UL) != 0UL) + { + result *= baseValue; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static UInt128 Pow(this UInt128 value, ulong exponent) - { - UInt128 result = UInt128.One; - UInt128 baseValue = value; + baseValue *= baseValue; + exponent >>= 1; + } - while (exponent != 0UL) - { - if ((exponent & 1UL) != 0UL) - { - result *= baseValue; - } + return result; + } - baseValue *= baseValue; - exponent >>= 1; - } - - return result; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static UInt128 ModPow(this UInt128 value, UInt128 exponent, UInt128 modulus) - { - UInt128 result, one = result = UInt128.One, - zero = UInt128.Zero, - baseValue = value % modulus; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt128 ModPow(this UInt128 value, UInt128 exponent, UInt128 modulus) + { + UInt128 result, one = result = UInt128.One, + zero = UInt128.Zero, + baseValue = value % modulus; while (exponent != zero) { @@ -284,7 +270,7 @@ public static class UInt128Extensions { a -= modulus; } - + b >>= 1; } diff --git a/PerfectNumbers.Core/ULongExtensions.cs b/PerfectNumbers.Core/ULongExtensions.cs index 9f3f68b..5073c0e 100644 --- a/PerfectNumbers.Core/ULongExtensions.cs +++ b/PerfectNumbers.Core/ULongExtensions.cs @@ -57,6 +57,47 @@ public static class ULongExtensions return q; } + [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; + uint byteSum = 0U; + + do + { + byteSum += (byte)temp; + temp >>= 8; + } + while (temp != 0UL); + + mod8 = value & 7UL; + + uint mod5Value = PerfectNumbersMath.FastRemainder5(byteSum); + uint mod3Value = PerfectNumbersMath.FastRemainder3(byteSum); + + ulong parity = mod8 & 1UL; + mod10 = parity == 0UL + ? mod5Value switch + { + 0U => 0UL, + 1U => 6UL, + 2U => 2UL, + 3U => 8UL, + _ => 4UL, + } + : mod5Value switch + { + 0U => 5UL, + 1U => 1UL, + 2U => 7UL, + 3U => 3UL, + _ => 9UL, + }; + + mod5 = mod5Value; + mod3 = mod3Value; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsPrimeCandidate(this ulong n) {