diff --git a/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModCycleComputationBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModCycleComputationBenchmarks.cs index 2ae2a6e..682f22e 100644 --- a/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModCycleComputationBenchmarks.cs +++ b/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModCycleComputationBenchmarks.cs @@ -279,17 +279,17 @@ public class Pow2MontgomeryModCycleComputationBenchmarks #if DEBUG Console.WriteLine("Trying heuristic. Prime order calculation"); #endif - PrimeOrderCalculator.PrimeOrderResult orderResult = PrimeOrderCalculator.Calculate( - modulus, - _previousPrimeOrder, - divisorData, - PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, - PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); + ulong order = PrimeOrderCalculator.Calculate( + modulus, + _previousPrimeOrder, + divisorData, + PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, + PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); - if (orderResult.Order != 0UL) + if (order != 0UL) { - _previousPrimeOrder = orderResult.Order; - return orderResult.Order; + _previousPrimeOrder = order; + return order; } _previousPrimeOrder = null; @@ -308,15 +308,15 @@ public class Pow2MontgomeryModCycleComputationBenchmarks #if DEBUG Console.WriteLine("Trying heuristic. Wide prime order calculation"); #endif - PrimeOrderCalculator.PrimeOrderResultWide orderResult = PrimeOrderCalculator.Calculate( + UInt128 order = PrimeOrderCalculator.Calculate( modulus, _previousWidePrimeOrder, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault); - if (orderResult.Order != UInt128.Zero) + if (order != UInt128.Zero) { - _previousWidePrimeOrder = orderResult.Order; - return orderResult.Order; + _previousWidePrimeOrder = order; + return order; } _previousWidePrimeOrder = null; diff --git a/PerfectNumbers.Core.Tests/Gpu/PrimeOrderGpuHeuristicsTests.cs b/PerfectNumbers.Core.Tests/Gpu/PrimeOrderGpuHeuristicsTests.cs index 6f77ade..c381c39 100644 --- a/PerfectNumbers.Core.Tests/Gpu/PrimeOrderGpuHeuristicsTests.cs +++ b/PerfectNumbers.Core.Tests/Gpu/PrimeOrderGpuHeuristicsTests.cs @@ -359,10 +359,9 @@ public class PrimeOrderGpuHeuristicsTests PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0; MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime); - PrimeOrderCalculator.PrimeOrderResult result = PrimeOrderCalculator.Calculate(prime, null, divisorData, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); + ulong result = PrimeOrderCalculator.Calculate(prime, null, divisorData, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); - result.Status.Should().Be(PrimeOrderCalculator.PrimeOrderStatus.Found); - result.Order.Should().Be(3UL); + result.Should().Be(3UL); } finally { diff --git a/PerfectNumbers.Core.Tests/PrimeOrderCalculatorTests.cs b/PerfectNumbers.Core.Tests/PrimeOrderCalculatorTests.cs index 9d3968e..bea113b 100644 --- a/PerfectNumbers.Core.Tests/PrimeOrderCalculatorTests.cs +++ b/PerfectNumbers.Core.Tests/PrimeOrderCalculatorTests.cs @@ -45,15 +45,14 @@ public class PrimeOrderCalculatorTests { ulong expected = ComputeOrderByDoubling(prime); - PrimeOrderCalculator.PrimeOrderResult result = PrimeOrderCalculator.Calculate( - prime, - previousOrder: null, - MontgomeryDivisorData.FromModulus(prime), - PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, - PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); + ulong result = PrimeOrderCalculator.Calculate( + prime, + previousOrder: null, + MontgomeryDivisorData.FromModulus(prime), + PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, + PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); - result.Status.Should().Be(PrimeOrderCalculator.PrimeOrderStatus.Found); - result.Order.Should().Be(expected); + result.Should().Be(expected); } [Theory] @@ -63,23 +62,21 @@ public class PrimeOrderCalculatorTests { ulong previousOrder = ComputeOrderByDoubling(previousPrime); - PrimeOrderCalculator.PrimeOrderResult heuristic = PrimeOrderCalculator.Calculate( - prime, - previousOrder, - MontgomeryDivisorData.FromModulus(prime), - PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, - PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); + ulong heuristic = PrimeOrderCalculator.Calculate( + prime, + previousOrder, + MontgomeryDivisorData.FromModulus(prime), + PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, + PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); - PrimeOrderCalculator.PrimeOrderResult strict = PrimeOrderCalculator.Calculate( - prime, - previousOrder: null, - MontgomeryDivisorData.FromModulus(prime), - PrimeOrderCalculator.PrimeOrderSearchConfig.StrictDefault, - PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); + ulong strict = PrimeOrderCalculator.Calculate( + prime, + previousOrder: null, + MontgomeryDivisorData.FromModulus(prime), + PrimeOrderCalculator.PrimeOrderSearchConfig.StrictDefault, + PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); - heuristic.Status.Should().Be(PrimeOrderCalculator.PrimeOrderStatus.Found); - strict.Status.Should().Be(PrimeOrderCalculator.PrimeOrderStatus.Found); - heuristic.Order.Should().Be(strict.Order); + heuristic.Should().Be(strict); } [Theory] @@ -88,15 +85,14 @@ public class PrimeOrderCalculatorTests [InlineData(3UL, 2UL)] public void Calculate_handles_trivial_primes(ulong prime, ulong expectedOrder) { - PrimeOrderCalculator.PrimeOrderResult result = PrimeOrderCalculator.Calculate( - prime, - previousOrder: null, - MontgomeryDivisorData.FromModulus(prime), - PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, - PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); + ulong result = PrimeOrderCalculator.Calculate( + prime, + previousOrder: null, + MontgomeryDivisorData.FromModulus(prime), + PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, + PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); - result.Status.Should().Be(PrimeOrderCalculator.PrimeOrderStatus.Found); - result.Order.Should().Be(expectedOrder); + result.Should().Be(expectedOrder); } [Fact] @@ -109,15 +105,14 @@ public class PrimeOrderCalculatorTests maxPowChecks: 8, mode: PrimeOrderCalculator.PrimeOrderMode.Heuristic); - PrimeOrderCalculator.PrimeOrderResult result = PrimeOrderCalculator.Calculate( - prime: 13UL, - previousOrder: null, - MontgomeryDivisorData.FromModulus(13UL), - config, - PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); + ulong result = PrimeOrderCalculator.Calculate( + prime: 13UL, + previousOrder: null, + MontgomeryDivisorData.FromModulus(13UL), + config, + PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); - result.Status.Should().Be(PrimeOrderCalculator.PrimeOrderStatus.HeuristicUnresolved); - result.Order.Should().Be(ComputeOrderByDoubling(13UL)); + result.Should().Be(ComputeOrderByDoubling(13UL)); } [Fact] @@ -131,15 +126,14 @@ public class PrimeOrderCalculatorTests mode: PrimeOrderCalculator.PrimeOrderMode.Heuristic); MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(239UL); - PrimeOrderCalculator.PrimeOrderResult heuristic = PrimeOrderCalculator.Calculate( - prime: 239UL, - previousOrder: null, - divisorData, - heuristicConfig, - PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); + ulong heuristic = PrimeOrderCalculator.Calculate( + prime: 239UL, + previousOrder: null, + divisorData, + heuristicConfig, + PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); - heuristic.Status.Should().Be(PrimeOrderCalculator.PrimeOrderStatus.HeuristicUnresolved); - heuristic.Order.Should().Be(ComputeOrderByDoubling(239UL)); + heuristic.Should().Be(ComputeOrderByDoubling(239UL)); var strictConfig = new PrimeOrderCalculator.PrimeOrderSearchConfig( smallFactorLimit: 2, @@ -147,15 +141,14 @@ public class PrimeOrderCalculatorTests maxPowChecks: 1, mode: PrimeOrderCalculator.PrimeOrderMode.Strict); - PrimeOrderCalculator.PrimeOrderResult strict = PrimeOrderCalculator.Calculate( - prime: 239UL, - previousOrder: null, - divisorData, - strictConfig, - PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); + ulong strict = PrimeOrderCalculator.Calculate( + prime: 239UL, + previousOrder: null, + divisorData, + strictConfig, + PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); - strict.Status.Should().Be(PrimeOrderCalculator.PrimeOrderStatus.Found); - strict.Order.Should().Be(heuristic.Order); + strict.Should().Be(heuristic); } [Fact] @@ -164,13 +157,12 @@ public class PrimeOrderCalculatorTests { UInt128 prime = UInt128.Parse("18446744073709641691"); - PrimeOrderCalculator.PrimeOrderResultWide result = PrimeOrderCalculator.Calculate( + UInt128 result = PrimeOrderCalculator.Calculate( prime, previousOrder: null, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault); - result.Status.Should().Be(PrimeOrderCalculator.PrimeOrderStatus.Found); - result.Order.Should().Be((UInt128)1229782938247309446UL); + result.Should().Be((UInt128)1229782938247309446UL); } [Fact] diff --git a/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs b/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs index fa4251e..cb920cc 100644 --- a/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs +++ b/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs @@ -415,9 +415,9 @@ internal static partial class PrimeOrderGpuHeuristics ulong? previousOrder, PrimeOrderCalculator.PrimeOrderSearchConfig config, in MontgomeryDivisorData divisorData, - out PrimeOrderCalculator.PrimeOrderResult result) + out ulong order) { - result = default; + order = 0UL; var lease = GpuKernelPool.GetKernel(useGpuOrder: true); var execution = lease.EnterExecutionScope(); @@ -497,18 +497,17 @@ internal static partial class PrimeOrderGpuHeuristics throw new InvalidOperationException("GPU Pollard Rho stack overflow; increase HeuristicStackCapacity."); } - ulong order = 0UL; resultBuffer.View.CopyToCPU(ref order, 1); - PrimeOrderCalculator.PrimeOrderStatus finalStatus = kernelStatus == PrimeOrderKernelStatus.Found - ? PrimeOrderCalculator.PrimeOrderStatus.Found - : PrimeOrderCalculator.PrimeOrderStatus.HeuristicUnresolved; + if (kernelStatus == PrimeOrderKernelStatus.FactoringFailure) + { + order = 0UL; + } - result = new PrimeOrderCalculator.PrimeOrderResult(finalStatus, order); DisposeResources(); lease.Dispose(); - return true; + return order != 0UL; void DisposeResources() { diff --git a/PerfectNumbers.Core/MersenneDivisorCycles.cs b/PerfectNumbers.Core/MersenneDivisorCycles.cs index ee77260..1d1b1f3 100644 --- a/PerfectNumbers.Core/MersenneDivisorCycles.cs +++ b/PerfectNumbers.Core/MersenneDivisorCycles.cs @@ -234,14 +234,14 @@ public class MersenneDivisorCycles return (UInt128)cycle; } - PrimeOrderCalculator.PrimeOrderResultWide wideResult = PrimeOrderCalculator.Calculate( + UInt128 wideOrder = PrimeOrderCalculator.Calculate( divisor, previousOrder: null, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, PrimeOrderCalculator.PrimeOrderHeuristicDevice.Cpu); - if (wideResult.Order != UInt128.Zero) + if (wideOrder != UInt128.Zero) { - return wideResult.Order; + return wideOrder; } // Otherwise, find order of 2 mod divisor @@ -285,15 +285,15 @@ public class MersenneDivisorCycles return true; } - PrimeOrderCalculator.PrimeOrderResult orderResult = PrimeOrderCalculator.Calculate( + ulong order = PrimeOrderCalculator.Calculate( divisor, previousOrder: null, divisorData, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, PrimeOrderCalculator.PrimeOrderHeuristicDevice.Cpu); - if (orderResult.Order != 0UL) + if (order != 0UL) { - cycleLength = orderResult.Order; + cycleLength = order; return true; } @@ -940,15 +940,15 @@ public class MersenneDivisorCycles if (PrimeTester.IsPrimeInternal(divisor, CancellationToken.None)) { - PrimeOrderCalculator.PrimeOrderResult orderResult = PrimeOrderCalculator.Calculate( + ulong order = PrimeOrderCalculator.Calculate( divisor, previousOrder: null, divisorData, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, PrimeOrderCalculator.PrimeOrderHeuristicDevice.Cpu); - if (orderResult.Order != 0UL) + if (order != 0UL) { - cycleLength = orderResult.Order; + cycleLength = order; return true; } } diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Wide.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Wide.cs index 072f5ab..2d02535 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Wide.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Wide.cs @@ -9,23 +9,10 @@ namespace PerfectNumbers.Core; internal static partial class PrimeOrderCalculator { - internal readonly struct PrimeOrderResultWide - { - public PrimeOrderResultWide(PrimeOrderStatus status, UInt128 order) - { - Status = status; - Order = order; - } - - public PrimeOrderStatus Status { get; } - - public UInt128 Order { get; } - } - - public static PrimeOrderResultWide Calculate(UInt128 prime, UInt128? previousOrder, PrimeOrderSearchConfig config) + public static UInt128 Calculate(UInt128 prime, UInt128? previousOrder, PrimeOrderSearchConfig config) => Calculate(prime, previousOrder, config, PrimeOrderHeuristicDevice.Gpu); - public static PrimeOrderResultWide Calculate( + public static UInt128 Calculate( UInt128 prime, UInt128? previousOrder, PrimeOrderSearchConfig config, @@ -33,6 +20,7 @@ internal static partial class PrimeOrderCalculator { var scope = UsePow2Mode(device); MontgomeryDivisorData divisorData; + UInt128 result; if (prime <= ulong.MaxValue) { ulong? previous = null; @@ -51,50 +39,49 @@ internal static partial class PrimeOrderCalculator ulong prime64 = (ulong)prime; divisorData = MontgomeryDivisorData.FromModulus(prime64); - PrimeOrderResult result = Calculate(prime64, previous, divisorData, config, device); - scope.Dispose(); - return new PrimeOrderResultWide(result.Status, result.Order); + ulong order64 = Calculate(prime64, previous, divisorData, config, device); + result = order64 == 0UL ? UInt128.Zero : (UInt128)order64; } else { divisorData = default; + result = CalculateWideInternal(prime, previousOrder, divisorData, config); } - PrimeOrderResultWide resultWide = CalculateWideInternal(prime, previousOrder, divisorData, config); - scope.Dispose(); - return resultWide; + scope.Dispose(); + return result; } - private static PrimeOrderResultWide CalculateWideInternal(UInt128 prime, UInt128? previousOrder, in MontgomeryDivisorData divisorData, PrimeOrderSearchConfig config) + private static UInt128 CalculateWideInternal(UInt128 prime, UInt128? previousOrder, in MontgomeryDivisorData divisorData, PrimeOrderSearchConfig config) { if (prime <= UInt128.One) { - return new PrimeOrderResultWide(PrimeOrderStatus.Found, UInt128.One); + return UInt128.One; } if (prime == (UInt128)3UL) { - return new PrimeOrderResultWide(PrimeOrderStatus.Found, (UInt128)2UL); + return (UInt128)2UL; } UInt128 phi = prime - UInt128.One; var debugScope = UseDebugLogging(!IsGpuHeuristicDevice); - PartialFactorResult128 phiFactors = PartialFactorWide(phi, config); - PrimeOrderResultWide result; + PartialFactorResult128 phiFactors = PartialFactorWide(phi, config); + UInt128 result; if (phiFactors.Factors is null) { - // HeuristicFailureLog.Record(prime, null, HeuristicFailureReason.PhiPartialFactorizationFailed); - result = FinishStrictlyWide(prime, divisorData, config.Mode); - debugScope.Dispose(); - return result; + result = FinishStrictlyWide(prime, divisorData); + } + else + { + result = RunHeuristicPipelineWide(prime, previousOrder, divisorData, config, phi, phiFactors); } - result = RunHeuristicPipelineWide(prime, previousOrder, divisorData, config, phi, phiFactors); - debugScope.Dispose(); - return result; + debugScope.Dispose(); + return result; } - private static PrimeOrderResultWide RunHeuristicPipelineWide( + private static UInt128 RunHeuristicPipelineWide( UInt128 prime, UInt128? previousOrder, in MontgomeryDivisorData divisorData, @@ -104,7 +91,7 @@ internal static partial class PrimeOrderCalculator { if (phiFactors.FullyFactored && TrySpecialMaxWide(phi, prime, divisorData, phiFactors)) { - return new PrimeOrderResultWide(PrimeOrderStatus.Found, phi); + return phi; } UInt128 candidateOrder = InitializeStartingOrderWide(prime, phi, divisorData); @@ -112,30 +99,25 @@ internal static partial class PrimeOrderCalculator if (TryConfirmOrderWide(prime, candidateOrder, divisorData, config)) { - return new PrimeOrderResultWide(PrimeOrderStatus.Found, candidateOrder); + return candidateOrder; } if (config.Mode == PrimeOrderMode.Strict) { - // HeuristicFailureLog.Record(prime, candidateOrder, HeuristicFailureReason.StrictModeRequested); - return FinishStrictlyWide(prime, divisorData, PrimeOrderMode.Strict); + return FinishStrictlyWide(prime, divisorData); } if (TryHeuristicFinishWide(prime, candidateOrder, previousOrder, divisorData, config, out UInt128 order)) { - return new PrimeOrderResultWide(PrimeOrderStatus.Found, order); + return order; } - // HeuristicFailureLog.Record(prime, candidateOrder, HeuristicFailureReason.HeuristicPipelineAssumedFullOrder); - // The heuristic pipeline could not narrow the order any further; assume the current order is final. - return new PrimeOrderResultWide(PrimeOrderStatus.Found, candidateOrder); + return candidateOrder; } - private static PrimeOrderResultWide FinishStrictlyWide(UInt128 prime, in MontgomeryDivisorData divisorData, PrimeOrderMode mode) + private static UInt128 FinishStrictlyWide(UInt128 prime, in MontgomeryDivisorData divisorData) { - UInt128 strictOrder = CalculateByFactorizationWide(prime, divisorData); - PrimeOrderStatus status = mode == PrimeOrderMode.Strict ? PrimeOrderStatus.Found : PrimeOrderStatus.HeuristicUnresolved; - return new PrimeOrderResultWide(status, strictOrder); + return CalculateByFactorizationWide(prime, divisorData); } private static bool TrySpecialMaxWide(UInt128 phi, UInt128 prime, in MontgomeryDivisorData divisorData, PartialFactorResult128 factors) diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.cs b/PerfectNumbers.Core/PrimeOrderCalculator.cs index 8f83c81..5124839 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.cs @@ -18,24 +18,7 @@ namespace PerfectNumbers.Core; internal static partial class PrimeOrderCalculator { - internal enum PrimeOrderStatus - { - Found, - HeuristicUnresolved, - } - internal readonly struct PrimeOrderResult - { - public PrimeOrderResult(PrimeOrderStatus status, ulong order) - { - Status = status; - Order = order; - } - - public PrimeOrderStatus Status { get; } - - public ulong Order { get; } - } internal enum PrimeOrderMode { @@ -167,7 +150,14 @@ internal static partial class PrimeOrderCalculator } } - public static PrimeOrderResult Calculate( + public static ulong Calculate( + ulong prime, + ulong? previousOrder, + in MontgomeryDivisorData divisorData, + in PrimeOrderSearchConfig config) + => Calculate(prime, previousOrder, divisorData, config, PrimeOrderHeuristicDevice.Cpu); + + public static ulong Calculate( ulong prime, ulong? previousOrder, in MontgomeryDivisorData divisorData, @@ -175,41 +165,36 @@ internal static partial class PrimeOrderCalculator PrimeOrderHeuristicDevice device) { var scope = UsePow2Mode(device); - PrimeOrderResult result = CalculateInternal(prime, previousOrder, divisorData, config); - scope.Dispose(); - return result; + ulong order = CalculateInternal(prime, previousOrder, divisorData, config); + scope.Dispose(); + return order; } - private static PrimeOrderResult CalculateInternal(ulong prime, ulong? previousOrder, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config) + private static ulong CalculateInternal(ulong prime, ulong? previousOrder, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config) { if (prime <= 3UL) { - return new PrimeOrderResult(PrimeOrderStatus.Found, prime == 3UL ? 2UL : 1UL); + return prime == 3UL ? 2UL : 1UL; } ulong phi = prime - 1UL; - if (IsGpuHeuristicDevice && PrimeOrderGpuHeuristics.TryCalculateOrder(prime, previousOrder, config, divisorData, out PrimeOrderResult gpuResult)) + if (IsGpuHeuristicDevice && PrimeOrderGpuHeuristics.TryCalculateOrder(prime, previousOrder, config, divisorData, out ulong gpuOrder)) { - return gpuResult; + return gpuOrder; } - // using var debugScope = UseDebugLogging(!IsGpuHeuristicDevice); - // DebugLog("Partial factoring φ(p)"); - using PartialFactorResult phiFactors = PartialFactor(phi, config); if (phiFactors.Factors is null) { - // DebugLog("No factors found"); - // HeuristicFailureLog.Record(prime, null, HeuristicFailureReason.PhiPartialFactorizationFailed); - return FinishStrictly(prime, divisorData, config.Mode); + return FinishStrictly(prime, divisorData); } return RunHeuristicPipeline(prime, previousOrder, config, divisorData, phi, phiFactors); } - private static PrimeOrderResult RunHeuristicPipeline( + private static ulong RunHeuristicPipeline( ulong prime, ulong? previousOrder, in PrimeOrderSearchConfig config, @@ -217,44 +202,35 @@ internal static partial class PrimeOrderCalculator ulong phi, PartialFactorResult phiFactors) { - // DebugLog("Trying special max check"); if (phiFactors.FullyFactored && TrySpecialMax(phi, prime, phiFactors, divisorData)) { - return new PrimeOrderResult(PrimeOrderStatus.Found, phi); + return phi; } - // DebugLog("Initializing starting order"); ulong candidateOrder = InitializeStartingOrder(prime, phi, divisorData); candidateOrder = ExponentLowering(candidateOrder, prime, phiFactors, divisorData); - // DebugLog("Trying to confirm order"); if (TryConfirmOrder(prime, candidateOrder, divisorData, config)) { - return new PrimeOrderResult(PrimeOrderStatus.Found, candidateOrder); + return candidateOrder; } if (config.Mode == PrimeOrderMode.Strict) { - // HeuristicFailureLog.Record(prime, candidateOrder, HeuristicFailureReason.StrictModeRequested); - return FinishStrictly(prime, divisorData, PrimeOrderMode.Strict); + return FinishStrictly(prime, divisorData); } if (TryHeuristicFinish(prime, candidateOrder, previousOrder, divisorData, config, phiFactors, out ulong order)) { - return new PrimeOrderResult(PrimeOrderStatus.Found, order); + return order; } - // DebugLog("Heuristic unresolved, assuming current order"); - // HeuristicFailureLog.Record(prime, candidateOrder, HeuristicFailureReason.HeuristicPipelineAssumedFullOrder); - // The heuristic pipeline could not rule out additional divisors; assume the current order is final. - return new PrimeOrderResult(PrimeOrderStatus.Found, candidateOrder); + return candidateOrder; } - private static PrimeOrderResult FinishStrictly(ulong prime, in MontgomeryDivisorData divisorData, PrimeOrderMode mode) + private static ulong FinishStrictly(ulong prime, in MontgomeryDivisorData divisorData) { - ulong strictOrder = CalculateByFactorization(prime, divisorData); - PrimeOrderStatus status = mode == PrimeOrderMode.Strict ? PrimeOrderStatus.Found : PrimeOrderStatus.HeuristicUnresolved; - return new PrimeOrderResult(status, strictOrder); + return CalculateByFactorization(prime, divisorData); } private static bool TrySpecialMax(ulong phi, ulong prime, PartialFactorResult factors, in MontgomeryDivisorData divisorData) @@ -993,7 +969,7 @@ internal static partial class PrimeOrderCalculator { if (value <= 1UL) { - return PartialFactorResult.Empty; + return PartialFactorResult.RentEmpty(); } const int FactorSlotCount = GpuSmallPrimeFactorSlots; @@ -1098,12 +1074,12 @@ internal static partial class PrimeOrderCalculator { if ((counts is null || counts.Count == 0) && cofactor == value) { - return new PartialFactorResult(null, value, false, 0); + return PartialFactorResult.Rent(null, value, false, 0); } if (counts is null || counts.Count == 0) { - return new PartialFactorResult(null, cofactor, cofactor == 1UL, 0); + return PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0); } FactorEntry[] factors = ArrayPool.Shared.Rent(counts.Count); @@ -1118,7 +1094,7 @@ internal static partial class PrimeOrderCalculator span.Sort(static (a, b) => a.Value.CompareTo(b.Value)); bool fullyFactored = cofactor == 1UL; - return new PartialFactorResult(factors, cofactor, fullyFactored, index, pooled: true); + return PartialFactorResult.Rent(factors, cofactor, fullyFactored, index); } int actualCount = 0; @@ -1134,10 +1110,10 @@ internal static partial class PrimeOrderCalculator { if (cofactor == value) { - return new PartialFactorResult(null, value, false, 0); + return PartialFactorResult.Rent(null, value, false, 0); } - return new PartialFactorResult(null, cofactor, cofactor == 1UL, 0); + return PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0); } FactorEntry[] array = ArrayPool.Shared.Rent(actualCount); @@ -1158,7 +1134,7 @@ internal static partial class PrimeOrderCalculator Span arraySpan = array.AsSpan(0, arrayIndex); arraySpan.Sort(static (a, b) => a.Value.CompareTo(b.Value)); bool fullyFactoredArray = cofactor == 1UL; - return new PartialFactorResult(array, cofactor, fullyFactoredArray, arrayIndex, pooled: true); + return PartialFactorResult.Rent(array, cofactor, fullyFactoredArray, arrayIndex); } finally { @@ -1588,58 +1564,89 @@ internal static partial class PrimeOrderCalculator public readonly int Exponent = exponent; } - private struct PartialFactorResult : IDisposable + private sealed class PartialFactorResult : IDisposable { - private FactorEntry[]? _factors; - private readonly bool _pooled; + [ThreadStatic] + private static PartialFactorResult? s_poolHead; - public PartialFactorResult(FactorEntry[]? factors, ulong cofactor, bool fullyFactored, int count, bool pooled = false) + private PartialFactorResult? _next; + private bool _returned; + + public FactorEntry[]? Factors; + public ulong Cofactor; + public bool FullyFactored; + public int Count; + + private PartialFactorResult() { - _factors = factors; - Cofactor = cofactor; - FullyFactored = fullyFactored; - Count = count; - _pooled = pooled && factors is not null; } - public FactorEntry[]? Factors => _factors; - - public ulong Cofactor { get; } - - public bool FullyFactored { get; } - - public int Count { get; } - - public static PartialFactorResult Empty => new(null, 1UL, true, 0); - - public void Dispose() + public static PartialFactorResult Rent(FactorEntry[]? factors, ulong cofactor, bool fullyFactored, int count) { - FactorEntry[]? factors = _factors; - if (factors is null || !_pooled) + PartialFactorResult? instance = s_poolHead; + if (instance is null) { - return; + instance = new PartialFactorResult(); + } + else + { + s_poolHead = instance._next; } - _factors = null; - ArrayPool.Shared.Return(factors, clearArray: false); + instance._next = null; + instance._returned = false; + instance.Factors = factors; + instance.Cofactor = cofactor; + instance.FullyFactored = fullyFactored; + instance.Count = count; + return instance; + } + + public static PartialFactorResult RentEmpty() + { + return Rent(null, 1UL, true, 0); } public PartialFactorResult WithAdditionalPrime(ulong prime) { - if (_factors is null || Count == 0) + FactorEntry[]? source = Factors; + if (source is null || Count == 0) { FactorEntry[] local = ArrayPool.Shared.Rent(1); local[0] = new FactorEntry(prime, 1); - return new PartialFactorResult(local, 1UL, true, 1, pooled: true); + return Rent(local, 1UL, true, 1); } FactorEntry[] extended = ArrayPool.Shared.Rent(Count + 1); - Array.Copy(_factors, 0, extended, 0, Count); + Array.Copy(source, 0, extended, 0, Count); extended[Count] = new FactorEntry(prime, 1); Span span = extended.AsSpan(0, Count + 1); span.Sort(static (a, b) => a.Value.CompareTo(b.Value)); - return new PartialFactorResult(extended, 1UL, true, Count + 1, pooled: true); + return Rent(extended, 1UL, true, Count + 1); + } + + public void Dispose() + { + if (_returned) + { + return; + } + + _returned = true; + + FactorEntry[]? factors = Factors; + if (factors is not null) + { + Factors = null; + ArrayPool.Shared.Return(factors, clearArray: false); + } + + Cofactor = 0UL; + FullyFactored = false; + Count = 0; + + _next = s_poolHead; + s_poolHead = this; } } } -