Consolidate MulMod helpers across CPU factoring path

This commit is contained in:
Marek Lemańczyk committed 2025-10-20 11:12:39 +02:00
1 parent bfc6605e97
commit 34240ab3e9
4 files changed
+174 -177

No files matched your search

+63 -7
View File
@@ -455,13 +455,66 @@ public static partial class ULongExtensions
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong AddMod64(this ulong value, ulong addend, ulong modulus)
{
if (modulus <= 1UL)
{
return 0UL;
}
UInt128 sum = (UInt128)value + addend;
UInt128 mod = modulus;
if (sum >= mod)
{
sum -= mod;
if (sum >= mod)
{
sum -= mod;
}
}
return (ulong)sum;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong FoldMontgomery(ulong value, ulong modulus)
{
if (modulus <= 1UL)
{
return 0UL;
}
if (value < modulus)
{
return value;
}
ulong folded = 0UL;
int leadingZeros = BitOperations.LeadingZeroCount(value);
int bitIndex = 63 - leadingZeros;
for (; bitIndex >= 0; bitIndex--)
{
folded = folded.AddMod64(folded, modulus);
if (((value >> bitIndex) & 1UL) != 0UL)
{
folded = folded.AddMod64(1UL, modulus);
}
}
return folded;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong ModPow64(this ulong value, ulong exponent, ulong modulus)
{
if (modulus <= 1UL)
{
return 0UL;
}
ulong result = 1UL;
// TODO: Replace this `%` with the Montgomery folding helper highlighted in MulMod64Benchmarks so the
// modular exponentiation avoids the slow integer division before the ladder even starts.
value %= modulus;
value = FoldMontgomery(value, modulus);
while (exponent != 0UL)
{
@@ -477,10 +530,13 @@ public static partial class ULongExtensions
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
// TODO: Replace this fallback with the UInt128 Montgomery helper measured fastest in
// MulMod64Benchmarks so CPU callers stop paying for triple modulo operations.
public static ulong MulMod64(this ulong a, ulong b, ulong modulus) => (ulong)(UInt128)(((a % modulus) * (b % modulus)) % modulus);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
// Reuse the direct UInt128 reduction path that dominated MulMod64Benchmarks to avoid
// the redundant operand modulo operations in the older fallback.
public static ulong MulMod64(this ulong a, ulong b, ulong modulus)
{
return (ulong)(((UInt128)a * b) % modulus);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong MontgomeryMultiply(this ulong a, ulong b, ulong modulus, ulong nPrime)