Add heuristic support for 128-bit prime orders

This commit is contained in:
Marek Lemańczyk committed 2025-10-04 01:58:11 +02:00
1 parent e6a80bb213
commit 07dad89fdf
4 files changed
+1108 -22

No files matched your search

@@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics;
using FluentAssertions; using FluentAssertions;
using Xunit; using Xunit;
@@ -142,6 +143,29 @@ public class PrimeOrderCalculatorTests
strict.Order.Should().Be(heuristic.Order); strict.Order.Should().Be(heuristic.Order);
} }
[Fact]
[Trait("Category", "Fast")]
public void Calculate_handles_128_bit_prime()
{
UInt128 prime = UInt128.Parse("18446744073709641691");
PrimeOrderCalculator.PrimeOrderResultWide result = PrimeOrderCalculator.Calculate(
prime,
previousOrder: null,
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault);
result.Status.Should().Be(PrimeOrderCalculator.PrimeOrderStatus.Found);
result.Order.Should().Be((UInt128)1229782938247309446UL);
}
[Fact]
[Trait("Category", "Fast")]
public void GetCycle_uses_wide_prime_heuristic()
{
UInt128 prime = UInt128.Parse("18446744073709641691");
MersenneDivisorCycles.GetCycle(prime).Should().Be((UInt128)1229782938247309446UL);
}
private static ulong ComputeOrderByDoubling(ulong prime) private static ulong ComputeOrderByDoubling(ulong prime)
{ {
if (prime == 2UL) if (prime == 2UL)
+39 -21
View File
@@ -126,30 +126,48 @@ public class MersenneDivisorCycles
return CalculateCycleLength(divisor); return CalculateCycleLength(divisor);
} }
public static UInt128 GetCycle(UInt128 divisor) public static UInt128 GetCycle(UInt128 divisor)
{ {
// For divisor = 2^k, cycle is 1 // For divisor = 2^k, cycle is 1
if ((divisor & (divisor - UInt128.One)) == UInt128.Zero) if ((divisor & (divisor - UInt128.One)) == UInt128.Zero)
{ {
return UInt128.One; return UInt128.One;
} }
// Otherwise, find order of 2 mod divisor if (divisor <= ulong.MaxValue)
// TODO: Port this UInt128 path to the unrolled-hex cycle calculator so wide {
// divisors stop relying on the slow shift-and-subtract loop measured in the ulong cycle = CalculateCycleLength((ulong)divisor);
// GPU benchmarks. return (UInt128)cycle;
UInt128 order = UInt128.One, pow = UInt128Numbers.Two; }
while (pow != UInt128.One)
{
pow <<= 1;
if (pow >= divisor)
pow -= divisor;
order++; PrimeOrderCalculator.PrimeOrderResultWide wideResult = PrimeOrderCalculator.Calculate(
} divisor,
previousOrder: null,
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault);
if (wideResult.Order != UInt128.Zero)
{
return wideResult.Order;
}
return order; // Otherwise, find order of 2 mod divisor
} // TODO: Port this UInt128 path to the unrolled-hex cycle calculator so wide
// divisors stop relying on the slow shift-and-subtract loop measured in the
// GPU benchmarks.
UInt128 order = UInt128.One;
UInt128 pow = UInt128Numbers.Two;
while (pow != UInt128.One)
{
pow <<= 1;
if (pow >= divisor)
{
pow -= divisor;
}
order++;
}
return order;
}
public static bool TryCalculateCycleLengthForExponent( public static bool TryCalculateCycleLengthForExponent(
ulong divisor, ulong divisor,
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -9,7 +9,7 @@ using System.Threading;
namespace PerfectNumbers.Core; namespace PerfectNumbers.Core;
internal static class PrimeOrderCalculator internal static partial class PrimeOrderCalculator
{ {
internal enum PrimeOrderStatus internal enum PrimeOrderStatus
{ {