Support for q > 2^64 added in --mersenne=bydivisor mode
This commit is contained in:
1 parent
cf5bd6bc1a
commit
86f20ed083
11 files changed
+542
-222
No files matched your search
@@ -1,4 +1,7 @@
|
|||||||
|
using System;
|
||||||
using System.Buffers.Text;
|
using System.Buffers.Text;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using PerfectNumbers.Core;
|
using PerfectNumbers.Core;
|
||||||
@@ -93,19 +96,46 @@ internal static class CalculationResultHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static void HandleResult(ulong currentP, BigInteger divisor, bool searchedMersenne, bool detailedCheck, bool passedAllTests, bool lastWasComposite)
|
||||||
|
{
|
||||||
|
Span<char> divisorBuffer = stackalloc char[256];
|
||||||
|
if (!divisor.TryFormat(divisorBuffer, out int divisorChars, provider: CultureInfo.InvariantCulture))
|
||||||
|
{
|
||||||
|
string divisorString = divisor.ToString(CultureInfo.InvariantCulture);
|
||||||
|
HandleResultInternal(currentP, divisorString.AsSpan(), searchedMersenne, detailedCheck, passedAllTests, lastWasComposite);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HandleResultInternal(currentP, divisorBuffer[..divisorChars], searchedMersenne, detailedCheck, passedAllTests, lastWasComposite);
|
||||||
|
}
|
||||||
|
|
||||||
internal static void HandleResult(ulong currentP, ulong divisor, bool searchedMersenne, bool detailedCheck, bool passedAllTests, bool lastWasComposite)
|
internal static void HandleResult(ulong currentP, ulong divisor, bool searchedMersenne, bool detailedCheck, bool passedAllTests, bool lastWasComposite)
|
||||||
|
{
|
||||||
|
Span<char> divisorBuffer = stackalloc char[32];
|
||||||
|
if (!divisor.TryFormat(divisorBuffer, out int divisorChars, provider: CultureInfo.InvariantCulture))
|
||||||
|
{
|
||||||
|
string divisorString = divisor.ToString(CultureInfo.InvariantCulture);
|
||||||
|
HandleResultInternal(currentP, divisorString.AsSpan(), searchedMersenne, detailedCheck, passedAllTests, lastWasComposite);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HandleResultInternal(currentP, divisorBuffer[..divisorChars], searchedMersenne, detailedCheck, passedAllTests, lastWasComposite);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void HandleResultInternal(ulong currentP, ReadOnlySpan<char> divisorText, bool searchedMersenne, bool detailedCheck, bool passedAllTests, bool lastWasComposite)
|
||||||
{
|
{
|
||||||
// Program flow ensures Initialize(...) and InitializeOutputBuffer() ran before this call. Do not add initialization guards here.
|
// Program flow ensures Initialize(...) and InitializeOutputBuffer() ran before this call. Do not add initialization guards here.
|
||||||
|
|
||||||
Span<byte> stackBuffer = stackalloc byte[128];
|
Span<byte> stackBuffer = stackalloc byte[256];
|
||||||
byte[]? rentedBuffer = null;
|
byte[]? rentedBuffer = null;
|
||||||
Span<byte> utf8Span = stackBuffer;
|
Span<byte> utf8Span = stackBuffer;
|
||||||
int byteCount = TryFormatResultLine(utf8Span, currentP, divisor, searchedMersenne, detailedCheck, passedAllTests);
|
int byteCount = TryFormatResultLine(utf8Span, currentP, divisorText, searchedMersenne, detailedCheck, passedAllTests);
|
||||||
if (byteCount < 0)
|
if (byteCount < 0)
|
||||||
{
|
{
|
||||||
rentedBuffer = ThreadStaticPools.BytePool.Rent(256);
|
int minimumSize = Math.Max((divisorText.Length + 5) * 4, 256);
|
||||||
|
rentedBuffer = ThreadStaticPools.BytePool.Rent(minimumSize);
|
||||||
utf8Span = rentedBuffer;
|
utf8Span = rentedBuffer;
|
||||||
byteCount = TryFormatResultLine(utf8Span, currentP, divisor, searchedMersenne, detailedCheck, passedAllTests);
|
byteCount = TryFormatResultLine(utf8Span, currentP, divisorText, searchedMersenne, detailedCheck, passedAllTests);
|
||||||
if (byteCount < 0)
|
if (byteCount < 0)
|
||||||
{
|
{
|
||||||
ThreadStaticPools.BytePool.Return(rentedBuffer);
|
ThreadStaticPools.BytePool.Return(rentedBuffer);
|
||||||
@@ -114,7 +144,7 @@ internal static class CalculationResultHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<byte> utf8Line = utf8Span[..byteCount];
|
ReadOnlySpan<byte> utf8Line = utf8Span[..byteCount];
|
||||||
Span<char> charBuffer = stackalloc char[128];
|
Span<char> charBuffer = stackalloc char[256];
|
||||||
int charCount = Utf8Encoding.GetChars(utf8Line, charBuffer);
|
int charCount = Utf8Encoding.GetChars(utf8Line, charBuffer);
|
||||||
ReadOnlySpan<char> recordSpan = charBuffer[..charCount];
|
ReadOnlySpan<char> recordSpan = charBuffer[..charCount];
|
||||||
|
|
||||||
@@ -229,7 +259,7 @@ internal static class CalculationResultHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static int TryFormatResultLine(Span<byte> destination, ulong currentP, ulong divisor, bool searchedMersenne, bool detailedCheck, bool passedAllTests)
|
private static int TryFormatResultLine(Span<byte> destination, ulong currentP, ReadOnlySpan<char> divisorText, bool searchedMersenne, bool detailedCheck, bool passedAllTests)
|
||||||
{
|
{
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
|
||||||
@@ -246,16 +276,14 @@ internal static class CalculationResultHandler
|
|||||||
|
|
||||||
destination[offset++] = (byte)',';
|
destination[offset++] = (byte)',';
|
||||||
|
|
||||||
if (!Utf8Formatter.TryFormat(divisor, destination[offset..], out written))
|
int divisorByteCount = Utf8Encoding.GetByteCount(divisorText);
|
||||||
|
if (offset + divisorByteCount >= destination.Length)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
offset += written;
|
divisorByteCount = Utf8Encoding.GetBytes(divisorText, destination[offset..]);
|
||||||
if (offset >= destination.Length)
|
offset += divisorByteCount;
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
destination[offset++] = (byte)',';
|
destination[offset++] = (byte)',';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Numerics;
|
||||||
using PerfectNumbers.Core;
|
using PerfectNumbers.Core;
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
@@ -50,7 +51,7 @@ internal readonly struct CliArguments
|
|||||||
internal readonly double ZeroFractionHard;
|
internal readonly double ZeroFractionHard;
|
||||||
internal readonly double ZeroFractionConjecture;
|
internal readonly double ZeroFractionConjecture;
|
||||||
internal readonly int MaxZeroConjecture;
|
internal readonly int MaxZeroConjecture;
|
||||||
internal readonly ulong MinK;
|
internal readonly BigInteger MinK;
|
||||||
internal readonly int GpuRatio;
|
internal readonly int GpuRatio;
|
||||||
|
|
||||||
private CliArguments(
|
private CliArguments(
|
||||||
@@ -96,7 +97,7 @@ internal readonly struct CliArguments
|
|||||||
double zeroFractionHard,
|
double zeroFractionHard,
|
||||||
double zeroFractionConjecture,
|
double zeroFractionConjecture,
|
||||||
int maxZeroConjecture,
|
int maxZeroConjecture,
|
||||||
ulong minK,
|
BigInteger minK,
|
||||||
int gpuRatio)
|
int gpuRatio)
|
||||||
{
|
{
|
||||||
StartPrime = startPrime;
|
StartPrime = startPrime;
|
||||||
@@ -191,7 +192,7 @@ internal readonly struct CliArguments
|
|||||||
double zeroFractionHard = -1.0;
|
double zeroFractionHard = -1.0;
|
||||||
double zeroFractionConjecture = -1.0;
|
double zeroFractionConjecture = -1.0;
|
||||||
int maxZeroConjecture = -1;
|
int maxZeroConjecture = -1;
|
||||||
ulong minK = 1UL;
|
BigInteger minK = BigInteger.One;
|
||||||
int gpuRatio = PerfectNumberConstants.GpuRatio;
|
int gpuRatio = PerfectNumberConstants.GpuRatio;
|
||||||
|
|
||||||
foreach (string argument in args)
|
foreach (string argument in args)
|
||||||
@@ -314,14 +315,31 @@ internal readonly struct CliArguments
|
|||||||
|
|
||||||
if (argument.StartsWith("--min-k=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--min-k=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (Utf8CliParser.TryParseUInt64(argument.AsSpan("--min-k=".Length), out ulong parsedMinK) && parsedMinK > 0UL)
|
ReadOnlySpan<char> minKSpan = argument.AsSpan("--min-k=".Length);
|
||||||
|
if (Utf8CliParser.TryParseUInt64(minKSpan, out ulong parsedMinK) && parsedMinK > 0UL)
|
||||||
{
|
{
|
||||||
minK = parsedMinK;
|
minK = parsedMinK;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine("Invalid value for --min-k.");
|
try
|
||||||
return default;
|
{
|
||||||
|
BigInteger parsedMinKBig = BigInteger.Parse(minKSpan, CultureInfo.InvariantCulture);
|
||||||
|
if (parsedMinKBig > BigInteger.Zero)
|
||||||
|
{
|
||||||
|
minK = parsedMinKBig;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Invalid value for --min-k.");
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Console.WriteLine("Invalid value for --min-k.");
|
||||||
|
return default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using EvenPerfectBitScanner.Candidates;
|
using EvenPerfectBitScanner.Candidates;
|
||||||
using EvenPerfectBitScanner.IO;
|
using EvenPerfectBitScanner.IO;
|
||||||
@@ -241,6 +242,11 @@ internal static class Program
|
|||||||
: new MersenneNumberDivisorByDivisorCpuTester(orderDevice);
|
: new MersenneNumberDivisorByDivisorCpuTester(orderDevice);
|
||||||
_byDivisorTester.MinK = _cliArguments.MinK;
|
_byDivisorTester.MinK = _cliArguments.MinK;
|
||||||
_byDivisorTester.BatchSize = scanBatchSize;
|
_byDivisorTester.BatchSize = scanBatchSize;
|
||||||
|
if (_byDivisorTester is MersenneNumberDivisorByDivisorGpuTester && _cliArguments.MinK > ulong.MaxValue)
|
||||||
|
{
|
||||||
|
Console.WriteLine("--min-k values above UInt64 are only supported on the CPU path for --mersenne=bydivisor.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load RLE blacklist (optional)
|
// Load RLE blacklist (optional)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Numerics;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
@@ -12,7 +13,7 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
|||||||
{
|
{
|
||||||
var candidates = new List<ulong> { 5UL, 7UL, 11UL, 13UL };
|
var candidates = new List<ulong> { 5UL, 7UL, 11UL, 13UL };
|
||||||
var tester = new FakeTester();
|
var tester = new FakeTester();
|
||||||
var results = new List<(ulong Prime, bool Searched, bool Detailed, bool Passed, ulong Divisor)>();
|
var results = new List<(ulong Prime, bool Searched, bool Detailed, bool Passed, BigInteger Divisor)>();
|
||||||
int compositeMarks = 0;
|
int compositeMarks = 0;
|
||||||
int clearedMarks = 0;
|
int clearedMarks = 0;
|
||||||
|
|
||||||
@@ -31,12 +32,12 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
|||||||
compositeMarks.Should().Be(2);
|
compositeMarks.Should().Be(2);
|
||||||
clearedMarks.Should().Be(2);
|
clearedMarks.Should().Be(2);
|
||||||
results.Should().Equal(
|
results.Should().Equal(
|
||||||
new[]
|
new (ulong Prime, bool Searched, bool Detailed, bool Passed, BigInteger Divisor)[]
|
||||||
{
|
{
|
||||||
(5UL, true, true, true, 0UL),
|
(5UL, true, true, true, BigInteger.Zero),
|
||||||
(7UL, true, true, false, 7UL),
|
(7UL, true, true, false, new BigInteger(7)),
|
||||||
(11UL, true, true, false, 11UL),
|
(11UL, true, true, false, new BigInteger(11)),
|
||||||
(13UL, true, true, true, 0UL),
|
(13UL, true, true, true, BigInteger.Zero),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +47,7 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
|||||||
|
|
||||||
public int BatchSize { get; set; }
|
public int BatchSize { get; set; }
|
||||||
|
|
||||||
public ulong MinK { get; set; }
|
public BigInteger MinK { get; set; }
|
||||||
|
|
||||||
public string? StateFilePath { get; set; }
|
public string? StateFilePath { get; set; }
|
||||||
|
|
||||||
@@ -54,7 +55,7 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResumeFromState(ulong lastSavedK)
|
public void ResumeFromState(BigInteger lastSavedK)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +71,7 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
|||||||
return DivisorLimit;
|
return DivisorLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsPrime(PrimeOrderCalculatorAccelerator gpu, ulong prime, out bool divisorsExhausted, out ulong divisor)
|
public bool IsPrime(PrimeOrderCalculatorAccelerator gpu, ulong prime, out bool divisorsExhausted, out BigInteger divisor)
|
||||||
{
|
{
|
||||||
divisorsExhausted = true;
|
divisorsExhausted = true;
|
||||||
if (prime % 7UL == 0UL)
|
if (prime % 7UL == 0UL)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Numerics;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using PerfectNumbers.Core.Cpu;
|
using PerfectNumbers.Core.Cpu;
|
||||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
@@ -17,17 +18,17 @@ public class MersenneNumberDivisorCpuTesterTests
|
|||||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
tester.IsPrime(gpu, 5UL, out bool divisorsExhausted, out ulong divisor).Should().BeTrue();
|
tester.IsPrime(gpu, 5UL, out bool divisorsExhausted, out BigInteger divisor).Should().BeTrue();
|
||||||
divisorsExhausted.Should().BeTrue();
|
divisorsExhausted.Should().BeTrue();
|
||||||
divisor.Should().Be(0UL);
|
divisor.Should().Be(BigInteger.Zero);
|
||||||
|
|
||||||
tester.IsPrime(gpu, 7UL, out divisorsExhausted, out divisor).Should().BeTrue();
|
tester.IsPrime(gpu, 7UL, out divisorsExhausted, out divisor).Should().BeTrue();
|
||||||
divisorsExhausted.Should().BeTrue();
|
divisorsExhausted.Should().BeTrue();
|
||||||
divisor.Should().Be(0UL);
|
divisor.Should().Be(BigInteger.Zero);
|
||||||
|
|
||||||
tester.IsPrime(gpu, 11UL, out divisorsExhausted, out divisor).Should().BeFalse();
|
tester.IsPrime(gpu, 11UL, out divisorsExhausted, out divisor).Should().BeFalse();
|
||||||
divisorsExhausted.Should().BeTrue();
|
divisorsExhausted.Should().BeTrue();
|
||||||
divisor.Should().BeGreaterThan(0UL);
|
divisor.Should().BeGreaterThan(BigInteger.Zero);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Numerics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using PerfectNumbers.Core.Cpu;
|
using PerfectNumbers.Core.Cpu;
|
||||||
@@ -102,17 +103,17 @@ public class MersenneNumberDivisorGpuTesterTests
|
|||||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
tester.IsPrime(gpu, 31UL, out bool exhausted, out ulong divisor).Should().BeTrue();
|
tester.IsPrime(gpu, 31UL, out bool exhausted, out BigInteger divisor).Should().BeTrue();
|
||||||
exhausted.Should().BeTrue();
|
exhausted.Should().BeTrue();
|
||||||
divisor.Should().Be(0UL);
|
divisor.Should().Be(BigInteger.Zero);
|
||||||
|
|
||||||
tester.IsPrime(gpu, 37UL, out exhausted, out divisor).Should().BeFalse();
|
tester.IsPrime(gpu, 37UL, out exhausted, out divisor).Should().BeFalse();
|
||||||
exhausted.Should().BeTrue();
|
exhausted.Should().BeTrue();
|
||||||
divisor.Should().BeGreaterThan(0UL);
|
divisor.Should().BeGreaterThan(BigInteger.Zero);
|
||||||
|
|
||||||
tester.IsPrime(gpu, 43UL, out exhausted, out divisor).Should().BeFalse();
|
tester.IsPrime(gpu, 43UL, out exhausted, out divisor).Should().BeFalse();
|
||||||
exhausted.Should().BeTrue();
|
exhausted.Should().BeTrue();
|
||||||
divisor.Should().BeGreaterThan(0UL);
|
divisor.Should().BeGreaterThan(BigInteger.Zero);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -145,15 +146,15 @@ public class MersenneNumberDivisorGpuTesterTests
|
|||||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
tester.IsPrime(gpu, 31UL, out _, out ulong divisor).Should().BeTrue();
|
tester.IsPrime(gpu, 31UL, out _, out BigInteger divisor).Should().BeTrue();
|
||||||
divisor.Should().Be(0UL);
|
divisor.Should().Be(BigInteger.Zero);
|
||||||
tester.IsPrime(gpu, 37UL, out bool exhausted, out divisor).Should().BeFalse();
|
tester.IsPrime(gpu, 37UL, out bool exhausted, out divisor).Should().BeFalse();
|
||||||
exhausted.Should().BeTrue();
|
exhausted.Should().BeTrue();
|
||||||
divisor.Should().BeGreaterThan(0UL);
|
divisor.Should().BeGreaterThan(BigInteger.Zero);
|
||||||
|
|
||||||
tester.IsPrime(gpu, 41UL, out bool divisorsExhausted, out divisor).Should().BeFalse();
|
tester.IsPrime(gpu, 41UL, out bool divisorsExhausted, out divisor).Should().BeFalse();
|
||||||
divisorsExhausted.Should().BeTrue();
|
divisorsExhausted.Should().BeTrue();
|
||||||
divisor.Should().BeGreaterThan(0UL);
|
divisor.Should().BeGreaterThan(BigInteger.Zero);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -175,16 +176,16 @@ public class MersenneNumberDivisorGpuTesterTests
|
|||||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
tester.IsPrime(gpu, exponent, out bool exhausted, out ulong divisor).Should().Be(expectedPrime);
|
tester.IsPrime(gpu, exponent, out bool exhausted, out BigInteger divisor).Should().Be(expectedPrime);
|
||||||
|
|
||||||
exhausted.Should().BeTrue();
|
exhausted.Should().BeTrue();
|
||||||
if (expectedPrime)
|
if (expectedPrime)
|
||||||
{
|
{
|
||||||
divisor.Should().Be(0UL);
|
divisor.Should().Be(BigInteger.Zero);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
divisor.Should().BeGreaterThan(0UL);
|
divisor.Should().BeGreaterThan(BigInteger.Zero);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -207,9 +208,9 @@ public class MersenneNumberDivisorGpuTesterTests
|
|||||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
tester.IsPrime(gpu, 41UL, out bool divisorsExhausted, out ulong divisor).Should().BeFalse();
|
tester.IsPrime(gpu, 41UL, out bool divisorsExhausted, out BigInteger divisor).Should().BeFalse();
|
||||||
divisorsExhausted.Should().BeTrue();
|
divisorsExhausted.Should().BeTrue();
|
||||||
divisor.Should().BeGreaterThan(0UL);
|
divisor.Should().BeGreaterThan(BigInteger.Zero);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -397,9 +398,9 @@ public class MersenneNumberDivisorGpuTesterTests
|
|||||||
.GetField("_divisorLimit", BindingFlags.NonPublic | BindingFlags.Instance)!
|
.GetField("_divisorLimit", BindingFlags.NonPublic | BindingFlags.Instance)!
|
||||||
.SetValue(tester, 200UL);
|
.SetValue(tester, 200UL);
|
||||||
|
|
||||||
tester.IsPrime(gpu, 19UL, out bool divisorsExhausted, out ulong divisor).Should().BeTrue();
|
tester.IsPrime(gpu, 19UL, out bool divisorsExhausted, out BigInteger divisor).Should().BeTrue();
|
||||||
divisorsExhausted.Should().BeTrue();
|
divisorsExhausted.Should().BeTrue();
|
||||||
divisor.Should().Be(0UL);
|
divisor.Should().Be(BigInteger.Zero);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -8,15 +9,15 @@ namespace PerfectNumbers.Core.Cpu;
|
|||||||
|
|
||||||
public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDivisorByDivisorTester
|
public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDivisorByDivisorTester
|
||||||
{
|
{
|
||||||
private ulong _divisorLimit;
|
private BigInteger _divisorLimit;
|
||||||
private int _batchSize = 1_024;
|
private int _batchSize = 1_024;
|
||||||
private ulong _minK = 1UL;
|
private BigInteger _minK = BigInteger.One;
|
||||||
private readonly ComputationDevice _orderDevice;
|
private readonly ComputationDevice _orderDevice;
|
||||||
private readonly TryCycleLengthDelegate _tryCalculateCycleLengthForExponent;
|
private readonly TryCycleLengthDelegate _tryCalculateCycleLengthForExponent;
|
||||||
private readonly CalculateCycleLengthDelegate _calculateCycleLength;
|
private readonly CalculateCycleLengthDelegate _calculateCycleLength;
|
||||||
private string? _stateFilePath;
|
private string? _stateFilePath;
|
||||||
private int _stateCounter;
|
private int _stateCounter;
|
||||||
private ulong _lastSavedK;
|
private BigInteger _lastSavedK;
|
||||||
public ComputationDevice OrderDevice => _orderDevice;
|
public ComputationDevice OrderDevice => _orderDevice;
|
||||||
|
|
||||||
private delegate bool TryCycleLengthDelegate(
|
private delegate bool TryCycleLengthDelegate(
|
||||||
@@ -67,11 +68,11 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
set => _batchSize = Math.Max(1, value);
|
set => _batchSize = Math.Max(1, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong MinK
|
public BigInteger MinK
|
||||||
{
|
{
|
||||||
get => _minK;
|
get => _minK;
|
||||||
set => _minK = value < 1UL ? 1UL : value;
|
set => _minK = value < BigInteger.One ? BigInteger.One : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string? StateFilePath
|
public string? StateFilePath
|
||||||
{
|
{
|
||||||
@@ -84,37 +85,42 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
_stateCounter = 0;
|
_stateCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResumeFromState(ulong lastSavedK)
|
public void ResumeFromState(BigInteger lastSavedK)
|
||||||
{
|
{
|
||||||
_lastSavedK = lastSavedK;
|
_lastSavedK = lastSavedK;
|
||||||
_minK = lastSavedK + 1UL;
|
_minK = lastSavedK + BigInteger.One;
|
||||||
_stateCounter = 0;
|
_stateCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ConfigureFromMaxPrime(ulong maxPrime)
|
public void ConfigureFromMaxPrime(ulong maxPrime)
|
||||||
{
|
{
|
||||||
_divisorLimit = ComputeDivisorLimitFromMaxPrime(maxPrime);
|
_divisorLimit = ComputeDivisorLimitFromMaxPrimeBig(maxPrime);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong DivisorLimit
|
public ulong DivisorLimit
|
||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return _divisorLimit;
|
if (_divisorLimit.IsZero)
|
||||||
}
|
{
|
||||||
}
|
return ulong.MaxValue;
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
return _divisorLimit > ulong.MaxValue ? ulong.MaxValue : (ulong)_divisorLimit;
|
||||||
public ulong GetAllowedMaxDivisor(ulong prime) => ComputeAllowedMaxDivisor(prime, _divisorLimit);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsPrime(PrimeOrderCalculatorAccelerator gpu, ulong prime, out bool divisorsExhausted, out ulong divisor)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
{
|
public ulong GetAllowedMaxDivisor(ulong prime) => ComputeAllowedMaxDivisor(prime, _divisorLimit);
|
||||||
ulong allowedMax = ComputeAllowedMaxDivisor(prime, _divisorLimit);
|
|
||||||
|
|
||||||
// The CPU by-divisor run always hands us primes with enormous divisor limits, so the fallback below never executes.
|
public bool IsPrime(PrimeOrderCalculatorAccelerator gpu, ulong prime, out bool divisorsExhausted, out BigInteger divisor)
|
||||||
// if (allowedMax < 3UL)
|
{
|
||||||
// {
|
BigInteger allowedMax = ComputeAllowedMaxDivisorBig(prime, _divisorLimit);
|
||||||
|
|
||||||
|
// The CPU by-divisor run always hands us primes with enormous divisor limits, so the fallback below never executes.
|
||||||
|
// if (allowedMax < 3UL)
|
||||||
|
// {
|
||||||
// // EvenPerfectBitScanner routes primes below the small-divisor cutoff to the GPU path, so the CPU path still sees
|
// // EvenPerfectBitScanner routes primes below the small-divisor cutoff to the GPU path, so the CPU path still sees
|
||||||
// // trivial candidates during targeted tests. Short-circuit here to keep those runs aligned with the production flow.
|
// // trivial candidates during targeted tests. Short-circuit here to keep those runs aligned with the production flow.
|
||||||
// divisorsExhausted = true;
|
// divisorsExhausted = true;
|
||||||
@@ -123,34 +129,34 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
|
|
||||||
bool processedAll;
|
bool processedAll;
|
||||||
|
|
||||||
bool composite = CheckDivisors(
|
bool composite = CheckDivisors(
|
||||||
gpu,
|
gpu,
|
||||||
prime,
|
prime,
|
||||||
allowedMax,
|
allowedMax,
|
||||||
_minK,
|
_minK,
|
||||||
out processedAll,
|
out processedAll,
|
||||||
out divisor);
|
out divisor);
|
||||||
|
|
||||||
if (composite)
|
if (composite)
|
||||||
{
|
{
|
||||||
divisorsExhausted = true;
|
divisorsExhausted = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
divisorsExhausted = processedAll || composite;
|
divisorsExhausted = processedAll || composite;
|
||||||
divisor = 0UL;
|
divisor = BigInteger.Zero;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PrepareCandidates(in ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues)
|
public void PrepareCandidates(in ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues)
|
||||||
{
|
{
|
||||||
ulong divisorLimit = _divisorLimit;
|
ulong divisorLimit = GetAllowedDivisorLimitForSpan(_divisorLimit);
|
||||||
int length = primes.Length;
|
int length = primes.Length;
|
||||||
for (int index = 0; index < length; index++)
|
for (int index = 0; index < length; index++)
|
||||||
{
|
{
|
||||||
allowedMaxValues[index] = ComputeAllowedMaxDivisor(primes[index], divisorLimit);
|
allowedMaxValues[index] = ComputeAllowedMaxDivisor(primes[index], divisorLimit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession(PrimeOrderCalculatorAccelerator gpu)
|
public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession(PrimeOrderCalculatorAccelerator gpu)
|
||||||
{
|
{
|
||||||
@@ -165,17 +171,84 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
return new MersenneCpuDivisorScanSession(gpu, _orderDevice);
|
return new MersenneCpuDivisorScanSession(gpu, _orderDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CheckDivisors(
|
private bool CheckDivisors(
|
||||||
PrimeOrderCalculatorAccelerator gpu,
|
PrimeOrderCalculatorAccelerator gpu,
|
||||||
ulong prime,
|
ulong prime,
|
||||||
ulong allowedMax,
|
BigInteger allowedMax,
|
||||||
ulong minK,
|
BigInteger minK,
|
||||||
out bool processedAll,
|
out bool processedAll,
|
||||||
out ulong foundDivisor)
|
out BigInteger foundDivisor)
|
||||||
{
|
{
|
||||||
foundDivisor = 0UL;
|
BigInteger normalizedMinK = minK < BigInteger.One ? BigInteger.One : minK;
|
||||||
processedAll = true;
|
BigInteger step = ((BigInteger)prime) << 1;
|
||||||
ulong currentK = minK < 1UL ? 1UL : minK;
|
BigInteger firstDivisor = (step * normalizedMinK) + BigInteger.One;
|
||||||
|
bool unlimited = allowedMax.IsZero;
|
||||||
|
|
||||||
|
bool fits64 = normalizedMinK <= ulong.MaxValue && firstDivisor <= ulong.MaxValue;
|
||||||
|
bool limitFits64 = unlimited || allowedMax <= ulong.MaxValue;
|
||||||
|
if (fits64 && limitFits64)
|
||||||
|
{
|
||||||
|
ulong allowedMax64 = unlimited ? ulong.MaxValue : (ulong)allowedMax;
|
||||||
|
bool composite64 = CheckDivisors64Bit(
|
||||||
|
gpu,
|
||||||
|
prime,
|
||||||
|
allowedMax64,
|
||||||
|
(ulong)normalizedMinK,
|
||||||
|
out processedAll,
|
||||||
|
out ulong foundDivisor64);
|
||||||
|
foundDivisor = foundDivisor64;
|
||||||
|
return composite64;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fits64 && allowedMax > ulong.MaxValue)
|
||||||
|
{
|
||||||
|
ulong allowedMax64 = ulong.MaxValue;
|
||||||
|
bool composite64 = CheckDivisors64Bit(
|
||||||
|
gpu,
|
||||||
|
prime,
|
||||||
|
allowedMax64,
|
||||||
|
(ulong)normalizedMinK,
|
||||||
|
out processedAll,
|
||||||
|
out ulong foundDivisor64);
|
||||||
|
if (composite64)
|
||||||
|
{
|
||||||
|
foundDivisor = foundDivisor64;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger iterations = ((BigInteger)(allowedMax64 - (ulong)firstDivisor) / (ulong)step) + BigInteger.One;
|
||||||
|
BigInteger nextK = normalizedMinK + iterations;
|
||||||
|
return CheckDivisorsLarge(
|
||||||
|
gpu,
|
||||||
|
prime,
|
||||||
|
allowedMax,
|
||||||
|
nextK,
|
||||||
|
step,
|
||||||
|
out processedAll,
|
||||||
|
out foundDivisor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CheckDivisorsLarge(
|
||||||
|
gpu,
|
||||||
|
prime,
|
||||||
|
allowedMax,
|
||||||
|
normalizedMinK,
|
||||||
|
step,
|
||||||
|
out processedAll,
|
||||||
|
out foundDivisor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CheckDivisors64Bit(
|
||||||
|
PrimeOrderCalculatorAccelerator gpu,
|
||||||
|
ulong prime,
|
||||||
|
ulong allowedMax,
|
||||||
|
ulong minK,
|
||||||
|
out bool processedAll,
|
||||||
|
out ulong foundDivisor)
|
||||||
|
{
|
||||||
|
foundDivisor = 0UL;
|
||||||
|
processedAll = true;
|
||||||
|
ulong currentK = minK < 1UL ? 1UL : minK;
|
||||||
|
|
||||||
// The EvenPerfectBitScanner feeds primes >= 138,000,000 here, so allowedMax >= 3 in production runs.
|
// The EvenPerfectBitScanner feeds primes >= 138,000,000 here, so allowedMax >= 3 in production runs.
|
||||||
// Keeping the guard commented out documents the reasoning for benchmarks and tests.
|
// Keeping the guard commented out documents the reasoning for benchmarks and tests.
|
||||||
@@ -405,16 +478,105 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
remainder19 = AddMod19(remainder19, step19);
|
remainder19 = AddMod19(remainder19, step19);
|
||||||
}
|
}
|
||||||
|
|
||||||
processedAll = true;
|
processedAll = true;
|
||||||
foundDivisor = 0UL;
|
foundDivisor = 0UL;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CheckDivisors64Range(
|
private bool CheckDivisorsLarge(
|
||||||
PrimeOrderCalculatorAccelerator gpu,
|
PrimeOrderCalculatorAccelerator gpu,
|
||||||
ulong prime,
|
ulong prime,
|
||||||
ulong step,
|
BigInteger allowedMax,
|
||||||
ulong allowedMax,
|
BigInteger minK,
|
||||||
|
BigInteger step,
|
||||||
|
out bool processedAll,
|
||||||
|
out BigInteger foundDivisor)
|
||||||
|
{
|
||||||
|
processedAll = true;
|
||||||
|
foundDivisor = BigInteger.Zero;
|
||||||
|
BigInteger currentK = minK < BigInteger.One ? BigInteger.One : minK;
|
||||||
|
BigInteger divisor = (step * currentK) + BigInteger.One;
|
||||||
|
_ = gpu;
|
||||||
|
if (allowedMax.IsZero)
|
||||||
|
{
|
||||||
|
processedAll = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (divisor > allowedMax)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LastDigit lastDigit = (prime & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||||
|
ushort decimalMask = DivisorGenerator.GetDecimalMask(lastDigit);
|
||||||
|
|
||||||
|
byte step10 = (byte)(step % 10);
|
||||||
|
byte step8 = (byte)(step % 8);
|
||||||
|
byte step3 = (byte)(step % 3);
|
||||||
|
byte step7 = (byte)(step % 7);
|
||||||
|
byte step11 = (byte)(step % 11);
|
||||||
|
byte step13 = (byte)(step % 13);
|
||||||
|
byte step17 = (byte)(step % 17);
|
||||||
|
byte step19 = (byte)(step % 19);
|
||||||
|
|
||||||
|
byte remainder10 = (byte)(divisor % 10);
|
||||||
|
byte remainder8 = (byte)(divisor % 8);
|
||||||
|
byte remainder3 = (byte)(divisor % 3);
|
||||||
|
byte remainder7 = (byte)(divisor % 7);
|
||||||
|
byte remainder11 = (byte)(divisor % 11);
|
||||||
|
byte remainder13 = (byte)(divisor % 13);
|
||||||
|
byte remainder17 = (byte)(divisor % 17);
|
||||||
|
byte remainder19 = (byte)(divisor % 19);
|
||||||
|
|
||||||
|
while (divisor <= allowedMax)
|
||||||
|
{
|
||||||
|
bool passesSmallModuli = remainder3 != 0 && remainder7 != 0 && remainder11 != 0 && remainder13 != 0 && remainder17 != 0 && remainder19 != 0;
|
||||||
|
if (passesSmallModuli && (remainder8 == 1 || remainder8 == 7) && ((decimalMask >> remainder10) & 1) != 0)
|
||||||
|
{
|
||||||
|
if (IsProbablePrimeBigInteger(divisor))
|
||||||
|
{
|
||||||
|
BigInteger powResult = BigInteger.ModPow(2, prime, divisor);
|
||||||
|
RecordState(currentK);
|
||||||
|
if (powResult.IsOne)
|
||||||
|
{
|
||||||
|
foundDivisor = divisor;
|
||||||
|
processedAll = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RecordState(currentK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentK += BigInteger.One;
|
||||||
|
divisor += step;
|
||||||
|
remainder10 = AddMod10(remainder10, step10);
|
||||||
|
remainder8 = AddMod8(remainder8, step8);
|
||||||
|
remainder3 = AddMod3(remainder3, step3);
|
||||||
|
remainder7 = AddMod7(remainder7, step7);
|
||||||
|
remainder11 = AddMod11(remainder11, step11);
|
||||||
|
remainder13 = AddMod13(remainder13, step13);
|
||||||
|
remainder17 = AddMod17(remainder17, step17);
|
||||||
|
remainder19 = AddMod19(remainder19, step19);
|
||||||
|
|
||||||
|
if (divisor > allowedMax)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
processedAll = true;
|
||||||
|
foundDivisor = BigInteger.Zero;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CheckDivisors64Range(
|
||||||
|
PrimeOrderCalculatorAccelerator gpu,
|
||||||
|
ulong prime,
|
||||||
|
ulong step,
|
||||||
|
ulong allowedMax,
|
||||||
ulong startK,
|
ulong startK,
|
||||||
ulong endK,
|
ulong endK,
|
||||||
ushort decimalMask,
|
ushort decimalMask,
|
||||||
@@ -647,18 +809,18 @@ private bool CheckDivisors64(
|
|||||||
return residue == 1UL ? (byte)1 : (byte)0;
|
return residue == 1UL ? (byte)1 : (byte)0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RecordState(ulong k)
|
private void RecordState(BigInteger k)
|
||||||
{
|
{
|
||||||
string? path = _stateFilePath;
|
string? path = _stateFilePath;
|
||||||
if (string.IsNullOrEmpty(path))
|
if (string.IsNullOrEmpty(path))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (k <= _lastSavedK)
|
if (k <= _lastSavedK)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int next = _stateCounter + 1;
|
int next = _stateCounter + 1;
|
||||||
if (next >= PerfectNumberConstants.ByDivisorStateSaveInterval)
|
if (next >= PerfectNumberConstants.ByDivisorStateSaveInterval)
|
||||||
@@ -674,15 +836,70 @@ private bool CheckDivisors64(
|
|||||||
_lastSavedK = k;
|
_lastSavedK = k;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_stateCounter = next;
|
_stateCounter = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryCalculateCycleLengthForExponentCpu(
|
private static bool IsProbablePrimeBigInteger(BigInteger value)
|
||||||
ulong divisor,
|
{
|
||||||
PrimeOrderCalculatorAccelerator gpu,
|
if (value <= 3)
|
||||||
ulong exponent,
|
{
|
||||||
|
return value >= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.IsEven)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger d = value - 1;
|
||||||
|
int s = 0;
|
||||||
|
while ((d & 1) == 0)
|
||||||
|
{
|
||||||
|
d >>= 1;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<int> bases = stackalloc int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37 };
|
||||||
|
for (int i = 0; i < bases.Length; i++)
|
||||||
|
{
|
||||||
|
int baseValue = bases[i];
|
||||||
|
if ((BigInteger)baseValue >= value)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger x = BigInteger.ModPow(baseValue, d, value);
|
||||||
|
if (x == 1 || x == value - 1)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool witnessFound = true;
|
||||||
|
for (int r = 1; r < s; r++)
|
||||||
|
{
|
||||||
|
x = BigInteger.ModPow(x, 2, value);
|
||||||
|
if (x == value - 1)
|
||||||
|
{
|
||||||
|
witnessFound = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (witnessFound)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryCalculateCycleLengthForExponentCpu(
|
||||||
|
ulong divisor,
|
||||||
|
PrimeOrderCalculatorAccelerator gpu,
|
||||||
|
ulong exponent,
|
||||||
in MontgomeryDivisorData divisorData,
|
in MontgomeryDivisorData divisorData,
|
||||||
out ulong cycleLength,
|
out ulong cycleLength,
|
||||||
out bool primeOrderFailed) => MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(
|
out bool primeOrderFailed) => MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(
|
||||||
@@ -854,35 +1071,63 @@ private bool CheckDivisors64(
|
|||||||
return (byte)((value + delta) & 7);
|
return (byte)((value + delta) & 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ulong ComputeDivisorLimitFromMaxPrime(ulong maxPrime)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
{
|
private static ulong GetAllowedDivisorLimitForSpan(BigInteger divisorLimit)
|
||||||
// The by-divisor CPU configuration only supplies primes greater than 1, so the guard below never trips.
|
{
|
||||||
// if (maxPrime <= 1UL)
|
if (divisorLimit.IsZero)
|
||||||
// {
|
{
|
||||||
// return 0UL;
|
return ulong.MaxValue;
|
||||||
// }
|
}
|
||||||
if (maxPrime - 1UL >= 64UL)
|
|
||||||
{
|
|
||||||
return ulong.MaxValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (1UL << (int)(maxPrime - 1UL)) - 1UL;
|
return divisorLimit > ulong.MaxValue ? ulong.MaxValue : (ulong)divisorLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ulong ComputeAllowedMaxDivisor(ulong prime, ulong divisorLimit)
|
private static BigInteger ComputeDivisorLimitFromMaxPrimeBig(ulong maxPrime)
|
||||||
{
|
{
|
||||||
// Production by-divisor scans only handle primes, so inputs never fall below 2.
|
if (maxPrime <= 1UL)
|
||||||
// if (prime <= 1UL)
|
{
|
||||||
// {
|
return BigInteger.Zero;
|
||||||
// return 0UL;
|
}
|
||||||
// }
|
|
||||||
if (prime - 1UL >= 64UL)
|
|
||||||
{
|
|
||||||
return divisorLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Math.Min((1UL << (int)(prime - 1UL)) - 1UL, divisorLimit);
|
if (maxPrime - 1UL >= 64UL)
|
||||||
}
|
{
|
||||||
|
return (BigInteger.One << 256) - BigInteger.One;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (BigInteger.One << (int)(maxPrime - 1UL)) - BigInteger.One;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BigInteger ComputeAllowedMaxDivisorBig(ulong prime, BigInteger divisorLimit)
|
||||||
|
{
|
||||||
|
if (prime <= 1UL)
|
||||||
|
{
|
||||||
|
return BigInteger.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (divisorLimit.IsZero || prime - 1UL >= 64UL)
|
||||||
|
{
|
||||||
|
return divisorLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger candidateLimit = (BigInteger.One << (int)(prime - 1UL)) - BigInteger.One;
|
||||||
|
if (divisorLimit.IsZero)
|
||||||
|
{
|
||||||
|
return candidateLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BigInteger.Min(candidateLimit, divisorLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ulong ComputeAllowedMaxDivisor(ulong prime, BigInteger divisorLimit)
|
||||||
|
{
|
||||||
|
BigInteger allowed = ComputeAllowedMaxDivisorBig(prime, divisorLimit);
|
||||||
|
if (allowed.IsZero)
|
||||||
|
{
|
||||||
|
return ulong.MaxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return allowed > ulong.MaxValue ? ulong.MaxValue : (ulong)allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
@@ -18,7 +20,7 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
|
|||||||
// so the synchronization fields from the previous implementation remain commented out here.
|
// so the synchronization fields from the previous implementation remain commented out here.
|
||||||
// private readonly object _sync = new();
|
// private readonly object _sync = new();
|
||||||
private ulong _divisorLimit;
|
private ulong _divisorLimit;
|
||||||
private ulong _minK = 1UL;
|
private BigInteger _minK = BigInteger.One;
|
||||||
// private bool _isConfigured;
|
// private bool _isConfigured;
|
||||||
|
|
||||||
private readonly ConcurrentBag<MersenneNumberDivisorByDivisorAccelerator>[] _resourcePool = [.. AcceleratorPool.Shared.Accelerators.Select(x => new ConcurrentBag<MersenneNumberDivisorByDivisorAccelerator>())];
|
private readonly ConcurrentBag<MersenneNumberDivisorByDivisorAccelerator>[] _resourcePool = [.. AcceleratorPool.Shared.Accelerators.Select(x => new ConcurrentBag<MersenneNumberDivisorByDivisorAccelerator>())];
|
||||||
@@ -37,10 +39,10 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
|
|||||||
set => GpuBatchSize = value;
|
set => GpuBatchSize = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong MinK
|
public BigInteger MinK
|
||||||
{
|
{
|
||||||
get => _minK;
|
get => _minK;
|
||||||
set => _minK = value < 1UL ? 1UL : value;
|
set => _minK = value < BigInteger.One ? BigInteger.One : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string? StateFilePath { get; set; }
|
public string? StateFilePath { get; set; }
|
||||||
@@ -49,9 +51,14 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResumeFromState(ulong lastSavedK)
|
public void ResumeFromState(BigInteger lastSavedK)
|
||||||
{
|
{
|
||||||
_minK = lastSavedK + 1UL;
|
if (lastSavedK > ulong.MaxValue)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("GPU by-divisor tester does not support MinK above UInt64.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_minK = lastSavedK + BigInteger.One;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ConfigureFromMaxPrime(ulong maxPrime)
|
public void ConfigureFromMaxPrime(ulong maxPrime)
|
||||||
@@ -68,7 +75,7 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
|
|||||||
// _isConfigured = true;
|
// _isConfigured = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsPrime(PrimeOrderCalculatorAccelerator gpu, ulong prime, out bool divisorsExhausted, out ulong divisor)
|
public bool IsPrime(PrimeOrderCalculatorAccelerator gpu, ulong prime, out bool divisorsExhausted, out BigInteger divisor)
|
||||||
{
|
{
|
||||||
ulong allowedMax;
|
ulong allowedMax;
|
||||||
int batchCapacity;
|
int batchCapacity;
|
||||||
@@ -113,7 +120,7 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
|
|||||||
gpu,
|
gpu,
|
||||||
prime,
|
prime,
|
||||||
allowedMax,
|
allowedMax,
|
||||||
_minK,
|
GetMinKOrThrow(),
|
||||||
resources.CheckDivisorKernel,
|
resources.CheckDivisorKernel,
|
||||||
resources.DivisorDataBuffer,
|
resources.DivisorDataBuffer,
|
||||||
resources.OffsetBuffer,
|
resources.OffsetBuffer,
|
||||||
@@ -144,10 +151,20 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
|
|||||||
}
|
}
|
||||||
|
|
||||||
divisorsExhausted = coveredRange;
|
divisorsExhausted = coveredRange;
|
||||||
divisor = 0UL;
|
divisor = BigInteger.Zero;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ulong GetMinKOrThrow()
|
||||||
|
{
|
||||||
|
if (_minK > ulong.MaxValue)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("GPU by-divisor tester does not support MinK above UInt64.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ulong)_minK;
|
||||||
|
}
|
||||||
|
|
||||||
public void PrepareCandidates(in ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues)
|
public void PrepareCandidates(in ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues)
|
||||||
{
|
{
|
||||||
// The scanner always supplies matching spans, so the previous validation remains commented out.
|
// The scanner always supplies matching spans, so the previous validation remains commented out.
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Numerics;
|
||||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core;
|
namespace PerfectNumbers.Core;
|
||||||
@@ -6,7 +7,7 @@ public interface IMersenneNumberDivisorByDivisorTester
|
|||||||
{
|
{
|
||||||
int BatchSize { get; set; }
|
int BatchSize { get; set; }
|
||||||
|
|
||||||
ulong MinK { get; set; }
|
BigInteger MinK { get; set; }
|
||||||
|
|
||||||
void ConfigureFromMaxPrime(ulong maxPrime);
|
void ConfigureFromMaxPrime(ulong maxPrime);
|
||||||
|
|
||||||
@@ -14,7 +15,7 @@ public interface IMersenneNumberDivisorByDivisorTester
|
|||||||
|
|
||||||
ulong GetAllowedMaxDivisor(ulong prime);
|
ulong GetAllowedMaxDivisor(ulong prime);
|
||||||
|
|
||||||
bool IsPrime(PrimeOrderCalculatorAccelerator gpu, ulong prime, out bool divisorsExhausted, out ulong divisor);
|
bool IsPrime(PrimeOrderCalculatorAccelerator gpu, ulong prime, out bool divisorsExhausted, out BigInteger divisor);
|
||||||
|
|
||||||
IDivisorScanSession CreateDivisorSession(PrimeOrderCalculatorAccelerator gpu);
|
IDivisorScanSession CreateDivisorSession(PrimeOrderCalculatorAccelerator gpu);
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ public interface IMersenneNumberDivisorByDivisorTester
|
|||||||
|
|
||||||
void ResetStateTracking();
|
void ResetStateTracking();
|
||||||
|
|
||||||
void ResumeFromState(ulong lastSavedK);
|
void ResumeFromState(BigInteger lastSavedK);
|
||||||
|
|
||||||
void PrepareCandidates(in ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues);
|
void PrepareCandidates(in ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Numerics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
@@ -13,12 +14,12 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
List<ulong> candidates,
|
List<ulong> candidates,
|
||||||
IMersenneNumberDivisorByDivisorTester tester,
|
IMersenneNumberDivisorByDivisorTester tester,
|
||||||
Dictionary<ulong, (bool DetailedCheck, bool PassedAllTests)>? previousResults,
|
Dictionary<ulong, (bool DetailedCheck, bool PassedAllTests)>? previousResults,
|
||||||
ulong startPrime,
|
ulong startPrime,
|
||||||
Action markComposite,
|
Action markComposite,
|
||||||
Action clearComposite,
|
Action clearComposite,
|
||||||
Action<ulong, bool, bool, bool, ulong> printResult,
|
Action<ulong, bool, bool, bool, BigInteger> printResult,
|
||||||
int threadCount,
|
int threadCount,
|
||||||
int primesPerTask)
|
int primesPerTask)
|
||||||
{
|
{
|
||||||
if (candidates.Count == 0)
|
if (candidates.Count == 0)
|
||||||
{
|
{
|
||||||
@@ -243,15 +244,15 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
|
|
||||||
IMersenneNumberDivisorByDivisorTester CreateTester(IMersenneNumberDivisorByDivisorTester prototype)
|
IMersenneNumberDivisorByDivisorTester CreateTester(IMersenneNumberDivisorByDivisorTester prototype)
|
||||||
{
|
{
|
||||||
IMersenneNumberDivisorByDivisorTester tester = prototype switch
|
IMersenneNumberDivisorByDivisorTester tester = prototype switch
|
||||||
{
|
{
|
||||||
MersenneNumberDivisorByDivisorCpuTester cpu => new MersenneNumberDivisorByDivisorCpuTester(cpu.OrderDevice)
|
MersenneNumberDivisorByDivisorCpuTester cpu => new MersenneNumberDivisorByDivisorCpuTester(cpu.OrderDevice)
|
||||||
{
|
{
|
||||||
BatchSize = prototype.BatchSize,
|
BatchSize = prototype.BatchSize,
|
||||||
MinK = prototype.MinK,
|
MinK = prototype.MinK,
|
||||||
},
|
},
|
||||||
_ => CreateGpuTester(prototype),
|
_ => CreateGpuTester(prototype),
|
||||||
};
|
};
|
||||||
|
|
||||||
tester.ConfigureFromMaxPrime(maxPrime);
|
tester.ConfigureFromMaxPrime(maxPrime);
|
||||||
return tester;
|
return tester;
|
||||||
@@ -275,27 +276,27 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
IMersenneNumberDivisorByDivisorTester testerForPrime = CreateTester(tester);
|
IMersenneNumberDivisorByDivisorTester testerForPrime = CreateTester(tester);
|
||||||
string stateFile = Path.Combine(PerfectNumberConstants.ByDivisorStateDirectory, prime.ToString(CultureInfo.InvariantCulture) + ".bin");
|
string stateFile = Path.Combine(PerfectNumberConstants.ByDivisorStateDirectory, prime.ToString(CultureInfo.InvariantCulture) + ".bin");
|
||||||
|
|
||||||
ulong resumeK = tester.MinK;
|
BigInteger resumeK = tester.MinK;
|
||||||
if (File.Exists(stateFile))
|
if (File.Exists(stateFile))
|
||||||
{
|
{
|
||||||
ulong lastK = File.ReadLines(stateFile)
|
BigInteger lastK = File.ReadLines(stateFile)
|
||||||
.Where(static line => !string.IsNullOrWhiteSpace(line))
|
.Where(static line => !string.IsNullOrWhiteSpace(line))
|
||||||
.Select(static line => ulong.Parse(line, NumberStyles.None, CultureInfo.InvariantCulture))
|
.Select(static line => BigInteger.Parse(line, NumberStyles.None, CultureInfo.InvariantCulture))
|
||||||
.Max();
|
.Max();
|
||||||
|
|
||||||
resumeK = lastK + 1UL;
|
resumeK = lastK + BigInteger.One;
|
||||||
testerForPrime.ResumeFromState(lastK);
|
testerForPrime.ResumeFromState(lastK);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
testerForPrime.ResumeFromState(0UL);
|
testerForPrime.ResumeFromState(BigInteger.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
testerForPrime.MinK = resumeK;
|
testerForPrime.MinK = resumeK;
|
||||||
testerForPrime.StateFilePath = stateFile;
|
testerForPrime.StateFilePath = stateFile;
|
||||||
testerForPrime.ResetStateTracking();
|
testerForPrime.ResetStateTracking();
|
||||||
|
|
||||||
bool isPrime = testerForPrime.IsPrime(gpu, prime, out bool divisorsExhausted, out ulong divisor);
|
bool isPrime = testerForPrime.IsPrime(gpu, prime, out bool divisorsExhausted, out BigInteger divisor);
|
||||||
|
|
||||||
if (!isPrime)
|
if (!isPrime)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -537,6 +537,7 @@ public static class UInt128Extensions
|
|||||||
|
|
||||||
int i = 0, primesLength = smallPrimes.Length;
|
int i = 0, primesLength = smallPrimes.Length;
|
||||||
UInt128 cycle = MersenneDivisorCycles.GetCycleCpu(q);
|
UInt128 cycle = MersenneDivisorCycles.GetCycleCpu(q);
|
||||||
|
|
||||||
// TODO: If the cache lacks this cycle, immediately schedule the configured device
|
// TODO: If the cache lacks this cycle, immediately schedule the configured device
|
||||||
// (GPU by default) to compute it on the fly and skip inserting it into the cache so
|
// (GPU by default) to compute it on the fly and skip inserting it into the cache so
|
||||||
// wide-order factoring can still leverage cycle stepping without breaching the
|
// wide-order factoring can still leverage cycle stepping without breaching the
|
||||||
|
|||||||
Reference in new issue
Block a user