diff --git a/PerfectNumbers.Core/MutableUInt128.cs b/PerfectNumbers.Core/MutableUInt128.cs
new file mode 100644
index 0000000..7922ec1
--- /dev/null
+++ b/PerfectNumbers.Core/MutableUInt128.cs
@@ -0,0 +1,108 @@
+using System;
+using System.Numerics;
+
+namespace PerfectNumbers.Core;
+
+///
+/// Mutable helper for 128-bit arithmetic derived from the .NET runtime UInt128 implementation.
+/// Portions adapted from https://github.com/dotnet/runtime/blob/v8.0.0/src/libraries/System.Private.CoreLib/src/System/UInt128.cs.
+///
+internal sealed class MutableUInt128
+{
+ public ulong High { get; private set; }
+ public ulong Low { get; private set; }
+
+ public MutableUInt128()
+ {
+ }
+
+ public MutableUInt128(ulong value)
+ {
+ Low = value;
+ }
+
+ public MutableUInt128(ulong high, ulong low)
+ {
+ High = high;
+ Low = low;
+ }
+
+ public MutableUInt128(UInt128 value)
+ {
+ Set(value);
+ }
+
+ public void Set(UInt128 value)
+ {
+ High = (ulong)(value >> 64);
+ Low = (ulong)value;
+ }
+
+ public void Set(ulong high, ulong low)
+ {
+ High = high;
+ Low = low;
+ }
+
+ public UInt128 ToUInt128() => ((UInt128)High << 64) | Low;
+
+ public void Add(ulong value)
+ {
+ ulong low = Low + value;
+ if (low < value)
+ {
+ High++;
+ }
+
+ Low = low;
+ }
+
+ public void Add(in MutableUInt128 value)
+ {
+ ulong low = Low + value.Low;
+ ulong carry = low < Low ? 1UL : 0UL;
+ ulong high = High + value.High + carry;
+
+ Low = low;
+ High = high;
+ }
+
+ public void Subtract(ulong value)
+ {
+ ulong low = Low;
+ ulong result = low - value;
+ ulong borrow = result > low ? 1UL : 0UL;
+
+ Low = result;
+ High -= borrow;
+ }
+
+ public void Subtract(in MutableUInt128 value)
+ {
+ ulong low = Low;
+ ulong result = low - value.Low;
+ ulong borrow = low < value.Low ? 1UL : 0UL;
+
+ Low = result;
+ High = High - value.High - borrow;
+ }
+
+ public void Multiply(ulong value)
+ {
+ ulong high = High;
+ ulong low = Low;
+
+ ulong highContribution = Math.BigMul(low, value, out ulong newLow);
+ Math.BigMul(high, value, out ulong shiftedHigh);
+ ulong newHigh = shiftedHigh + highContribution;
+
+ Low = newLow;
+ High = newHigh;
+ }
+
+ public void MultiplyAdd(ulong multiplier, ulong addend)
+ {
+ Multiply(multiplier);
+ Add(addend);
+ }
+}
diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.cs b/PerfectNumbers.Core/PrimeOrderCalculator.cs
index 1312815..7df88c5 100644
--- a/PerfectNumbers.Core/PrimeOrderCalculator.cs
+++ b/PerfectNumbers.Core/PrimeOrderCalculator.cs
@@ -314,69 +314,84 @@ internal static partial class PrimeOrderCalculator
return order;
}
- [ThreadStatic]
- private static ArrayPool? _factorPool;
- private static ArrayPool FactorPool => _factorPool ??= ArrayPool.Create();
+ [ThreadStatic]
+ private static ArrayPool? _factorPool;
+ private static ArrayPool FactorPool => _factorPool ??= ArrayPool.Create();
- [ThreadStatic]
- private static ArrayPool? _ulongPool;
- private static ArrayPool ULongPool => _ulongPool ??= ArrayPool.Create();
+ private const int FactorEntryStackThreshold = 32;
- private static ulong ExponentLoweringCpu(ulong order, ulong prime, PartialFactorResult factors, in MontgomeryDivisorData divisorData)
- {
- ArrayPool factorPool = FactorPool;
+ [ThreadStatic]
+ private static ArrayPool? _ulongPool;
+ private static ArrayPool ULongPool => _ulongPool ??= ArrayPool.Create();
- FactorEntry[]? tempArray = null;
- try
- {
- ReadOnlySpan factorSpan = factors.Factors;
- int length = factors.Count;
- tempArray = factorPool.Rent(length + 1);
- Span buffer = tempArray;
- factorSpan.CopyTo(buffer);
+ private static ulong ExponentLoweringCpu(ulong order, ulong prime, PartialFactorResult factors, in MontgomeryDivisorData divisorData)
+ {
+ ArrayPool factorPool = FactorPool;
- bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(factors.Cofactor);
- // bool isPrime = PrimeTester.IsPrimeInternal(factors.Cofactor, CancellationToken.None);
- if (!factors.FullyFactored && factors.Cofactor > 1UL && isPrime)
- {
- buffer[length] = new FactorEntry(factors.Cofactor);
- length++;
- }
+ FactorEntry[]? tempArray = null;
+ try
+ {
+ ReadOnlySpan factorSpan = factors.Factors;
+ int length = factors.Count;
+ int capacity = length + 1;
- buffer.Slice(0, length).Sort(static (a, b) => a.Value.CompareTo(b.Value));
+ Span buffer;
+ if (capacity <= FactorEntryStackThreshold)
+ {
+ buffer = stackalloc FactorEntry[capacity];
+ }
+ else
+ {
+ tempArray = factorPool.Rent(capacity);
+ buffer = tempArray.AsSpan(0, capacity);
+ }
- for (int i = 0; i < length; i++)
- {
- ulong primeFactor = buffer[i].Value;
- int exponent = buffer[i].Exponent;
- for (int iteration = 0; iteration < exponent; iteration++)
- {
- if ((order % primeFactor) != 0UL)
- {
- break;
- }
+ factorSpan.CopyTo(buffer);
- ulong reduced = order / primeFactor;
- if (Pow2EqualsOneCpu(reduced, prime, divisorData))
- {
- order = reduced;
- continue;
- }
+ bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(factors.Cofactor);
+ // bool isPrime = PrimeTester.IsPrimeInternal(factors.Cofactor, CancellationToken.None);
+ if (!factors.FullyFactored && factors.Cofactor > 1UL && isPrime)
+ {
+ buffer[length] = new FactorEntry(factors.Cofactor);
+ length++;
+ }
- break;
- }
- }
+ buffer = buffer.Slice(0, length); // Reuse the same span restricted to populated entries.
+ buffer.Sort(static (a, b) => a.Value.CompareTo(b.Value));
- return order;
- }
- finally
- {
- if (tempArray is not null)
- {
- factorPool.Return(tempArray, clearArray: false);
- }
- }
- }
+ for (int i = 0; i < buffer.Length; i++)
+ {
+ FactorEntry entry = buffer[i];
+ ulong primeFactor = entry.Value;
+ int exponent = entry.Exponent;
+ for (int iteration = 0; iteration < exponent; iteration++)
+ {
+ if ((order % primeFactor) != 0UL)
+ {
+ break;
+ }
+
+ ulong reduced = order / primeFactor;
+ if (Pow2EqualsOneCpu(reduced, prime, divisorData))
+ {
+ order = reduced;
+ continue;
+ }
+
+ break;
+ }
+ }
+
+ return order;
+ }
+ finally
+ {
+ if (tempArray is not null)
+ {
+ factorPool.Return(tempArray, clearArray: false);
+ }
+ }
+ }
private static bool TryConfirmOrderCpu(ulong prime, ulong order, in MontgomeryDivisorData divisorData, ref PartialFactorResult orderFactors)
{