diff --git a/PerfectNumbers.Core/MersennePrimeFactorTester.cs b/PerfectNumbers.Core/MersennePrimeFactorTester.cs index 2953ee5..ecf9022 100644 --- a/PerfectNumbers.Core/MersennePrimeFactorTester.cs +++ b/PerfectNumbers.Core/MersennePrimeFactorTester.cs @@ -105,7 +105,7 @@ public static class MersennePrimeFactorTester } candidate = order / prime; - if (UInt128Numbers.Two.ModPow(candidate, q, ct) == UInt128.One) + if (ModPowWithCancellation(UInt128Numbers.Two, candidate, q, ct) == UInt128.One) { order = candidate; } @@ -130,6 +130,43 @@ public static class MersennePrimeFactorTester return order; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static UInt128 ModPowWithCancellation(UInt128 baseValue, UInt128 exponent, UInt128 modulus, CancellationToken ct) + { + // Keep this helper local to the factoring path so we can preserve the token-aware early outs + // without carrying a separate overload on the shared UInt128 extensions. + if (modulus <= UInt128.One) + { + return UInt128.Zero; + } + + UInt128 result = UInt128.One; + UInt128 baseResidue = baseValue % modulus; + + while (exponent != UInt128.Zero) + { + if (ct.IsCancellationRequested) + { + return UInt128.Zero; + } + + if ((exponent & UInt128.One) != UInt128.Zero) + { + result = result.MulMod(baseResidue, modulus); + } + + if (ct.IsCancellationRequested) + { + return UInt128.Zero; + } + + baseResidue = baseResidue.MulMod(baseResidue, modulus); + exponent >>= 1; + } + + return result; + } + private static readonly ConcurrentBag> ListPool = new(); private static readonly ConcurrentBag> DictPool = new(); private static readonly Dictionary FactorCache = new(); diff --git a/PerfectNumbers.Core/UInt128Extensions.cs b/PerfectNumbers.Core/UInt128Extensions.cs index c55e483..5a0c093 100644 --- a/PerfectNumbers.Core/UInt128Extensions.cs +++ b/PerfectNumbers.Core/UInt128Extensions.cs @@ -560,43 +560,6 @@ public static class UInt128Extensions public static ulong Mod128(this UInt128 value) => (ulong)value & 127UL; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static UInt128 ModPow(this UInt128 value, UInt128 exponent, UInt128 modulus, CancellationToken ct) - { - UInt128 zero = UInt128.Zero; - UInt128 one = UInt128.One; - if (modulus <= one) - { - return zero; - } - - UInt128 result = UInt128.One; - UInt128 baseValue = value % modulus; - - while (exponent != zero) - { - if (ct.IsCancellationRequested) - { - return zero; - } - - if ((exponent & one) != zero) - { - result = result.MulMod(baseValue, modulus); - } - - if (ct.IsCancellationRequested) - { - return zero; - } - - baseValue = baseValue.MulMod(baseValue, modulus); - exponent >>= 1; - } - - return result; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static UInt128 ModPow(this UInt128 value, UInt128 exponent, UInt128 modulus) {