diff --git a/EvenPerfectBitScanner.Benchmarks/GpuModularArithmeticBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/GpuModularArithmeticBenchmarks.cs index 466cd70..9367ec3 100644 --- a/EvenPerfectBitScanner.Benchmarks/GpuModularArithmeticBenchmarks.cs +++ b/EvenPerfectBitScanner.Benchmarks/GpuModularArithmeticBenchmarks.cs @@ -9,12 +9,10 @@ namespace EvenPerfectBitScanner.Benchmarks; public class GpuModularArithmeticBenchmarks { private const int MaxBatchSize = 512; - private readonly ulong[] _exponents = new ulong[MaxBatchSize]; - private readonly ulong[] _moduli = new ulong[MaxBatchSize]; - private readonly ulong[] _mersenneModuli = new ulong[MaxBatchSize]; - private readonly int[] _mersenneBitWidths = new int[MaxBatchSize]; - - private readonly Random _random = new(7); + private ulong[] _exponents = Array.Empty(); + private ulong[] _moduli = Array.Empty(); + private ulong[] _mersenneModuli = Array.Empty(); + private int[] _mersenneBitWidths = Array.Empty(); [Params(32, 256)] public int BatchSize { get; set; } @@ -22,14 +20,69 @@ public class GpuModularArithmeticBenchmarks [GlobalSetup] public void Setup() { - for (int i = 0; i < MaxBatchSize; i++) - { - _exponents[i] = NextExponent(); - _moduli[i] = NextOddModulus(); + ModularArithmeticSampleData data = ModularArithmeticSampleCache.Instance; + _exponents = data.Exponents; + _moduli = data.Moduli; + _mersenneModuli = data.MersenneModuli; + _mersenneBitWidths = data.MersenneBitWidths; + } - int bits = _random.Next(8, 62); - _mersenneBitWidths[i] = bits; - _mersenneModuli[i] = (1UL << bits) - 1UL; + private sealed class ModularArithmeticSampleData + { + public ModularArithmeticSampleData(ulong[] exponents, ulong[] moduli, ulong[] mersenneModuli, int[] mersenneBitWidths) + { + Exponents = exponents; + Moduli = moduli; + MersenneModuli = mersenneModuli; + MersenneBitWidths = mersenneBitWidths; + } + + public ulong[] Exponents { get; } + public ulong[] Moduli { get; } + public ulong[] MersenneModuli { get; } + public int[] MersenneBitWidths { get; } + } + + private static class ModularArithmeticSampleCache + { + private static readonly Lazy Cache = new(Create); + + public static ModularArithmeticSampleData Instance => Cache.Value; + + private static ModularArithmeticSampleData Create() + { + Random random = new(7); + var exponents = new ulong[MaxBatchSize]; + var moduli = new ulong[MaxBatchSize]; + var mersenneModuli = new ulong[MaxBatchSize]; + var mersenneBitWidths = new int[MaxBatchSize]; + + for (int i = 0; i < MaxBatchSize; i++) + { + exponents[i] = NextExponent(random); + moduli[i] = NextOddModulus(random); + int bits = random.Next(8, 62); + mersenneBitWidths[i] = bits; + mersenneModuli[i] = (1UL << bits) - 1UL; + } + + return new ModularArithmeticSampleData(exponents, moduli, mersenneModuli, mersenneBitWidths); + } + + private static ulong NextExponent(Random random) + { + return (ulong)random.Next(24, 63); + } + + private static ulong NextOddModulus(Random random) + { + ulong value = (ulong)random.NextInt64(5L, long.MaxValue); + if ((value & 1UL) == 0UL) + { + value++; + } + + return value; } } @@ -69,22 +122,6 @@ public class GpuModularArithmeticBenchmarks return checksum; } - private ulong NextExponent() - { - return (ulong)_random.Next(24, 63); - } - - private ulong NextOddModulus() - { - ulong value = (ulong)_random.NextInt64(5L, long.MaxValue); - if ((value & 1UL) == 0UL) - { - value++; - } - - return value; - } - private static ulong Pow2ModBaseline(ulong exponent, ulong modulus) { if (modulus <= 1UL) diff --git a/EvenPerfectBitScanner.Benchmarks/MontgomeryMultiplyBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/MontgomeryMultiplyBenchmarks.cs index a3fe844..9f12ad8 100644 --- a/EvenPerfectBitScanner.Benchmarks/MontgomeryMultiplyBenchmarks.cs +++ b/EvenPerfectBitScanner.Benchmarks/MontgomeryMultiplyBenchmarks.cs @@ -9,12 +9,10 @@ public class MontgomeryMultiplyBenchmarks { private const int MaxBatchSize = 1024; - private readonly ulong[] _operandsA = new ulong[MaxBatchSize]; - private readonly ulong[] _operandsB = new ulong[MaxBatchSize]; - private readonly ulong[] _moduli = new ulong[MaxBatchSize]; - private readonly ulong[] _nPrimes = new ulong[MaxBatchSize]; - - private readonly Random _random = new(1979); + private ulong[] _operandsA = Array.Empty(); + private ulong[] _operandsB = Array.Empty(); + private ulong[] _moduli = Array.Empty(); + private ulong[] _nPrimes = Array.Empty(); [Params(64, 256, 1024)] public int BatchSize { get; set; } @@ -22,13 +20,64 @@ public class MontgomeryMultiplyBenchmarks [GlobalSetup] public void Setup() { - for (int i = 0; i < MaxBatchSize; i++) + MontgomerySampleData data = MontgomerySampleDataCache.Instance; + _operandsA = data.OperandsA; + _operandsB = data.OperandsB; + _moduli = data.Moduli; + _nPrimes = data.NPrimes; + } + + private sealed class MontgomerySampleData + { + public MontgomerySampleData(ulong[] operandsA, ulong[] operandsB, ulong[] moduli, ulong[] nPrimes) { - ulong modulus = NextOddModulus(); - _moduli[i] = modulus; - _nPrimes[i] = ComputeMontgomeryNPrime(modulus); - _operandsA[i] = (ulong)_random.NextInt64(0, (long)modulus); - _operandsB[i] = (ulong)_random.NextInt64(0, (long)modulus); + OperandsA = operandsA; + OperandsB = operandsB; + Moduli = moduli; + NPrimes = nPrimes; + } + + public ulong[] OperandsA { get; } + public ulong[] OperandsB { get; } + public ulong[] Moduli { get; } + public ulong[] NPrimes { get; } + } + + private static class MontgomerySampleDataCache + { + private static readonly Lazy Cache = new(Create); + + public static MontgomerySampleData Instance => Cache.Value; + + private static MontgomerySampleData Create() + { + Random random = new(1979); + var operandsA = new ulong[MaxBatchSize]; + var operandsB = new ulong[MaxBatchSize]; + var moduli = new ulong[MaxBatchSize]; + var nPrimes = new ulong[MaxBatchSize]; + + for (int i = 0; i < MaxBatchSize; i++) + { + ulong modulus = NextOddModulus(random); + moduli[i] = modulus; + nPrimes[i] = ComputeMontgomeryNPrime(modulus); + operandsA[i] = (ulong)random.NextInt64(0, (long)modulus); + operandsB[i] = (ulong)random.NextInt64(0, (long)modulus); + } + + return new MontgomerySampleData(operandsA, operandsB, moduli, nPrimes); + } + + private static ulong NextOddModulus(Random random) + { + ulong modulus = (ulong)random.NextInt64(3, long.MaxValue); + if ((modulus & 1UL) == 0UL) + { + modulus++; + } + + return modulus; } } @@ -68,17 +117,6 @@ public class MontgomeryMultiplyBenchmarks return checksum; } - private ulong NextOddModulus() - { - ulong modulus = (ulong)_random.NextInt64(3, long.MaxValue); - if ((modulus & 1UL) == 0UL) - { - modulus++; - } - - return modulus; - } - private static ulong OriginalMontgomeryMultiply(ulong a, ulong b, ulong modulus, ulong nPrime) { ulong tLow = unchecked(a * b); diff --git a/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModBenchmarks.cs index 3c5680f..f031f83 100644 --- a/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModBenchmarks.cs +++ b/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModBenchmarks.cs @@ -26,20 +26,17 @@ public class Pow2MontgomeryModBenchmarks private const ulong OneShiftedLeft63 = 1UL << 63; private const long OneShiftedLeft60 = 1L << 60; - private readonly MontgomeryDivisorData[] _smallDivisors = new MontgomeryDivisorData[SampleCount]; - private readonly MontgomeryDivisorData[] _largeDivisors = new MontgomeryDivisorData[SampleCount]; - private readonly MontgomeryDivisorData[] _veryLargeDivisors = new MontgomeryDivisorData[SampleCount]; - private readonly UInt128[] _wideModuli = new UInt128[SampleCount]; - private readonly ulong[] _smallExponents = new ulong[SampleCount]; - private readonly ulong[] _largeExponents = new ulong[SampleCount]; - private readonly ulong[] _veryLargeExponents = new ulong[SampleCount]; - private readonly ulong[] _smallCycles = new ulong[SampleCount]; - private readonly ulong[] _largeCycles = new ulong[SampleCount]; - private readonly ulong[] _veryLargeCycles = new ulong[SampleCount]; - private readonly UInt128[] _wideCycles = new UInt128[SampleCount]; - - private readonly Random _random = new(13); - private readonly Random _heuristicRandom = new(113); + private MontgomeryDivisorData[] _smallDivisors = Array.Empty(); + private MontgomeryDivisorData[] _largeDivisors = Array.Empty(); + private MontgomeryDivisorData[] _veryLargeDivisors = Array.Empty(); + private UInt128[] _wideModuli = Array.Empty(); + private ulong[] _smallExponents = Array.Empty(); + private ulong[] _largeExponents = Array.Empty(); + private ulong[] _veryLargeExponents = Array.Empty(); + private ulong[] _smallCycles = Array.Empty(); + private ulong[] _largeCycles = Array.Empty(); + private ulong[] _veryLargeCycles = Array.Empty(); + private UInt128[] _wideCycles = Array.Empty(); private ulong? _previousPrimeOrder; private UInt128? _previousWidePrimeOrder; @@ -56,34 +53,18 @@ public class Pow2MontgomeryModBenchmarks [GlobalSetup] public void Setup() { - for (int i = 0; i < SampleCount; i++) - { -#if DEBUG - Console.WriteLine($"Generating sample {i}"); -#endif - ulong smallModulus = NextSmallOddModulus(); - _smallDivisors[i] = CreateMontgomeryDivisorData(smallModulus); - _smallExponents[i] = NextSmallExponent(); - _smallCycles[i] = MersenneDivisorCycles.CalculateCycleLength(smallModulus, MontgomeryDivisorData.FromModulus(smallModulus)); - - (ulong largeModulus, ulong largeCycle) = NextLargeModulusAndCycle(); - _largeDivisors[i] = CreateMontgomeryDivisorData(largeModulus); - _largeExponents[i] = NextLargeExponent(); - _largeCycles[i] = largeCycle; - - ulong veryLargeModulus = NextVeryLargeOddModulus(); - _veryLargeDivisors[i] = CreateMontgomeryDivisorData(veryLargeModulus); - _veryLargeExponents[i] = NextVeryLargeExponent(); -#if DEBUG - Console.WriteLine($"Calculating cycle length {veryLargeModulus}"); -#endif - _veryLargeCycles[i] = CalculateCycleLengthWithHeuristics(veryLargeModulus); - - Console.WriteLine($"Generating wide sample {i}"); - UInt128 wideModulus = NextWideOddModulus(); - _wideModuli[i] = wideModulus; - _wideCycles[i] = CalculateCycleLengthWithHeuristics(wideModulus); - } + SampleCollections cache = SampleCollectionsProvider.Instance; + _smallDivisors = cache.SmallDivisors; + _largeDivisors = cache.LargeDivisors; + _veryLargeDivisors = cache.VeryLargeDivisors; + _wideModuli = cache.WideModuli; + _smallExponents = cache.SmallExponents; + _largeExponents = cache.LargeExponents; + _veryLargeExponents = cache.VeryLargeExponents; + _smallCycles = cache.SmallCycles; + _largeCycles = cache.LargeCycles; + _veryLargeCycles = cache.VeryLargeCycles; + _wideCycles = cache.WideCycles; } /// @@ -230,7 +211,6 @@ public class Pow2MontgomeryModBenchmarks for (int i = 0; i < SampleCount; i++) { UInt128 cycle = CalculateCycleLengthWithHeuristics(_wideModuli[i]); - _wideCycles[i] = cycle; checksum ^= cycle; } @@ -363,50 +343,199 @@ public class Pow2MontgomeryModBenchmarks } } - private ulong NextSmallExponent() => (ulong)_random.NextInt64(1L, 1_000_000L); - - private ulong NextLargeExponent() => (ulong)_random.NextInt64(OneShiftedLeft60, long.MaxValue); - - private ulong NextVeryLargeExponent() + private sealed class SampleCollections { - Span buffer = stackalloc byte[8]; - _heuristicRandom.NextBytes(buffer); - ulong value = checked(BinaryPrimitives.ReadUInt64LittleEndian(buffer) | OneShiftedLeft63); - return value; + public SampleCollections( + MontgomeryDivisorData[] smallDivisors, + MontgomeryDivisorData[] largeDivisors, + MontgomeryDivisorData[] veryLargeDivisors, + UInt128[] wideModuli, + ulong[] smallExponents, + ulong[] largeExponents, + ulong[] veryLargeExponents, + ulong[] smallCycles, + ulong[] largeCycles, + ulong[] veryLargeCycles, + UInt128[] wideCycles) + { + SmallDivisors = smallDivisors; + LargeDivisors = largeDivisors; + VeryLargeDivisors = veryLargeDivisors; + WideModuli = wideModuli; + SmallExponents = smallExponents; + LargeExponents = largeExponents; + VeryLargeExponents = veryLargeExponents; + SmallCycles = smallCycles; + LargeCycles = largeCycles; + VeryLargeCycles = veryLargeCycles; + WideCycles = wideCycles; + } + + public MontgomeryDivisorData[] SmallDivisors { get; } + public MontgomeryDivisorData[] LargeDivisors { get; } + public MontgomeryDivisorData[] VeryLargeDivisors { get; } + public UInt128[] WideModuli { get; } + public ulong[] SmallExponents { get; } + public ulong[] LargeExponents { get; } + public ulong[] VeryLargeExponents { get; } + public ulong[] SmallCycles { get; } + public ulong[] LargeCycles { get; } + public ulong[] VeryLargeCycles { get; } + public UInt128[] WideCycles { get; } } - private ulong NextSmallOddModulus() + private static class SampleCollectionsProvider { - ulong value = (ulong)_random.NextInt64(3L, 1_000_000L); - return value | 1UL; - } + private static readonly Lazy Cache = new(Create); - private (ulong modulus, ulong cycleLength) NextLargeModulusAndCycle() - { - int bitLength = _random.Next(48, 64); - ulong modulus = (1UL << bitLength) - 1UL; - return (modulus, (ulong)bitLength); - } + public static SampleCollections Instance => Cache.Value; - private ulong NextVeryLargeOddModulus() - { - Span buffer = stackalloc byte[8]; - _heuristicRandom.NextBytes(buffer); - ulong value = BinaryPrimitives.ReadUInt64LittleEndian(buffer) | OneShiftedLeft63 | 1UL; - return value; - } + private static SampleCollections Create() + { + Random random = new(13); + Random heuristicRandom = new(113); - private UInt128 NextWideOddModulus() - { - Span buffer = stackalloc byte[16]; - _heuristicRandom.NextBytes(buffer); + var smallDivisors = new MontgomeryDivisorData[SampleCount]; + var largeDivisors = new MontgomeryDivisorData[SampleCount]; + var veryLargeDivisors = new MontgomeryDivisorData[SampleCount]; + var wideModuli = new UInt128[SampleCount]; + var smallExponents = new ulong[SampleCount]; + var largeExponents = new ulong[SampleCount]; + var veryLargeExponents = new ulong[SampleCount]; + var smallCycles = new ulong[SampleCount]; + var largeCycles = new ulong[SampleCount]; + var veryLargeCycles = new ulong[SampleCount]; + var wideCycles = new UInt128[SampleCount]; - ulong low = BinaryPrimitives.ReadUInt64LittleEndian(buffer); - Span highSlice = buffer.Slice(8, 8); - ulong high = BinaryPrimitives.ReadUInt64LittleEndian(highSlice) | (1UL << 63); + ulong? previousPrimeOrder = null; + UInt128? previousWidePrimeOrder = null; - UInt128 value = ((UInt128)high << 64) | low; - return value | UInt128.One; + for (int i = 0; i < SampleCount; i++) + { + ulong smallModulus = NextSmallOddModulus(random); + smallDivisors[i] = CreateMontgomeryDivisorData(smallModulus); + smallExponents[i] = NextSmallExponent(random); + smallCycles[i] = MersenneDivisorCycles.CalculateCycleLength(smallModulus, MontgomeryDivisorData.FromModulus(smallModulus)); + + (ulong largeModulus, ulong largeCycle) = NextLargeModulusAndCycle(random); + largeDivisors[i] = CreateMontgomeryDivisorData(largeModulus); + largeExponents[i] = NextLargeExponent(random); + largeCycles[i] = largeCycle; + + ulong veryLargeModulus = NextVeryLargeOddModulus(heuristicRandom); + veryLargeDivisors[i] = CreateMontgomeryDivisorData(veryLargeModulus); + veryLargeExponents[i] = NextVeryLargeExponent(heuristicRandom); + veryLargeCycles[i] = CalculateCycleLengthWithHeuristics(veryLargeModulus, ref previousPrimeOrder); + + UInt128 wideModulus = NextWideOddModulus(heuristicRandom); + wideModuli[i] = wideModulus; + wideCycles[i] = CalculateCycleLengthWithHeuristics(wideModulus, ref previousWidePrimeOrder); + } + + return new SampleCollections( + smallDivisors, + largeDivisors, + veryLargeDivisors, + wideModuli, + smallExponents, + largeExponents, + veryLargeExponents, + smallCycles, + largeCycles, + veryLargeCycles, + wideCycles); + } + + private static ulong NextSmallExponent(Random random) => (ulong)random.NextInt64(1L, 1_000_000L); + + private static ulong NextLargeExponent(Random random) => (ulong)random.NextInt64(OneShiftedLeft60, long.MaxValue); + + private static ulong NextVeryLargeExponent(Random random) + { + Span buffer = stackalloc byte[8]; + random.NextBytes(buffer); + ulong value = checked(BinaryPrimitives.ReadUInt64LittleEndian(buffer) | OneShiftedLeft63); + return value; + } + + private static ulong NextSmallOddModulus(Random random) + { + ulong value = (ulong)random.NextInt64(3L, 1_000_000L); + return value | 1UL; + } + + private static (ulong Modulus, ulong CycleLength) NextLargeModulusAndCycle(Random random) + { + int bitLength = random.Next(48, 64); + ulong modulus = (1UL << bitLength) - 1UL; + return (modulus, (ulong)bitLength); + } + + private static ulong NextVeryLargeOddModulus(Random random) + { + Span buffer = stackalloc byte[8]; + random.NextBytes(buffer); + ulong value = BinaryPrimitives.ReadUInt64LittleEndian(buffer) | OneShiftedLeft63 | 1UL; + return value; + } + + private static UInt128 NextWideOddModulus(Random random) + { + Span buffer = stackalloc byte[16]; + random.NextBytes(buffer); + ulong low = BinaryPrimitives.ReadUInt64LittleEndian(buffer); + ulong high = BinaryPrimitives.ReadUInt64LittleEndian(buffer[8..]) | (1UL << 63); + UInt128 value = ((UInt128)high << 64) | low; + return value | UInt128.One; + } + + private static ulong CalculateCycleLengthWithHeuristics(ulong modulus, ref ulong? previousPrimeOrder) + { + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(modulus); + if (modulus <= 1UL || (modulus & 1UL) == 0UL) + { + return MersenneDivisorCycles.CalculateCycleLength(modulus, divisorData); + } + + ulong order = PrimeOrderCalculator.Calculate( + modulus, + previousPrimeOrder, + divisorData, + PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, + PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); + + if (order != 0UL) + { + previousPrimeOrder = order; + return order; + } + + previousPrimeOrder = null; + return MersenneDivisorCycles.CalculateCycleLength(modulus, divisorData); + } + + private static UInt128 CalculateCycleLengthWithHeuristics(UInt128 modulus, ref UInt128? previousWidePrimeOrder) + { + if (modulus <= UInt128.One || (modulus & UInt128.One) == UInt128.Zero) + { + return MersenneDivisorCycles.GetCycle(modulus); + } + + UInt128 order = PrimeOrderCalculator.Calculate( + modulus, + previousWidePrimeOrder, + PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, + PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); + + if (order != UInt128.Zero) + { + previousWidePrimeOrder = order; + return order; + } + + previousWidePrimeOrder = null; + return MersenneDivisorCycles.GetCycle(modulus); + } } private static MontgomeryDivisorData CreateMontgomeryDivisorData(ulong modulus) diff --git a/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModUInt128Benchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModUInt128Benchmarks.cs index cc84563..bffcaf8 100644 --- a/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModUInt128Benchmarks.cs +++ b/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModUInt128Benchmarks.cs @@ -13,21 +13,86 @@ public class Pow2MontgomeryModUInt128Benchmarks private const int SampleCount = 256; private const ulong Pow2WindowFallbackThreshold = 32UL; - private readonly UInt128[] _moduli = new UInt128[SampleCount]; - private readonly UInt128[] _exponents = new UInt128[SampleCount]; - - private readonly Random _random = new(19); + private UInt128[] _moduli = Array.Empty(); + private UInt128[] _exponents = Array.Empty(); [GlobalSetup] public void Setup() { - for (int i = 0; i < SampleCount; i++) - { - UInt128 modulus = NextWideOddModulus(); - _moduli[i] = modulus; + WideSampleData cache = WideSampleDataCache.Instance; + _moduli = cache.Moduli; + _exponents = cache.Exponents; + } - UInt128 exponent = NextWideExponent(); - _exponents[i] = exponent; + private sealed class WideSampleData + { + public WideSampleData(UInt128[] moduli, UInt128[] exponents) + { + Moduli = moduli; + Exponents = exponents; + } + + public UInt128[] Moduli { get; } + public UInt128[] Exponents { get; } + } + + private static class WideSampleDataCache + { + private static readonly Lazy Cache = new(Create); + + public static WideSampleData Instance => Cache.Value; + + private static WideSampleData Create() + { + Random random = new(19); + var moduli = new UInt128[SampleCount]; + var exponents = new UInt128[SampleCount]; + + for (int i = 0; i < SampleCount; i++) + { + moduli[i] = NextWideOddModulus(random); + exponents[i] = NextWideExponent(random); + } + + return new WideSampleData(moduli, exponents); + } + + private static UInt128 NextWideOddModulus(Random random) + { + Span buffer = stackalloc byte[16]; + random.NextBytes(buffer); + + ulong low = BinaryPrimitives.ReadUInt64LittleEndian(buffer); + ulong high = BinaryPrimitives.ReadUInt64LittleEndian(buffer[8..]); + if (high == 0UL) + { + high = 1UL; + } + + UInt128 modulus = ((UInt128)high << 64) | low; + modulus |= UInt128.One; + + if (modulus <= UInt128.One) + { + modulus += 2UL; + } + + return modulus; + } + + private static UInt128 NextWideExponent(Random random) + { + Span buffer = stackalloc byte[16]; + random.NextBytes(buffer); + + ulong low = BinaryPrimitives.ReadUInt64LittleEndian(buffer); + ulong high = BinaryPrimitives.ReadUInt64LittleEndian(buffer[8..]); + if (high == 0UL) + { + high = 1UL; + } + + return ((UInt128)high << 64) | low; } } @@ -63,44 +128,6 @@ public class Pow2MontgomeryModUInt128Benchmarks return checksum; } - private UInt128 NextWideOddModulus() - { - Span buffer = stackalloc byte[16]; - _random.NextBytes(buffer); - - ulong low = BinaryPrimitives.ReadUInt64LittleEndian(buffer); - ulong high = BinaryPrimitives.ReadUInt64LittleEndian(buffer[8..]); - if (high == 0UL) - { - high = 1UL; - } - - UInt128 modulus = ((UInt128)high << 64) | low; - modulus |= UInt128.One; - - if (modulus <= UInt128.One) - { - modulus += 2UL; - } - - return modulus; - } - - private UInt128 NextWideExponent() - { - Span buffer = stackalloc byte[16]; - _random.NextBytes(buffer); - - ulong low = BinaryPrimitives.ReadUInt64LittleEndian(buffer); - ulong high = BinaryPrimitives.ReadUInt64LittleEndian(buffer[8..]); - if (high == 0UL) - { - high = 1UL; - } - - return ((UInt128)high << 64) | low; - } - private static UInt128 Pow2MontgomeryModWindowedGpuShim(UInt128 exponent, UInt128 modulus) { if (modulus == UInt128.One)