Hybrid device mode implemented as the current --mersenne=bydivisor CPU execution path. CPU path is now limited to CPU calculations, only.
This commit is contained in:
1 parent
353a121d03
commit
941733905b
49 files changed
+4965
-1726
No files matched your search
@@ -5,263 +5,329 @@ namespace PerfectNumbers.Core;
|
||||
|
||||
public static partial class ULongExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong Pow2MontgomeryModWindowedCpu(this ulong exponent, in MontgomeryDivisorData divisor, bool keepMontgomery)
|
||||
{
|
||||
ulong modulus = divisor.Modulus;
|
||||
// This should never happen in production code.
|
||||
// if (exponent == 0UL)
|
||||
// {
|
||||
// return keepMontgomery ? divisor.MontgomeryOne : 1UL % modulus;
|
||||
// }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong Pow2MontgomeryModWindowedKeepMontgomeryCpu(this ulong exponent, in MontgomeryDivisorData divisor)
|
||||
{
|
||||
ulong modulus = divisor.Modulus;
|
||||
// This should never happen in production code.
|
||||
// if (exponent == 0UL)
|
||||
// {
|
||||
// return keepMontgomery ? divisor.MontgomeryOne : 1UL % modulus;
|
||||
// }
|
||||
|
||||
// Small divisors come from the precomputed <= 4,000,000 snapshot and tiny rotation counts
|
||||
// appear when cycle reductions trigger. Route both cases through the single-bit ladder so the
|
||||
// CPU path avoids renting the window tables when a cheaper method is available.
|
||||
if (exponent <= Pow2WindowFallbackThreshold || modulus <= (ulong)PerfectNumberConstants.MaxQForDivisorCycles)
|
||||
{
|
||||
return Pow2MontgomeryModSingleBit(exponent, divisor, keepMontgomery);
|
||||
}
|
||||
// Small divisors come from the precomputed <= 4,000,000 snapshot and tiny rotation counts
|
||||
// appear when cycle reductions trigger. Route both cases through the single-bit ladder so the
|
||||
// CPU path avoids renting the window tables when a cheaper method is available.
|
||||
if (exponent <= Pow2WindowFallbackThreshold || modulus <= (ulong)PerfectNumberConstants.MaxQForDivisorCycles)
|
||||
{
|
||||
return Pow2MontgomeryModSingleBitKeepMontgomery(exponent, divisor);
|
||||
}
|
||||
|
||||
int bitLength = GetPortableBitLengthCpu(exponent);
|
||||
int windowSize = GetWindowSizeCpu(bitLength);
|
||||
int oddPowerCount = 1 << (windowSize - 1);
|
||||
int bitLength = GetPortableBitLengthCpu(exponent);
|
||||
int windowSize = GetWindowSizeCpu(bitLength);
|
||||
int oddPowerCount = 1 << (windowSize - 1);
|
||||
|
||||
ulong result = divisor.MontgomeryOne;
|
||||
ulong nPrime = divisor.NPrime;
|
||||
ulong result = divisor.MontgomeryOne;
|
||||
ulong nPrime = divisor.NPrime;
|
||||
|
||||
ulong[] oddPowersArray = ThreadStaticPools.UlongPool.Rent(oddPowerCount);
|
||||
Span<ulong> oddPowers = oddPowersArray.AsSpan(0, oddPowerCount);
|
||||
InitializeMontgomeryOddPowersCpu(divisor, modulus, nPrime, oddPowers);
|
||||
ulong[] oddPowersArray = ThreadStaticPools.UlongPool.Rent(oddPowerCount);
|
||||
Span<ulong> oddPowers = oddPowersArray.AsSpan(0, oddPowerCount);
|
||||
InitializeMontgomeryOddPowersCpu(divisor, modulus, nPrime, oddPowers);
|
||||
|
||||
int index = bitLength - 1;
|
||||
while (index >= 0)
|
||||
{
|
||||
if (((exponent >> index) & 1UL) == 0UL)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(result, modulus, nPrime);
|
||||
index--;
|
||||
continue;
|
||||
}
|
||||
int index = bitLength - 1;
|
||||
while (index >= 0)
|
||||
{
|
||||
if (((exponent >> index) & 1UL) == 0UL)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(result, modulus, nPrime);
|
||||
index--;
|
||||
continue;
|
||||
}
|
||||
|
||||
int windowStart = index - windowSize + 1;
|
||||
if (windowStart < 0)
|
||||
{
|
||||
windowStart = 0;
|
||||
}
|
||||
int windowStart = index - windowSize + 1;
|
||||
if (windowStart < 0)
|
||||
{
|
||||
windowStart = 0;
|
||||
}
|
||||
|
||||
while (((exponent >> windowStart) & 1UL) == 0UL)
|
||||
{
|
||||
windowStart++;
|
||||
}
|
||||
while (((exponent >> windowStart) & 1UL) == 0UL)
|
||||
{
|
||||
windowStart++;
|
||||
}
|
||||
|
||||
int windowLength = index - windowStart + 1;
|
||||
for (int square = 0; square < windowLength; square++)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(result, modulus, nPrime);
|
||||
}
|
||||
int windowLength = index - windowStart + 1;
|
||||
for (int square = 0; square < windowLength; square++)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(result, modulus, nPrime);
|
||||
}
|
||||
|
||||
ulong mask = (1UL << windowLength) - 1UL;
|
||||
ulong windowValue = (exponent >> windowStart) & mask;
|
||||
int tableIndex = (int)((windowValue - 1UL) >> 1);
|
||||
ulong multiplier = oddPowers[tableIndex];
|
||||
result = result.MontgomeryMultiplyCpu(multiplier, modulus, nPrime);
|
||||
ulong mask = (1UL << windowLength) - 1UL;
|
||||
ulong windowValue = (exponent >> windowStart) & mask;
|
||||
int tableIndex = (int)((windowValue - 1UL) >> 1);
|
||||
ulong multiplier = oddPowers[tableIndex];
|
||||
result = result.MontgomeryMultiplyCpu(multiplier, modulus, nPrime);
|
||||
|
||||
index = windowStart - 1;
|
||||
}
|
||||
index = windowStart - 1;
|
||||
}
|
||||
|
||||
ThreadStaticPools.UlongPool.Return(oddPowersArray);
|
||||
ThreadStaticPools.UlongPool.Return(oddPowersArray);
|
||||
|
||||
if (!keepMontgomery)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(1UL, modulus, nPrime);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong Pow2MontgomeryModWindowedConvertToStandardCpu(this ulong exponent, in MontgomeryDivisorData divisor)
|
||||
{
|
||||
ulong modulus = divisor.Modulus;
|
||||
// This should never happen in production code.
|
||||
// if (exponent == 0UL)
|
||||
// {
|
||||
// return keepMontgomery ? divisor.MontgomeryOne : 1UL % modulus;
|
||||
// }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void InitializeMontgomeryOddPowersCpu(in MontgomeryDivisorData divisor, ulong modulus, ulong nPrime, Span<ulong> oddPowers)
|
||||
{
|
||||
oddPowers[0] = divisor.MontgomeryTwo;
|
||||
if (oddPowers.Length == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Small divisors come from the precomputed <= 4,000,000 snapshot and tiny rotation counts
|
||||
// appear when cycle reductions trigger. Route both cases through the single-bit ladder so the
|
||||
// CPU path avoids renting the window tables when a cheaper method is available.
|
||||
if (exponent <= Pow2WindowFallbackThreshold || modulus <= (ulong)PerfectNumberConstants.MaxQForDivisorCycles)
|
||||
{
|
||||
return Pow2MontgomeryModSingleBitConvertToStandard(exponent, divisor);
|
||||
}
|
||||
|
||||
ulong previous;
|
||||
ulong square = divisor.MontgomeryTwoSquared;
|
||||
int bitLength = GetPortableBitLengthCpu(exponent);
|
||||
int windowSize = GetWindowSizeCpu(bitLength);
|
||||
int oddPowerCount = 1 << (windowSize - 1);
|
||||
|
||||
for (int i = 1; i < oddPowers.Length; i++)
|
||||
{
|
||||
previous = oddPowers[i - 1];
|
||||
oddPowers[i] = previous.MontgomeryMultiplyCpu(square, modulus, nPrime);
|
||||
}
|
||||
}
|
||||
ulong result = divisor.MontgomeryOne;
|
||||
ulong nPrime = divisor.NPrime;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong Pow2MontgomeryModWithCycleCpu(this ulong exponent, ulong cycleLength, in MontgomeryDivisorData divisor)
|
||||
{
|
||||
ulong rotationCount = exponent % cycleLength;
|
||||
return Pow2MontgomeryModWindowedCpu(rotationCount, divisor, keepMontgomery: false);
|
||||
}
|
||||
ulong[] oddPowersArray = ThreadStaticPools.UlongPool.Rent(oddPowerCount);
|
||||
Span<ulong> oddPowers = oddPowersArray.AsSpan(0, oddPowerCount);
|
||||
InitializeMontgomeryOddPowersCpu(divisor, modulus, nPrime, oddPowers);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong Pow2MontgomeryModFromCycleRemainderCpu(this ulong reducedExponent, in MontgomeryDivisorData divisor)
|
||||
{
|
||||
return Pow2MontgomeryModWindowedCpu(reducedExponent, divisor, keepMontgomery: false);
|
||||
}
|
||||
int index = bitLength - 1;
|
||||
while (index >= 0)
|
||||
{
|
||||
if (((exponent >> index) & 1UL) == 0UL)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(result, modulus, nPrime);
|
||||
index--;
|
||||
continue;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong CalculateMersenneDivisorCycleLengthUnrolledHexCpu(this ulong divisor)
|
||||
{
|
||||
// EvenPerfectBitScanner only routes odd divisors here; keep the guard commented out for benchmarks.
|
||||
// if ((divisor & (divisor - 1UL)) == 0UL)
|
||||
// {
|
||||
// return 1UL;
|
||||
// }
|
||||
int windowStart = index - windowSize + 1;
|
||||
if (windowStart < 0)
|
||||
{
|
||||
windowStart = 0;
|
||||
}
|
||||
|
||||
ulong order = 1UL;
|
||||
ulong pow = 2UL;
|
||||
while (((exponent >> windowStart) & 1UL) == 0UL)
|
||||
{
|
||||
windowStart++;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
int windowLength = index - windowStart + 1;
|
||||
for (int square = 0; square < windowLength; square++)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(result, modulus, nPrime);
|
||||
}
|
||||
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
ulong mask = (1UL << windowLength) - 1UL;
|
||||
ulong windowValue = (exponent >> windowStart) & mask;
|
||||
int tableIndex = (int)((windowValue - 1UL) >> 1);
|
||||
ulong multiplier = oddPowers[tableIndex];
|
||||
result = result.MontgomeryMultiplyCpu(multiplier, modulus, nPrime);
|
||||
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
index = windowStart - 1;
|
||||
}
|
||||
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
ThreadStaticPools.UlongPool.Return(oddPowersArray);
|
||||
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
result = result.MontgomeryMultiplyCpu(1UL, modulus, nPrime);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void InitializeMontgomeryOddPowersCpu(in MontgomeryDivisorData divisor, ulong modulus, ulong nPrime, Span<ulong> oddPowers)
|
||||
{
|
||||
oddPowers[0] = divisor.MontgomeryTwo;
|
||||
if (oddPowers.Length == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
ulong previous;
|
||||
ulong square = divisor.MontgomeryTwoSquared;
|
||||
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
for (int i = 1; i < oddPowers.Length; i++)
|
||||
{
|
||||
previous = oddPowers[i - 1];
|
||||
oddPowers[i] = previous.MontgomeryMultiplyCpu(square, modulus, nPrime);
|
||||
}
|
||||
}
|
||||
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong Pow2MontgomeryModWithCycleConvertToStandardCpu(this ulong exponent, ulong cycleLength, in MontgomeryDivisorData divisor)
|
||||
{
|
||||
ulong rotationCount = exponent % cycleLength;
|
||||
return Pow2MontgomeryModWindowedConvertToStandardCpu(rotationCount, divisor);
|
||||
}
|
||||
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong Pow2MontgomeryModFromCycleRemainderCpu(this ulong reducedExponent, in MontgomeryDivisorData divisor)
|
||||
{
|
||||
return Pow2MontgomeryModWindowedConvertToStandardCpu(reducedExponent, divisor);
|
||||
}
|
||||
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong CalculateMersenneDivisorCycleLengthUnrolledHexCpu(this ulong divisor)
|
||||
{
|
||||
// EvenPerfectBitScanner only routes odd divisors here; keep the guard commented out for benchmarks.
|
||||
// if ((divisor & (divisor - 1UL)) == 0UL)
|
||||
// {
|
||||
// return 1UL;
|
||||
// }
|
||||
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
ulong order = 1UL;
|
||||
ulong pow = 2UL;
|
||||
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool AdvanceMersenneDivisorCycleStepCpu(ref ulong pow, ulong divisor, ref ulong order)
|
||||
{
|
||||
pow += pow;
|
||||
if (pow >= divisor)
|
||||
{
|
||||
pow -= divisor;
|
||||
}
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
|
||||
order++;
|
||||
return pow == 1UL;
|
||||
}
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int GetPortableBitLengthCpu(ulong value)
|
||||
{
|
||||
// Keep this commented out. It will never happen in production code.
|
||||
// if (value == 0UL)
|
||||
// {
|
||||
// return 0;
|
||||
// }
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
|
||||
return 64 - BitOperations.LeadingZeroCount(value);
|
||||
}
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int GetWindowSizeCpu(int bitLength)
|
||||
{
|
||||
if (bitLength <= 6)
|
||||
{
|
||||
return Math.Max(bitLength, 1);
|
||||
}
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
|
||||
if (bitLength <= 23)
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
|
||||
if (bitLength <= 79)
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
|
||||
if (bitLength <= 239)
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
|
||||
if (bitLength <= 671)
|
||||
{
|
||||
return 7;
|
||||
}
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
|
||||
return PerfectNumberConstants.Pow2WindowSize;
|
||||
}
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
|
||||
if (AdvanceMersenneDivisorCycleStepCpu(ref pow, divisor, ref order))
|
||||
{
|
||||
return order;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool AdvanceMersenneDivisorCycleStepCpu(ref ulong pow, ulong divisor, ref ulong order)
|
||||
{
|
||||
pow += pow;
|
||||
if (pow >= divisor)
|
||||
{
|
||||
pow -= divisor;
|
||||
}
|
||||
|
||||
order++;
|
||||
return pow == 1UL;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int GetPortableBitLengthCpu(ulong value)
|
||||
{
|
||||
// Keep this commented out. It will never happen in production code.
|
||||
// if (value == 0UL)
|
||||
// {
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
return 64 - BitOperations.LeadingZeroCount(value);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int GetWindowSizeCpu(int bitLength)
|
||||
{
|
||||
if (bitLength <= 6)
|
||||
{
|
||||
return Math.Max(bitLength, 1);
|
||||
}
|
||||
|
||||
if (bitLength <= 23)
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
if (bitLength <= 79)
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
|
||||
if (bitLength <= 239)
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
|
||||
if (bitLength <= 671)
|
||||
{
|
||||
return 7;
|
||||
}
|
||||
|
||||
return PerfectNumberConstants.Pow2WindowSize;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user