diff --git a/PerfectNumbers.Core/Gpu/Kernels/Pow2MontgomeryKernels.cs b/PerfectNumbers.Core/Gpu/Kernels/Pow2MontgomeryKernels.cs index fd441fd..8dbdf58 100644 --- a/PerfectNumbers.Core/Gpu/Kernels/Pow2MontgomeryKernels.cs +++ b/PerfectNumbers.Core/Gpu/Kernels/Pow2MontgomeryKernels.cs @@ -141,6 +141,12 @@ public static class Pow2MontgomeryKernels return Pow2MontgomeryModWindowedGpuMontgomeryResult(divisor, exponent); } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public static ulong Pow2MontgomeryModWindowedGpuKeepMontgomery(this MontgomeryDivisorDataGpu divisor, ulong exponent) + { + return Pow2MontgomeryModWindowedGpuMontgomeryResult(divisor, exponent); + } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong Pow2MontgomeryModWindowedGpuConvertToStandard(this MontgomeryDivisorDataGpu divisor, ulong exponent) { diff --git a/PerfectNumbers.Core/Gpu/Kernels/PrimeOrderKernels.cs b/PerfectNumbers.Core/Gpu/Kernels/PrimeOrderKernels.cs index 79ca9a0..20f9692 100644 --- a/PerfectNumbers.Core/Gpu/Kernels/PrimeOrderKernels.cs +++ b/PerfectNumbers.Core/Gpu/Kernels/PrimeOrderKernels.cs @@ -39,7 +39,7 @@ internal static partial class PrimeOrderKernels } // ulong candidate = order / primeFactor; - if (divisorData.Pow2MontgomeryModWindowedGpuConvertToStandard(candidate) == 1UL) + if (divisorData.Pow2MontgomeryModWindowedGpuKeepMontgomery(candidate) == divisorData.MontgomeryOne) { order = candidate; continue; @@ -395,7 +395,7 @@ internal static partial class PrimeOrderKernels } ulong reduced = phi / factor; - if (divisor.Pow2MontgomeryModWindowedGpuConvertToStandard(reduced) == 1UL) + if (divisor.Pow2MontgomeryModWindowedGpuKeepMontgomery(reduced) == divisor.MontgomeryOne) { return false; } @@ -1179,7 +1179,7 @@ internal static partial class PrimeOrderKernels private static bool Pow2EqualsOneKernel(ulong exponent, in MontgomeryDivisorDataGpu divisor) { - return divisor.Pow2MontgomeryModWindowedGpuConvertToStandard(exponent) == 1UL; + return divisor.Pow2MontgomeryModWindowedGpuKeepMontgomery(exponent) == divisor.MontgomeryOne; } [MethodImpl(MethodImplOptions.AggressiveOptimization)] diff --git a/PerfectNumbers.Core/MersenneDivisorCyclesTemplate.cs b/PerfectNumbers.Core/MersenneDivisorCyclesTemplate.cs index 419a90f..b5663dc 100644 --- a/PerfectNumbers.Core/MersenneDivisorCyclesTemplate.cs +++ b/PerfectNumbers.Core/MersenneDivisorCyclesTemplate.cs @@ -99,7 +99,7 @@ public struct MersenneDivisorCyclesTemplate return cycleLength == exponent; } - return exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisorData) == 1UL; + return exponent.Pow2MontgomeryModWindowedKeepMontgomeryCpu(divisorData) == divisorData.MontgomeryOne; } public static bool CycleEqualsExponentForMersenneCandidate(ulong divisor, in MontgomeryDivisorData divisorData, ulong exponent) @@ -125,7 +125,7 @@ public struct MersenneDivisorCyclesTemplate } } - return exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisorData) == 1UL; + return exponent.Pow2MontgomeryModWindowedKeepMontgomeryCpu(divisorData) == divisorData.MontgomeryOne; } private static bool IsValidMersenneDivisorCandidate(ulong divisor, ulong exponent) diff --git a/PerfectNumbers.Core/PrimeOrderCalculatorTemplate.cs b/PerfectNumbers.Core/PrimeOrderCalculatorTemplate.cs index 3bbf982..ff200f9 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculatorTemplate.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculatorTemplate.cs @@ -938,7 +938,7 @@ public static partial class PrimeOrderCalculatorTemplate ulong candidate = order - primeFactor * remainder; // ulong candidate = order / primeFactor; - if (candidate.Pow2MontgomeryModWindowedConvertToStandardCpu(divisorData) == 1UL) + if (candidate.Pow2MontgomeryModWindowedKeepMontgomeryCpu(divisorData) == divisorData.MontgomeryOne) { order = candidate; continue; @@ -1674,7 +1674,7 @@ public static partial class PrimeOrderCalculatorTemplate [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static bool Pow2EqualsOne(ulong exponent, in MontgomeryDivisorData divisorData) { - return exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisorData) == 1UL; + return exponent.Pow2MontgomeryModWindowedKeepMontgomeryCpu(divisorData) == divisorData.MontgomeryOne; } #if DEVICE_HYBRID || DEVICE_GPU @@ -1685,7 +1685,7 @@ public static partial class PrimeOrderCalculatorTemplate in MontgomeryDivisorData divisorData) { // TODO: swap to GPU pow2 when available; fallback to CPU for now. - return exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisorData) == 1UL; + return exponent.Pow2MontgomeryModWindowedKeepMontgomeryCpu(divisorData) == divisorData.MontgomeryOne; } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] @@ -1695,7 +1695,7 @@ public static partial class PrimeOrderCalculatorTemplate ulong prime, in MontgomeryDivisorData divisorData) { - return exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisorData) == 1UL; + return exponent.Pow2MontgomeryModWindowedKeepMontgomeryCpu(divisorData) == divisorData.MontgomeryOne; } #endif diff --git a/PerfectNumbers.Core/ULongExtensions.Cpu.cs b/PerfectNumbers.Core/ULongExtensions.Cpu.cs index f5af06a..6c37a93 100644 --- a/PerfectNumbers.Core/ULongExtensions.Cpu.cs +++ b/PerfectNumbers.Core/ULongExtensions.Cpu.cs @@ -18,7 +18,7 @@ public static partial class ULongExtensions // 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) + if (exponent <= Pow2WindowFallbackThreshold || modulus <= PerfectNumberConstants.MaxQForDivisorCycles) { return Pow2MontgomeryModSingleBitKeepMontgomery(exponent, divisor); } @@ -88,7 +88,7 @@ public static partial class ULongExtensions // 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) + if (exponent <= Pow2WindowFallbackThreshold || modulus <= PerfectNumberConstants.MaxQForDivisorCycles) { return Pow2MontgomeryModSingleBitConvertToStandard(exponent, divisor); } @@ -274,7 +274,7 @@ public static partial class ULongExtensions } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static bool AdvanceMersenneDivisorCycleStepCpu(ref ulong pow, ulong divisor, ref ulong order) { pow += pow; diff --git a/PerfectNumbers.Core/ULongExtensions.cs b/PerfectNumbers.Core/ULongExtensions.cs index 9e704e2..3054474 100644 --- a/PerfectNumbers.Core/ULongExtensions.cs +++ b/PerfectNumbers.Core/ULongExtensions.cs @@ -390,7 +390,7 @@ public static partial class ULongExtensions return value.BinaryGcd(exponentLog) > 1UL; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong BinaryGcd(this ulong a, ulong b) { if (a == 0UL) @@ -423,7 +423,7 @@ public static partial class ULongExtensions } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static ulong Select(ulong left, ulong right, bool useRight) { ulong mask = useRight ? ulong.MaxValue : 0UL; @@ -432,7 +432,7 @@ public static partial class ULongExtensions // Benchmarks (Mod5ULongBenchmarks) show the direct `% 5` is still cheaper (~0.26 ns vs 0.43 ns), so keep the modulo until a faster lookup is proven. // (Mod8/Mod10 stay masked because they win; Mod5 currently does not.) - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static byte Mod10(this ulong value) => (byte)(value % 5UL) switch { 0 => 0, @@ -446,7 +446,7 @@ public static partial class ULongExtensions public static ulong Mod128(this ulong value) => value & 127UL; // Benchmarks confirm `%` beats our current Mod5/Mod3 helpers for 64-bit inputs, so leave these modulo operations in place until a superior lookup is available. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static void Mod10_8_5_3(this ulong value, out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3) { mod8 = value & 7UL; @@ -474,7 +474,7 @@ public static partial class ULongExtensions } // Mod5/Mod3 lookup tables are currently slower on 64-bit operands; keep the direct modulo until benchmarks flip. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static void Mod10_8_5_3Steps(this ulong value, out ulong step10, out ulong step8, out ulong step5, out ulong step3) { ulong mod8 = value & 7UL; @@ -520,16 +520,16 @@ public static partial class ULongExtensions } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static UInt128 Mul64(this ulong a, ulong b) => ((UInt128)a.MulHighCpu(b) << 64) | (UInt128)(a * b); - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong MulHighCpu(this ulong x, ulong y) { return (ulong)(((UInt128)x * y) >> 64); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong MulHighGpu(this ulong x, ulong y) { // Retain this manual decomposition for GPU-style arithmetic so callers staging accelerator work @@ -553,7 +553,7 @@ public static partial class ULongExtensions return result; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong AddMod64(this ulong value, ulong addend, ulong modulus) { // Every CPU caller (Pollard Rho and the ModPow64 ladder) supplies moduli of at least three, so leave this guard @@ -580,7 +580,7 @@ public static partial class ULongExtensions return (ulong)sum; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static ulong FoldMontgomery(ulong value, ulong modulus) { // Pollard Rho and ModPow64 both feed moduli of at least three, so retain the documentation and skip the runtime guard. @@ -609,7 +609,7 @@ public static partial class ULongExtensions return folded; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong ModPow64(this ulong value, ulong exponent, ulong modulus) { // Pollard Rho and the Mersenne testers never pass moduli below two, so keep the guard commented for clarity. @@ -635,13 +635,10 @@ public static partial class ULongExtensions return result; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] // 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); - } + public static ulong MulMod64(this ulong a, ulong b, ulong modulus) => (ulong)(((UInt128)a * b) % modulus); [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong MontgomeryMultiplyCpu(this ulong a, ulong b, ulong modulus, ulong nPrime) @@ -658,7 +655,7 @@ public static partial class ULongExtensions return result; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static ulong Pow2MontgomeryModSingleBitKeepMontgomery(ulong exponent, in MontgomeryDivisorData divisor) { ulong modulus = divisor.Modulus; @@ -686,7 +683,7 @@ public static partial class ULongExtensions return result; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static ulong Pow2MontgomeryModSingleBitConvertToStandard(ulong exponent, in MontgomeryDivisorData divisor) { ulong modulus = divisor.Modulus; @@ -714,7 +711,7 @@ public static partial class ULongExtensions return result.MontgomeryMultiplyCpu(1UL, modulus, nPrime); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static UInt128 PowMod(this ulong exponent, UInt128 modulus) { // EvenPerfectBitScanner starts CPU scans at p >= 138,000,000 (Program.cs), @@ -1198,7 +1195,7 @@ public static partial class ULongExtensions return value % modulus; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static bool SharesFactorWithExponentMinusOneCpu(this ulong exponent) { ulong prime, value = exponent - 1UL; @@ -1235,7 +1232,7 @@ public static partial class ULongExtensions return false; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static bool SharesFactorWithExponentMinusOneGpu(this ulong exponent, PrimeOrderCalculatorAccelerator gpu) { ulong prime, value = exponent - 1UL; @@ -1272,7 +1269,7 @@ public static partial class ULongExtensions return false; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static bool SharesFactorWithExponentMinusOneHybrid(this ulong exponent, PrimeOrderCalculatorAccelerator gpu) { ulong prime, value = exponent - 1UL; diff --git a/excluded-138m.csv b/excluded-138m.csv index eb26495..3f334f3 120000 --- a/excluded-138m.csv +++ b/excluded-138m.csv @@ -34510,3 +34510,23 @@ p,divisor,searchedMersenne,detailedCheck,passedAllTests 10639,427251792503,True,True,False 20023,1235434157297,True,True,False 12497,347432096281,True,True,False +17539,1434748148857,True,True,False +5669,122398868791,True,True,False +12721,338895581441,True,True,False +15773,344578472209,True,True,False +16573,588828580471,True,True,False +5581,129359320121,True,True,False +17477,606383809609,True,True,False +19571,2844272257303,True,True,False +9433,444094320401,True,True,False +9769,296887940119,True,True,False +15629,284749721023,True,True,False +16519,2418552373423,True,True,False +7603,918910927273,True,True,False +20399,167108609,True,True,False +10729,290280712591,True,True,False +20411,40823,True,True,False +20441,406981291169,True,True,False +9511,1366035502967,True,True,False +2789,128664552119,True,True,False +20479,2948977,True,True,False