Tiny cleanup

This commit is contained in:
Marek Lemańczyk committed 2025-09-23 23:22:20 +02:00
1 parent 6e99ee39e3
commit dc22545adb
1 file changed
+294 -299
@@ -16,29 +16,29 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester
private ulong _lastStatusDivisor; private ulong _lastStatusDivisor;
private bool _useDivisorCycles; private bool _useDivisorCycles;
public readonly struct MontgomeryDivisorData public readonly struct MontgomeryDivisorData
{ {
public readonly ulong Modulus; public readonly ulong Modulus;
public readonly ulong NPrime; public readonly ulong NPrime;
public readonly ulong MontgomeryOne; public readonly ulong MontgomeryOne;
public readonly ulong MontgomeryTwo; public readonly ulong MontgomeryTwo;
public MontgomeryDivisorData(ulong modulus, ulong nPrime, ulong montgomeryOne, ulong montgomeryTwo) public MontgomeryDivisorData(ulong modulus, ulong nPrime, ulong montgomeryOne, ulong montgomeryTwo)
{ {
Modulus = modulus; Modulus = modulus;
NPrime = nPrime; NPrime = nPrime;
MontgomeryOne = montgomeryOne; MontgomeryOne = montgomeryOne;
MontgomeryTwo = montgomeryTwo; MontgomeryTwo = montgomeryTwo;
} }
} }
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, ArrayView<MontgomeryDivisorData>, ArrayView<byte>>> _kernelCache = new(); private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, ArrayView<MontgomeryDivisorData>, ArrayView<byte>>> _kernelCache = new();
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, MontgomeryDivisorData, ArrayView<ulong>, ArrayView<byte>>> _kernelByPrimeCache = new(); private readonly ConcurrentDictionary<Accelerator, Action<Index1D, MontgomeryDivisorData, ArrayView<ulong>, ArrayView<byte>>> _kernelByPrimeCache = new();
private Action<Index1D, ulong, ArrayView<MontgomeryDivisorData>, ArrayView<byte>> GetKernel(Accelerator accelerator) => private Action<Index1D, ulong, ArrayView<MontgomeryDivisorData>, ArrayView<byte>> GetKernel(Accelerator accelerator) =>
_kernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView<MontgomeryDivisorData>, ArrayView<byte>>(CheckKernel)); _kernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView<MontgomeryDivisorData>, ArrayView<byte>>(CheckKernel));
private Action<Index1D, MontgomeryDivisorData, ArrayView<ulong>, ArrayView<byte>> GetKernelByPrime(Accelerator accelerator) => private Action<Index1D, MontgomeryDivisorData, ArrayView<ulong>, ArrayView<byte>> GetKernelByPrime(Accelerator accelerator) =>
_kernelByPrimeCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, MontgomeryDivisorData, ArrayView<ulong>, ArrayView<byte>>(CheckKernelByPrime)); _kernelByPrimeCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, MontgomeryDivisorData, ArrayView<ulong>, ArrayView<byte>>(CheckKernelByPrime));
public int GpuBatchSize public int GpuBatchSize
{ {
@@ -96,12 +96,12 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester
} }
} }
private bool CheckDivisors(ulong prime, ulong allowedMax, out ulong lastProcessed, out bool coveredRange) private bool CheckDivisors(ulong prime, ulong allowedMax, out ulong lastProcessed, out bool coveredRange)
{ {
allowedMax = Math.Min(allowedMax, _divisorLimit); allowedMax = Math.Min(allowedMax, _divisorLimit);
lastProcessed = 0UL; lastProcessed = 0UL;
if (allowedMax < 3UL) if (allowedMax < 3UL)
{ {
coveredRange = true; coveredRange = true;
return false; return false;
@@ -112,85 +112,85 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester
var kernel = GetKernel(accelerator); var kernel = GetKernel(accelerator);
int batchCapacity = _gpuBatchSize; int batchCapacity = _gpuBatchSize;
var divisorsBuffer = accelerator.Allocate1D<MontgomeryDivisorData>(batchCapacity); var divisorsBuffer = accelerator.Allocate1D<MontgomeryDivisorData>(batchCapacity);
var hitsBuffer = accelerator.Allocate1D<byte>(batchCapacity); var hitsBuffer = accelerator.Allocate1D<byte>(batchCapacity);
ulong[] divisors = ArrayPool<ulong>.Shared.Rent(batchCapacity); ulong[] divisors = ArrayPool<ulong>.Shared.Rent(batchCapacity);
byte[] hits = ArrayPool<byte>.Shared.Rent(batchCapacity); byte[] hits = ArrayPool<byte>.Shared.Rent(batchCapacity);
MontgomeryDivisorData[] divisorData = ArrayPool<MontgomeryDivisorData>.Shared.Rent(batchCapacity); MontgomeryDivisorData[] divisorData = ArrayPool<MontgomeryDivisorData>.Shared.Rent(batchCapacity);
bool composite = false; bool composite = false;
bool processedAll = false; bool processedAll = false;
ulong divisor = 3UL; ulong divisor = 3UL;
try try
{ {
bool useCycles = _useDivisorCycles; bool useCycles = _useDivisorCycles;
MersenneDivisorCycles? divisorCycles = useCycles ? MersenneDivisorCycles.Shared : null; MersenneDivisorCycles? divisorCycles = useCycles ? MersenneDivisorCycles.Shared : null;
while (divisor <= allowedMax) while (divisor <= allowedMax)
{ {
int batchSize = 0; int batchSize = 0;
bool reachedEndInBatch = false; bool reachedEndInBatch = false;
while (batchSize < batchCapacity && divisor <= allowedMax) while (batchSize < batchCapacity && divisor <= allowedMax)
{ {
ulong current = divisor; ulong current = divisor;
ulong next = current + 2UL; ulong next = current + 2UL;
bool include = true; bool include = true;
if (useCycles) if (useCycles)
{ {
ulong cycle = divisorCycles!.GetCycle(current); ulong cycle = divisorCycles!.GetCycle(current);
if (cycle == 0UL || prime % cycle != 0UL) if (cycle == 0UL || prime % cycle != 0UL)
{ {
include = false; include = false;
lastProcessed = current; lastProcessed = current;
ReportStatus(lastProcessed); ReportStatus(lastProcessed);
} }
} }
if (include) if (include)
{ {
divisors[batchSize++] = current; divisors[batchSize++] = current;
} }
if (next <= current) if (next <= current)
{ {
reachedEndInBatch = true; reachedEndInBatch = true;
break; break;
} }
if (next > allowedMax) if (next > allowedMax)
{ {
divisor = next; divisor = next;
reachedEndInBatch = true; reachedEndInBatch = true;
break; break;
} }
divisor = next; divisor = next;
} }
if (batchSize == 0) if (batchSize == 0)
{ {
break; break;
} }
Span<MontgomeryDivisorData> divisorDataSpan = divisorData.AsSpan(0, batchSize); Span<MontgomeryDivisorData> divisorDataSpan = divisorData.AsSpan(0, batchSize);
var divisorView = divisorsBuffer.View.SubView(0, batchSize); var divisorView = divisorsBuffer.View.SubView(0, batchSize);
var hitsView = hitsBuffer.View.SubView(0, batchSize); var hitsView = hitsBuffer.View.SubView(0, batchSize);
Span<ulong> divisorSpan = divisors.AsSpan(0, batchSize); Span<ulong> divisorSpan = divisors.AsSpan(0, batchSize);
Span<byte> hitsSpan = hits.AsSpan(0, batchSize); Span<byte> hitsSpan = hits.AsSpan(0, batchSize);
for (int i = 0; i < batchSize; i++) for (int i = 0; i < batchSize; i++)
{ {
divisorDataSpan[i] = CreateMontgomeryDivisorData(divisorSpan[i]); divisorDataSpan[i] = CreateMontgomeryDivisorData(divisorSpan[i]);
} }
divisorView.CopyFromCPU(ref MemoryMarshal.GetReference(divisorDataSpan), batchSize); divisorView.CopyFromCPU(ref MemoryMarshal.GetReference(divisorDataSpan), batchSize);
kernel(batchSize, prime, divisorView, hitsView); kernel(batchSize, prime, divisorView, hitsView);
accelerator.Synchronize(); accelerator.Synchronize();
hitsView.CopyToCPU(ref MemoryMarshal.GetReference(hitsSpan), batchSize); hitsView.CopyToCPU(ref MemoryMarshal.GetReference(hitsSpan), batchSize);
for (int i = 0; i < batchSize; i++) for (int i = 0; i < batchSize; i++)
{ {
@@ -219,13 +219,13 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester
} }
finally finally
{ {
ArrayPool<byte>.Shared.Return(hits, clearArray: true); ArrayPool<byte>.Shared.Return(hits, clearArray: true);
ArrayPool<ulong>.Shared.Return(divisors, clearArray: true); ArrayPool<ulong>.Shared.Return(divisors, clearArray: true);
ArrayPool<MontgomeryDivisorData>.Shared.Return(divisorData, clearArray: true); ArrayPool<MontgomeryDivisorData>.Shared.Return(divisorData, clearArray: true);
hitsBuffer.Dispose(); hitsBuffer.Dispose();
divisorsBuffer.Dispose(); divisorsBuffer.Dispose();
gpuLease.Dispose(); gpuLease.Dispose();
} }
return composite; return composite;
} }
@@ -270,128 +270,123 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester
return Math.Min(maxByPrime, _divisorLimit); return Math.Min(maxByPrime, _divisorLimit);
} }
private static void CheckKernel(Index1D index, ulong prime, ArrayView<MontgomeryDivisorData> divisors, ArrayView<byte> hits) private static void CheckKernel(Index1D index, ulong prime, ArrayView<MontgomeryDivisorData> divisors, ArrayView<byte> hits)
{ {
MontgomeryDivisorData divisor = divisors[index]; MontgomeryDivisorData divisor = divisors[index];
if (divisor.Modulus <= 1UL || (divisor.Modulus & 1UL) == 0UL) if (divisor.Modulus <= 1UL || (divisor.Modulus & 1UL) == 0UL)
{ {
hits[index] = 0; hits[index] = 0;
return; return;
} }
ulong pow = Pow2Mod(prime, divisor); ulong pow = Pow2Mod(prime, divisor);
hits[index] = pow == 1UL ? (byte)1 : (byte)0; hits[index] = pow == 1UL ? (byte)1 : (byte)0;
} }
public sealed class DivisorScanSession : IDisposable public sealed class DivisorScanSession : IDisposable
{ {
private readonly MersenneNumberDivisorByDivisorGpuTester _owner; private readonly MersenneNumberDivisorByDivisorGpuTester _owner;
private readonly GpuContextPool.GpuContextLease _lease; private readonly GpuContextPool.GpuContextLease _lease;
private readonly Accelerator _accelerator; private readonly Accelerator _accelerator;
private readonly Action<Index1D, MontgomeryDivisorData, ArrayView<ulong>, ArrayView<byte>> _kernel; private readonly Action<Index1D, MontgomeryDivisorData, ArrayView<ulong>, ArrayView<byte>> _kernel;
private readonly MemoryBuffer1D<ulong, Stride1D.Dense> _primesBuffer; private readonly MemoryBuffer1D<ulong, Stride1D.Dense> _primesBuffer;
private readonly MemoryBuffer1D<byte, Stride1D.Dense> _hitsBuffer; private readonly MemoryBuffer1D<byte, Stride1D.Dense> _hitsBuffer;
private readonly ulong[] _primesHost; private readonly ulong[] _primesHost;
private readonly byte[] _hitsHost; private readonly byte[] _hitsHost;
private readonly int[] _indexHost; private readonly int[] _indexHost;
private bool _disposed; private bool _disposed;
internal DivisorScanSession(MersenneNumberDivisorByDivisorGpuTester owner) internal DivisorScanSession(MersenneNumberDivisorByDivisorGpuTester owner)
{ {
_owner = owner; _owner = owner;
_lease = GpuContextPool.RentPreferred(preferCpu: false); _lease = GpuContextPool.RentPreferred(preferCpu: false);
_accelerator = _lease.Accelerator; _accelerator = _lease.Accelerator;
_kernel = owner.GetKernelByPrime(_accelerator); _kernel = owner.GetKernelByPrime(_accelerator);
_primesBuffer = _accelerator.Allocate1D<ulong>(owner._gpuBatchSize); _primesBuffer = _accelerator.Allocate1D<ulong>(owner._gpuBatchSize);
_hitsBuffer = _accelerator.Allocate1D<byte>(owner._gpuBatchSize); _hitsBuffer = _accelerator.Allocate1D<byte>(owner._gpuBatchSize);
_primesHost = ArrayPool<ulong>.Shared.Rent(owner._gpuBatchSize); _primesHost = ArrayPool<ulong>.Shared.Rent(owner._gpuBatchSize);
_hitsHost = ArrayPool<byte>.Shared.Rent(owner._gpuBatchSize); _hitsHost = ArrayPool<byte>.Shared.Rent(owner._gpuBatchSize);
_indexHost = ArrayPool<int>.Shared.Rent(owner._gpuBatchSize); _indexHost = ArrayPool<int>.Shared.Rent(owner._gpuBatchSize);
} }
public void CheckDivisor(ulong divisor, ReadOnlySpan<ulong> primes, Span<byte> hits) public void CheckDivisor(ulong divisor, ReadOnlySpan<ulong> primes, Span<byte> hits)
{ {
if (_disposed) if (_disposed)
{ {
throw new ObjectDisposedException(nameof(DivisorScanSession)); throw new ObjectDisposedException(nameof(DivisorScanSession));
} }
if (primes.Length == 0) int offset = 0;
{ int consoleStatus = 0;
return; bool useCycles = _owner._useDivisorCycles;
} MersenneDivisorCycles? divisorCycles = useCycles ? MersenneDivisorCycles.Shared : null;
ulong cycle = useCycles ? divisorCycles!.GetCycle(divisor) : 0UL;
MontgomeryDivisorData divisorData = CreateMontgomeryDivisorData(divisor);
while (offset < primes.Length)
{
int batchSize = Math.Min(_owner._gpuBatchSize, primes.Length - offset);
Span<ulong> primeSpan = _primesHost.AsSpan(0, batchSize);
primes.Slice(offset, batchSize).CopyTo(primeSpan);
int offset = 0; if (++consoleStatus == PerfectNumberConstants.ConsoleInterval)
int consoleStatus = 0; {
bool useCycles = _owner._useDivisorCycles; Console.WriteLine($"...processed by-divisor candidate = {primes[0]}");
MersenneDivisorCycles? divisorCycles = useCycles ? MersenneDivisorCycles.Shared : null; consoleStatus = 0;
ulong cycle = useCycles ? divisorCycles!.GetCycle(divisor) : 0UL; }
MontgomeryDivisorData divisorData = CreateMontgomeryDivisorData(divisor);
while (offset < primes.Length)
{
int batchSize = Math.Min(_owner._gpuBatchSize, primes.Length - offset);
Span<ulong> primeSpan = _primesHost.AsSpan(0, batchSize);
primes.Slice(offset, batchSize).CopyTo(primeSpan);
if (++consoleStatus == PerfectNumberConstants.ConsoleInterval) Span<int> indexSpan = _indexHost.AsSpan(0, batchSize);
{ Span<byte> hitSpan = _hitsHost.AsSpan(0, batchSize);
Console.WriteLine($"...processed by-divisor candidate = {primes[0]}"); Span<byte> hitsSlice = hits.Slice(offset, batchSize);
consoleStatus = 0; int gpuCount = 0;
}
Span<int> indexSpan = _indexHost.AsSpan(0, batchSize); if (useCycles)
Span<byte> hitSpan = _hitsHost.AsSpan(0, batchSize); {
Span<byte> hitsSlice = hits.Slice(offset, batchSize); for (int i = 0; i < batchSize; i++)
int gpuCount = 0; {
ulong primeValue = primeSpan[i];
if (cycle == 0UL || primeValue % cycle != 0UL)
{
hitsSlice[i] = 0;
continue;
}
if (useCycles) indexSpan[gpuCount] = i;
{ primeSpan[gpuCount] = primeValue;
for (int i = 0; i < batchSize; i++) gpuCount++;
{ }
ulong primeValue = primeSpan[i];
if (cycle == 0UL || primeValue % cycle != 0UL)
{
hitsSlice[i] = 0;
continue;
}
indexSpan[gpuCount] = i; if (gpuCount > 0)
primeSpan[gpuCount] = primeValue; {
gpuCount++; var primesView = _primesBuffer.View.SubView(0, gpuCount);
} var hitsView = _hitsBuffer.View.SubView(0, gpuCount);
if (gpuCount > 0) primesView.CopyFromCPU(ref MemoryMarshal.GetReference(primeSpan.Slice(0, gpuCount)), gpuCount);
{ _kernel(gpuCount, divisorData, primesView, hitsView);
var primesView = _primesBuffer.View.SubView(0, gpuCount); _accelerator.Synchronize();
var hitsView = _hitsBuffer.View.SubView(0, gpuCount);
primesView.CopyFromCPU(ref MemoryMarshal.GetReference(primeSpan.Slice(0, gpuCount)), gpuCount); hitsView.CopyToCPU(ref MemoryMarshal.GetReference(hitSpan.Slice(0, gpuCount)), gpuCount);
_kernel(gpuCount, divisorData, primesView, hitsView); for (int i = 0; i < gpuCount; i++)
_accelerator.Synchronize(); {
hitsSlice[indexSpan[i]] = hitSpan[i];
}
}
}
else
{
var primesView = _primesBuffer.View.SubView(0, batchSize);
var hitsView = _hitsBuffer.View.SubView(0, batchSize);
hitsView.CopyToCPU(ref MemoryMarshal.GetReference(hitSpan.Slice(0, gpuCount)), gpuCount); primesView.CopyFromCPU(ref MemoryMarshal.GetReference(primeSpan), batchSize);
for (int i = 0; i < gpuCount; i++) _kernel(batchSize, divisorData, primesView, hitsView);
{ _accelerator.Synchronize();
hitsSlice[indexSpan[i]] = hitSpan[i];
}
}
}
else
{
var primesView = _primesBuffer.View.SubView(0, batchSize);
var hitsView = _hitsBuffer.View.SubView(0, batchSize);
primesView.CopyFromCPU(ref MemoryMarshal.GetReference(primeSpan), batchSize); hitsView.CopyToCPU(ref MemoryMarshal.GetReference(hitSpan), batchSize);
_kernel(batchSize, divisorData, primesView, hitsView); hitSpan.CopyTo(hitsSlice);
_accelerator.Synchronize(); }
hitsView.CopyToCPU(ref MemoryMarshal.GetReference(hitSpan), batchSize); offset += batchSize;
hitSpan.CopyTo(hitsSlice); }
} }
offset += batchSize;
}
}
public void Dispose() public void Dispose()
{ {
@@ -400,126 +395,126 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester
return; return;
} }
_disposed = true; _disposed = true;
ArrayPool<ulong>.Shared.Return(_primesHost, clearArray: true); ArrayPool<ulong>.Shared.Return(_primesHost, clearArray: true);
ArrayPool<byte>.Shared.Return(_hitsHost, clearArray: true); ArrayPool<byte>.Shared.Return(_hitsHost, clearArray: true);
ArrayPool<int>.Shared.Return(_indexHost, clearArray: true); ArrayPool<int>.Shared.Return(_indexHost, clearArray: true);
_primesBuffer.Dispose(); _primesBuffer.Dispose();
_hitsBuffer.Dispose(); _hitsBuffer.Dispose();
_lease.Dispose(); _lease.Dispose();
} }
} }
private static ulong Pow2Mod(ulong exponent, MontgomeryDivisorData divisor) private static ulong Pow2Mod(ulong exponent, MontgomeryDivisorData divisor)
{ {
ulong modulus = divisor.Modulus; ulong modulus = divisor.Modulus;
if (modulus <= 1UL || (modulus & 1UL) == 0UL) if (modulus <= 1UL || (modulus & 1UL) == 0UL)
{ {
return 0UL; return 0UL;
} }
ulong result = divisor.MontgomeryOne; ulong result = divisor.MontgomeryOne;
ulong baseVal = divisor.MontgomeryTwo; ulong baseVal = divisor.MontgomeryTwo;
ulong exp = exponent; ulong exp = exponent;
while (exp > 0UL) while (exp > 0UL)
{ {
if ((exp & 1UL) != 0UL) if ((exp & 1UL) != 0UL)
{ {
result = MontgomeryMultiply(result, baseVal, modulus, divisor.NPrime); result = MontgomeryMultiply(result, baseVal, modulus, divisor.NPrime);
} }
exp >>= 1; exp >>= 1;
if (exp == 0UL) if (exp == 0UL)
{ {
break; break;
} }
baseVal = MontgomeryMultiply(baseVal, baseVal, modulus, divisor.NPrime); baseVal = MontgomeryMultiply(baseVal, baseVal, modulus, divisor.NPrime);
} }
return MontgomeryMultiply(result, 1UL, modulus, divisor.NPrime); return MontgomeryMultiply(result, 1UL, modulus, divisor.NPrime);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong MontgomeryMultiply(ulong a, ulong b, ulong modulus, ulong nPrime) private static ulong MontgomeryMultiply(ulong a, ulong b, ulong modulus, ulong nPrime)
{ {
ulong tLow = unchecked(a * b); ulong tLow = unchecked(a * b);
ulong m = unchecked(tLow * nPrime); ulong m = unchecked(tLow * nPrime);
ulong sumLow = unchecked(tLow + m * modulus); ulong sumLow = unchecked(tLow + m * modulus);
ulong carry = sumLow < tLow ? 1UL : 0UL; ulong carry = sumLow < tLow ? 1UL : 0UL;
ulong sumHigh = unchecked(MultiplyHigh(a, b) + MultiplyHigh(m, modulus) + carry); ulong sumHigh = unchecked(MultiplyHigh(a, b) + MultiplyHigh(m, modulus) + carry);
ulong result = sumHigh; ulong result = sumHigh;
if (result >= modulus) if (result >= modulus)
{ {
result -= modulus; result -= modulus;
} }
return result; return result;
} }
private static void CheckKernelByPrime(Index1D index, MontgomeryDivisorData divisor, ArrayView<ulong> primes, ArrayView<byte> hits) private static void CheckKernelByPrime(Index1D index, MontgomeryDivisorData divisor, ArrayView<ulong> primes, ArrayView<byte> hits)
{ {
ulong prime = primes[index]; ulong prime = primes[index];
if (divisor.Modulus <= 1UL || (divisor.Modulus & 1UL) == 0UL) if (divisor.Modulus <= 1UL || (divisor.Modulus & 1UL) == 0UL)
{ {
hits[index] = 0; hits[index] = 0;
return; return;
} }
ulong pow = Pow2Mod(prime, divisor); ulong pow = Pow2Mod(prime, divisor);
hits[index] = pow == 1UL ? (byte)1 : (byte)0; hits[index] = pow == 1UL ? (byte)1 : (byte)0;
} }
private static MontgomeryDivisorData CreateMontgomeryDivisorData(ulong modulus) private static MontgomeryDivisorData CreateMontgomeryDivisorData(ulong modulus)
{ {
if (modulus <= 1UL || (modulus & 1UL) == 0UL) if (modulus <= 1UL || (modulus & 1UL) == 0UL)
{ {
return new MontgomeryDivisorData(modulus, 0UL, 0UL, 0UL); return new MontgomeryDivisorData(modulus, 0UL, 0UL, 0UL);
} }
ulong nPrime = ComputeMontgomeryNPrime(modulus); ulong nPrime = ComputeMontgomeryNPrime(modulus);
ulong montgomeryOne = ComputeMontgomeryResidue(1UL, modulus); ulong montgomeryOne = ComputeMontgomeryResidue(1UL, modulus);
ulong montgomeryTwo = ComputeMontgomeryResidue(2UL, modulus); ulong montgomeryTwo = ComputeMontgomeryResidue(2UL, modulus);
return new MontgomeryDivisorData(modulus, nPrime, montgomeryOne, montgomeryTwo); return new MontgomeryDivisorData(modulus, nPrime, montgomeryOne, montgomeryTwo);
} }
private static ulong ComputeMontgomeryResidue(ulong value, ulong modulus) private static ulong ComputeMontgomeryResidue(ulong value, ulong modulus)
{ {
UInt128 r = UInt128.One << 64; UInt128 r = UInt128.One << 64;
UInt128 result = ((UInt128)value * r) % modulus; UInt128 result = ((UInt128)value * r) % modulus;
return (ulong)result; return (ulong)result;
} }
private static ulong ComputeMontgomeryNPrime(ulong modulus) private static ulong ComputeMontgomeryNPrime(ulong modulus)
{ {
ulong inv = modulus; ulong inv = modulus;
inv *= unchecked(2UL - modulus * inv); inv *= unchecked(2UL - modulus * inv);
inv *= unchecked(2UL - modulus * inv); inv *= unchecked(2UL - modulus * inv);
inv *= unchecked(2UL - modulus * inv); inv *= unchecked(2UL - modulus * inv);
inv *= unchecked(2UL - modulus * inv); inv *= unchecked(2UL - modulus * inv);
inv *= unchecked(2UL - modulus * inv); inv *= unchecked(2UL - modulus * inv);
inv *= unchecked(2UL - modulus * inv); inv *= unchecked(2UL - modulus * inv);
return unchecked(0UL - inv); return unchecked(0UL - inv);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong MultiplyHigh(ulong x, ulong y) private static ulong MultiplyHigh(ulong x, ulong y)
{ {
ulong xLow = (uint)x; ulong xLow = (uint)x;
ulong xHigh = x >> 32; ulong xHigh = x >> 32;
ulong yLow = (uint)y; ulong yLow = (uint)y;
ulong yHigh = y >> 32; ulong yHigh = y >> 32;
ulong lowProduct = xLow * yLow; ulong lowProduct = xLow * yLow;
ulong mid = xHigh * yLow + (lowProduct >> 32); ulong mid = xHigh * yLow + (lowProduct >> 32);
ulong midLow = mid & 0xFFFFFFFFUL; ulong midLow = mid & 0xFFFFFFFFUL;
ulong midHigh = mid >> 32; ulong midHigh = mid >> 32;
midLow += xLow * yHigh; midLow += xLow * yHigh;
ulong resultHigh = xHigh * yHigh + midHigh + (midLow >> 32); ulong resultHigh = xHigh * yHigh + midHigh + (midLow >> 32);
return resultHigh; return resultHigh;
} }
public ulong DivisorLimit public ulong DivisorLimit
{ {