Add heuristic support for 128-bit prime orders
This commit is contained in:
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)
|
||||
|
||||
@@ -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
@@ -9,7 +9,7 @@ using System.Threading;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
internal static class PrimeOrderCalculator
|
||||
internal static partial class PrimeOrderCalculator
|
||||
{
|
||||
internal enum PrimeOrderStatus
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user