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.Numerics;
using FluentAssertions;
using Xunit;
@@ -142,6 +143,29 @@ public class PrimeOrderCalculatorTests
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)
{
if (prime == 2UL)
+39 -21
View File
@@ -126,30 +126,48 @@ public class MersenneDivisorCycles
return CalculateCycleLength(divisor);
}
public static UInt128 GetCycle(UInt128 divisor)
{
// For divisor = 2^k, cycle is 1
if ((divisor & (divisor - UInt128.One)) == UInt128.Zero)
{
return UInt128.One;
}
public static UInt128 GetCycle(UInt128 divisor)
{
// For divisor = 2^k, cycle is 1
if ((divisor & (divisor - UInt128.One)) == UInt128.Zero)
{
return UInt128.One;
}
// 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, pow = UInt128Numbers.Two;
while (pow != UInt128.One)
{
pow <<= 1;
if (pow >= divisor)
pow -= divisor;
if (divisor <= ulong.MaxValue)
{
ulong cycle = CalculateCycleLength((ulong)divisor);
return (UInt128)cycle;
}
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(
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;
internal static class PrimeOrderCalculator
internal static partial class PrimeOrderCalculator
{
internal enum PrimeOrderStatus
{