Reuse Pollard rho reciprocals for fast Montgomery stepping
This commit is contained in:
1 parent
01ac80466b
commit
645969309c
5 files changed
+352
-43
No files matched your search
@@ -527,20 +527,31 @@ public class MersenneDivisorCycles
|
||||
return 2UL;
|
||||
}
|
||||
|
||||
PollardRhoMontgomeryReducer reducer = PollardRhoMontgomeryReducer.Create(n);
|
||||
ulong diff = 0UL;
|
||||
ulong d = 0UL;
|
||||
|
||||
while (true)
|
||||
{
|
||||
ulong c = (DeterministicRandom.NextUInt64() % (n - 1UL)) + 1UL;
|
||||
ulong x = (DeterministicRandom.NextUInt64() % (n - 2UL)) + 2UL;
|
||||
ulong y = x;
|
||||
ulong d = 1UL;
|
||||
ulong xStandard = (DeterministicRandom.NextUInt64() % (n - 2UL)) + 2UL;
|
||||
ulong yStandard = xStandard;
|
||||
ulong cMontgomery = reducer.ConvertToMontgomery(c);
|
||||
ulong xMontgomery = reducer.ConvertToMontgomery(xStandard);
|
||||
ulong yMontgomery = xMontgomery;
|
||||
d = 1UL;
|
||||
|
||||
while (d == 1UL)
|
||||
{
|
||||
x = AdvancePolynomial(x, c, n);
|
||||
y = AdvancePolynomial(y, c, n);
|
||||
y = AdvancePolynomial(y, c, n);
|
||||
xMontgomery = reducer.Advance(xMontgomery, cMontgomery);
|
||||
xStandard = reducer.AdvanceStandard(xStandard, c);
|
||||
|
||||
ulong diff = x > y ? x - y : y - x;
|
||||
yMontgomery = reducer.Advance(yMontgomery, cMontgomery);
|
||||
yStandard = reducer.AdvanceStandard(yStandard, c);
|
||||
yMontgomery = reducer.Advance(yMontgomery, cMontgomery);
|
||||
yStandard = reducer.AdvanceStandard(yStandard, c);
|
||||
|
||||
diff = xStandard > yStandard ? xStandard - yStandard : yStandard - xStandard;
|
||||
d = BinaryGcd(diff, n);
|
||||
}
|
||||
|
||||
@@ -551,13 +562,6 @@ public class MersenneDivisorCycles
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong AdvancePolynomial(ulong x, ulong c, ulong modulus)
|
||||
{
|
||||
UInt128 value = (UInt128)x * x + c;
|
||||
return (ulong)(value % modulus);
|
||||
}
|
||||
|
||||
private static ulong BinaryGcd(ulong a, ulong b)
|
||||
{
|
||||
if (a == 0UL)
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
internal readonly struct PollardRhoMontgomeryReducer
|
||||
{
|
||||
private readonly MontgomeryDivisorData _divisorData;
|
||||
private readonly ulong _reciprocal;
|
||||
|
||||
[ThreadStatic]
|
||||
private static bool s_hasCachedReciprocal;
|
||||
|
||||
[ThreadStatic]
|
||||
private static ulong s_cachedReciprocal;
|
||||
|
||||
public PollardRhoMontgomeryReducer(in MontgomeryDivisorData divisorData, ulong reciprocal)
|
||||
{
|
||||
_divisorData = divisorData;
|
||||
_reciprocal = reciprocal == 0UL || reciprocal == ulong.MaxValue
|
||||
? ReciprocalMath.ComputeReciprocalEstimate(divisorData.Modulus)
|
||||
: reciprocal;
|
||||
}
|
||||
|
||||
public ulong Modulus => _divisorData.Modulus;
|
||||
private ulong NPrime => _divisorData.NPrime;
|
||||
private ulong MontgomeryTwoSquared => _divisorData.MontgomeryTwoSquared;
|
||||
private ulong MontgomeryOne => _divisorData.MontgomeryOne;
|
||||
private ulong MontgomeryTwo => _divisorData.MontgomeryTwo;
|
||||
public ulong Reciprocal => _reciprocal;
|
||||
|
||||
public static PollardRhoMontgomeryReducer Create(ulong modulus)
|
||||
{
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataCache.Get(modulus);
|
||||
ulong reciprocal;
|
||||
if (s_hasCachedReciprocal)
|
||||
{
|
||||
reciprocal = ReciprocalMath.RefineReciprocalEstimate(s_cachedReciprocal, modulus);
|
||||
if (reciprocal == 0UL || reciprocal == ulong.MaxValue)
|
||||
{
|
||||
reciprocal = ReciprocalMath.ComputeReciprocalEstimate(modulus);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reciprocal = ReciprocalMath.ComputeReciprocalEstimate(modulus);
|
||||
}
|
||||
|
||||
s_cachedReciprocal = reciprocal;
|
||||
s_hasCachedReciprocal = true;
|
||||
return new PollardRhoMontgomeryReducer(divisorData, reciprocal);
|
||||
}
|
||||
|
||||
public static PollardRhoMontgomeryReducer Create(in MontgomeryDivisorData divisorData, ulong previousReciprocal)
|
||||
{
|
||||
ulong reciprocal = ReciprocalMath.RefineReciprocalEstimate(previousReciprocal, divisorData.Modulus);
|
||||
if (reciprocal == 0UL || reciprocal == ulong.MaxValue)
|
||||
{
|
||||
reciprocal = ReciprocalMath.ComputeReciprocalEstimate(divisorData.Modulus);
|
||||
}
|
||||
|
||||
s_cachedReciprocal = reciprocal;
|
||||
s_hasCachedReciprocal = true;
|
||||
return new PollardRhoMontgomeryReducer(divisorData, reciprocal);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ulong ConvertToMontgomery(ulong value)
|
||||
{
|
||||
if (value == 0UL)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
|
||||
if (value == 1UL)
|
||||
{
|
||||
return MontgomeryOne;
|
||||
}
|
||||
|
||||
if (value == 2UL)
|
||||
{
|
||||
return MontgomeryTwo;
|
||||
}
|
||||
|
||||
return value.MontgomeryMultiply(MontgomeryTwoSquared, Modulus, NPrime);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ulong ReduceFromMontgomery(ulong montgomeryValue)
|
||||
{
|
||||
return montgomeryValue.MontgomeryMultiply(1UL, Modulus, NPrime);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ulong Advance(ulong montgomeryValue, ulong cMontgomery)
|
||||
{
|
||||
ulong squared = montgomeryValue.MontgomeryMultiply(montgomeryValue, Modulus, NPrime);
|
||||
return MontgomeryAdd(squared, cMontgomery, Modulus);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ulong AdvanceStandard(ulong standardValue, ulong c)
|
||||
{
|
||||
UInt128 polynomial = (UInt128)standardValue * standardValue + c;
|
||||
return ReciprocalMath.Reduce(polynomial, Modulus, _reciprocal);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong MontgomeryAdd(ulong a, ulong b, ulong modulus)
|
||||
{
|
||||
UInt128 total = (UInt128)a + b;
|
||||
if (total >= modulus)
|
||||
{
|
||||
total -= modulus;
|
||||
}
|
||||
|
||||
return (ulong)total;
|
||||
}
|
||||
}
|
||||
@@ -1164,11 +1164,9 @@ internal static partial class PrimeOrderCalculator
|
||||
return true;
|
||||
}
|
||||
|
||||
ulong c;
|
||||
ulong x;
|
||||
ulong y;
|
||||
ulong d;
|
||||
PollardRhoMontgomeryReducer reducer = PollardRhoMontgomeryReducer.Create(n);
|
||||
ulong diff = 0UL;
|
||||
ulong d = 0UL;
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -1177,9 +1175,12 @@ internal static partial class PrimeOrderCalculator
|
||||
return false;
|
||||
}
|
||||
|
||||
c = (DeterministicRandom.NextUInt64() % (n - 1UL)) + 1UL;
|
||||
x = (DeterministicRandom.NextUInt64() % (n - 2UL)) + 2UL;
|
||||
y = x;
|
||||
ulong c = (DeterministicRandom.NextUInt64() % (n - 1UL)) + 1UL;
|
||||
ulong xStandard = (DeterministicRandom.NextUInt64() % (n - 2UL)) + 2UL;
|
||||
ulong yStandard = xStandard;
|
||||
ulong cMontgomery = reducer.ConvertToMontgomery(c);
|
||||
ulong xMontgomery = reducer.ConvertToMontgomery(xStandard);
|
||||
ulong yMontgomery = xMontgomery;
|
||||
d = 1UL;
|
||||
|
||||
while (d == 1UL)
|
||||
@@ -1189,10 +1190,15 @@ internal static partial class PrimeOrderCalculator
|
||||
return false;
|
||||
}
|
||||
|
||||
x = AdvancePolynomial(x, c, n);
|
||||
y = AdvancePolynomial(y, c, n);
|
||||
y = AdvancePolynomial(y, c, n);
|
||||
diff = x > y ? x - y : y - x;
|
||||
xMontgomery = reducer.Advance(xMontgomery, cMontgomery);
|
||||
xStandard = reducer.AdvanceStandard(xStandard, c);
|
||||
|
||||
yMontgomery = reducer.Advance(yMontgomery, cMontgomery);
|
||||
yStandard = reducer.AdvanceStandard(yStandard, c);
|
||||
yMontgomery = reducer.Advance(yMontgomery, cMontgomery);
|
||||
yStandard = reducer.AdvanceStandard(yStandard, c);
|
||||
|
||||
diff = xStandard > yStandard ? xStandard - yStandard : yStandard - xStandard;
|
||||
d = BinaryGcd(diff, n);
|
||||
}
|
||||
|
||||
@@ -1204,13 +1210,6 @@ internal static partial class PrimeOrderCalculator
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong AdvancePolynomial(ulong x, ulong c, ulong modulus)
|
||||
{
|
||||
UInt128 value = (UInt128)x * x + c;
|
||||
return (ulong)(value % modulus);
|
||||
}
|
||||
|
||||
private static ulong BinaryGcd(ulong a, ulong b)
|
||||
{
|
||||
if (a == 0UL)
|
||||
@@ -1397,25 +1396,31 @@ internal static partial class PrimeOrderCalculator
|
||||
return 2UL;
|
||||
}
|
||||
|
||||
ulong c;
|
||||
ulong x;
|
||||
ulong y;
|
||||
ulong d;
|
||||
PollardRhoMontgomeryReducer reducer = PollardRhoMontgomeryReducer.Create(n);
|
||||
ulong diff = 0UL;
|
||||
ulong d = 0UL;
|
||||
|
||||
while (true)
|
||||
{
|
||||
c = (DeterministicRandom.NextUInt64() % (n - 1UL)) + 1UL;
|
||||
x = (DeterministicRandom.NextUInt64() % (n - 2UL)) + 2UL;
|
||||
y = x;
|
||||
ulong c = (DeterministicRandom.NextUInt64() % (n - 1UL)) + 1UL;
|
||||
ulong xStandard = (DeterministicRandom.NextUInt64() % (n - 2UL)) + 2UL;
|
||||
ulong yStandard = xStandard;
|
||||
ulong cMontgomery = reducer.ConvertToMontgomery(c);
|
||||
ulong xMontgomery = reducer.ConvertToMontgomery(xStandard);
|
||||
ulong yMontgomery = xMontgomery;
|
||||
d = 1UL;
|
||||
|
||||
while (d == 1UL)
|
||||
{
|
||||
x = AdvancePolynomial(x, c, n);
|
||||
y = AdvancePolynomial(y, c, n);
|
||||
y = AdvancePolynomial(y, c, n);
|
||||
diff = x > y ? x - y : y - x;
|
||||
xMontgomery = reducer.Advance(xMontgomery, cMontgomery);
|
||||
xStandard = reducer.AdvanceStandard(xStandard, c);
|
||||
|
||||
yMontgomery = reducer.Advance(yMontgomery, cMontgomery);
|
||||
yStandard = reducer.AdvanceStandard(yStandard, c);
|
||||
yMontgomery = reducer.Advance(yMontgomery, cMontgomery);
|
||||
yStandard = reducer.AdvanceStandard(yStandard, c);
|
||||
|
||||
diff = xStandard > yStandard ? xStandard - yStandard : yStandard - xStandard;
|
||||
d = BinaryGcd(diff, n);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
internal static class ReciprocalMath
|
||||
{
|
||||
private const int ReciprocalShift = 64;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong ComputeReciprocalEstimate(ulong divisor)
|
||||
{
|
||||
if (divisor <= 1UL)
|
||||
{
|
||||
return ulong.MaxValue;
|
||||
}
|
||||
|
||||
UInt128 scaledOne = UInt128.One << ReciprocalShift;
|
||||
UInt128 estimate = scaledOne / divisor;
|
||||
if ((scaledOne % divisor) != UInt128.Zero)
|
||||
{
|
||||
estimate += UInt128.One;
|
||||
}
|
||||
|
||||
if (estimate == UInt128.Zero)
|
||||
{
|
||||
return 1UL;
|
||||
}
|
||||
|
||||
if (estimate > ulong.MaxValue)
|
||||
{
|
||||
return ulong.MaxValue;
|
||||
}
|
||||
|
||||
return (ulong)estimate;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong RefineReciprocalEstimate(ulong reciprocal, ulong divisor)
|
||||
{
|
||||
if (divisor <= 1UL)
|
||||
{
|
||||
return ulong.MaxValue;
|
||||
}
|
||||
|
||||
if (reciprocal == 0UL || reciprocal == ulong.MaxValue)
|
||||
{
|
||||
return ComputeReciprocalEstimate(divisor);
|
||||
}
|
||||
|
||||
UInt128 reciprocal128 = reciprocal;
|
||||
UInt128 product = (UInt128)divisor * reciprocal128;
|
||||
UInt128 scaled = product >> ReciprocalShift;
|
||||
UInt128 twoScaled = UInt128.One << (ReciprocalShift + 1);
|
||||
if (scaled >= twoScaled)
|
||||
{
|
||||
return ComputeReciprocalEstimate(divisor);
|
||||
}
|
||||
|
||||
UInt128 correction = twoScaled - scaled;
|
||||
UInt128 refined = (reciprocal128 * correction) >> ReciprocalShift;
|
||||
if (refined == UInt128.Zero)
|
||||
{
|
||||
return 1UL;
|
||||
}
|
||||
|
||||
return refined > ulong.MaxValue ? ulong.MaxValue : (ulong)refined;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong Reduce(UInt128 numerator, ulong divisor, ulong reciprocal)
|
||||
{
|
||||
if (divisor <= 1UL)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
|
||||
if (numerator == UInt128.Zero)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
|
||||
if (reciprocal == 0UL || reciprocal == ulong.MaxValue)
|
||||
{
|
||||
return (ulong)(numerator % divisor);
|
||||
}
|
||||
|
||||
ulong high = (ulong)(numerator >> ReciprocalShift);
|
||||
ulong low = (ulong)numerator;
|
||||
|
||||
if (high >= divisor)
|
||||
{
|
||||
return (ulong)(numerator % divisor);
|
||||
}
|
||||
|
||||
UInt128 highProduct = (UInt128)high * reciprocal;
|
||||
UInt128 lowProduct = (UInt128)low * reciprocal;
|
||||
UInt128 estimate = highProduct + (lowProduct >> ReciprocalShift);
|
||||
|
||||
if (estimate == UInt128.Zero)
|
||||
{
|
||||
return (ulong)(numerator % divisor);
|
||||
}
|
||||
|
||||
UInt128 product = estimate * divisor;
|
||||
while (product > numerator)
|
||||
{
|
||||
if (estimate == UInt128.Zero)
|
||||
{
|
||||
return (ulong)(numerator % divisor);
|
||||
}
|
||||
|
||||
estimate--;
|
||||
product -= divisor;
|
||||
}
|
||||
|
||||
UInt128 remainder = numerator - product;
|
||||
while (remainder >= divisor)
|
||||
{
|
||||
remainder -= divisor;
|
||||
}
|
||||
|
||||
return (ulong)remainder;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user