TODO updated and small cleanups

This commit is contained in:
mlemanczyk committed 2025-10-18 22:31:36 +02:00
1 parent 4ac2ef1e6e
commit 98afb3ff9e
2 files changed
+367 -372

No files matched your search

+313 -314
View File
@@ -1,377 +1,376 @@
using System.Buffers.Text;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner;
// TODO: Convert this struct to an equivalent of a static class. Make all fields / properties / methods static and initialize it during program startup. Don't check if it's initialized within. Rely on the flow enforcing it.
internal struct CalculationResultHandler
{
private const string ResultsHeader = "p,searchedMersenne,detailedCheck,passedAllTests";
private const string PrimeFoundSuffix = " (FOUND VALID CANDIDATES)";
internal const int DefaultWriteBatchSize = 100;
private const string ResultsHeader = "p,searchedMersenne,detailedCheck,passedAllTests";
private const string PrimeFoundSuffix = " (FOUND VALID CANDIDATES)";
internal const int DefaultWriteBatchSize = 100;
private static readonly UTF8Encoding Utf8Encoding = new(false, true);
private static readonly UTF8Encoding Utf8Encoding = new(false, true);
private readonly object _sync;
private readonly object _fileWriteSync;
private StringBuilder? _outputBuilder;
private TextFileWriter? _resultsWriter;
private int _consoleCounter;
private int _writeIndex;
private readonly int _writeBatchSize;
private readonly string _resultsFileName;
private int _primeCount;
private bool _primeFoundAfterInit;
private readonly object _sync;
private readonly object _fileWriteSync;
private StringBuilder? _outputBuilder;
private TextFileWriter? _resultsWriter;
private int _consoleCounter;
private int _writeIndex;
private readonly int _writeBatchSize;
private readonly string _resultsFileName;
private int _primeCount;
private bool _primeFoundAfterInit;
private bool IsInitialized => _sync is not null;
private bool HasFileSync => _fileWriteSync is not null;
private readonly bool IsInitialized => _sync is not null;
private readonly bool HasFileSync => _fileWriteSync is not null;
internal CalculationResultHandler(string resultsFileName, int writeBatchSize)
{
_sync = new object();
_fileWriteSync = new object();
_outputBuilder = null;
_resultsWriter = null;
_consoleCounter = 0;
_writeIndex = 0;
_writeBatchSize = writeBatchSize > 0 ? writeBatchSize : DefaultWriteBatchSize;
_resultsFileName = resultsFileName;
_primeCount = 0;
_primeFoundAfterInit = false;
}
internal CalculationResultHandler(string resultsFileName, int writeBatchSize)
{
_sync = new object();
_fileWriteSync = new object();
_outputBuilder = null;
_resultsWriter = null;
_consoleCounter = 0;
_writeIndex = 0;
_writeBatchSize = writeBatchSize > 0 ? writeBatchSize : DefaultWriteBatchSize;
_resultsFileName = resultsFileName;
_primeCount = 0;
_primeFoundAfterInit = false;
}
internal string ResultsFileName => _resultsFileName;
internal readonly string ResultsFileName => _resultsFileName;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void InitializeOutputBuffer()
{
// Program flow ensures the handler is initialized before this call. Do not uncomment the guard below.
// if (!IsInitialized)
// {
// return;
// }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void InitializeOutputBuffer()
{
// Program flow ensures the handler is initialized before this call. Do not uncomment the guard below.
// if (!IsInitialized)
// {
// return;
// }
_outputBuilder = StringBuilderPool.Rent();
_writeIndex = 0;
}
_outputBuilder = StringBuilderPool.Rent();
_writeIndex = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ReleaseOutputBuffer()
{
// Program flow ensures the handler is initialized before this call. Do not uncomment the guard below.
// if (!IsInitialized)
// {
// return;
// }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ReleaseOutputBuffer()
{
// Program flow ensures the handler is initialized before this call. Do not uncomment the guard below.
// if (!IsInitialized)
// {
// return;
// }
StringBuilder builder = _outputBuilder!;
// Program flow guarantees the output builder is populated when releasing. Do not uncomment the guard below.
// if (builder is null)
// {
// return;
// }
StringBuilder builder = _outputBuilder!;
// Program flow guarantees the output builder is populated when releasing. Do not uncomment the guard below.
// if (builder is null)
// {
// return;
// }
_outputBuilder = null;
_writeIndex = 0;
StringBuilderPool.Return(builder);
}
_outputBuilder = null;
_writeIndex = 0;
StringBuilderPool.Return(builder);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void CreateResultsFileWithHeader()
{
// Program flow guarantees the file synchronization primitives are available here. Do not uncomment the guard below.
// if (!HasFileSync)
// {
// return;
// }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void CreateResultsFileWithHeader()
{
// Program flow guarantees the file synchronization primitives are available here. Do not uncomment the guard below.
// if (!HasFileSync)
// {
// return;
// }
lock (_fileWriteSync)
{
_resultsWriter?.Dispose();
var writer = new TextFileWriter(_resultsFileName, FileMode.Create);
writer.WriteLine(ResultsHeader.AsSpan());
_resultsWriter = writer;
}
}
lock (_fileWriteSync)
{
_resultsWriter?.Dispose();
var writer = new TextFileWriter(_resultsFileName, FileMode.Create);
writer.WriteLine(ResultsHeader.AsSpan());
_resultsWriter = writer;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void RegisterExistingResult(bool detailedCheck, bool passedAllTests)
{
if (!detailedCheck || !passedAllTests)
{
return;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void RegisterExistingResult(bool detailedCheck, bool passedAllTests)
{
if (!detailedCheck || !passedAllTests)
{
return;
}
int newCount = _primeCount + 1;
_primeCount = newCount;
if (newCount >= 2)
{
_primeFoundAfterInit = true;
}
}
int newCount = _primeCount + 1;
_primeCount = newCount;
if (newCount >= 2)
{
_primeFoundAfterInit = true;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void HandleResult(ulong currentP, bool searchedMersenne, bool detailedCheck, bool passedAllTests, bool lastWasComposite)
{
// Program flow ensures the handler is initialized before this call. Do not uncomment the guard below.
// if (!IsInitialized)
// {
// return;
// }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void HandleResult(ulong currentP, bool searchedMersenne, bool detailedCheck, bool passedAllTests, bool lastWasComposite)
{
// Program flow ensures the handler is initialized before this call. Do not uncomment the guard below.
// if (!IsInitialized)
// {
// return;
// }
Span<byte> stackBuffer = stackalloc byte[128];
byte[]? rentedBuffer = null;
Span<byte> utf8Span = stackBuffer;
int byteCount = TryFormatResultLine(utf8Span, currentP, searchedMersenne, detailedCheck, passedAllTests);
if (byteCount < 0)
{
rentedBuffer = ThreadStaticPools.BytePool.Rent(256);
utf8Span = rentedBuffer;
byteCount = TryFormatResultLine(utf8Span, currentP, searchedMersenne, detailedCheck, passedAllTests);
if (byteCount < 0)
{
ThreadStaticPools.BytePool.Return(rentedBuffer);
throw new InvalidOperationException("Result buffer too small.");
}
}
Span<byte> stackBuffer = stackalloc byte[128];
byte[]? rentedBuffer = null;
Span<byte> utf8Span = stackBuffer;
int byteCount = TryFormatResultLine(utf8Span, currentP, searchedMersenne, detailedCheck, passedAllTests);
if (byteCount < 0)
{
rentedBuffer = ThreadStaticPools.BytePool.Rent(256);
utf8Span = rentedBuffer;
byteCount = TryFormatResultLine(utf8Span, currentP, searchedMersenne, detailedCheck, passedAllTests);
if (byteCount < 0)
{
ThreadStaticPools.BytePool.Return(rentedBuffer);
throw new InvalidOperationException("Result buffer too small.");
}
}
ReadOnlySpan<byte> utf8Line = utf8Span[..byteCount];
Span<char> charBuffer = stackalloc char[128];
int charCount = Utf8Encoding.GetChars(utf8Line, charBuffer);
ReadOnlySpan<char> recordSpan = charBuffer[..charCount];
ReadOnlySpan<byte> utf8Line = utf8Span[..byteCount];
Span<char> charBuffer = stackalloc char[128];
int charCount = Utf8Encoding.GetChars(utf8Line, charBuffer);
ReadOnlySpan<char> recordSpan = charBuffer[..charCount];
bool primeCandidate = UpdatePrimeTracking(passedAllTests, lastWasComposite);
bool printToConsole = TryAcquireConsoleSlot(primeCandidate, out bool primeFlag);
StringBuilder? builderToFlush = AppendToOutputBuffer(recordSpan);
bool primeCandidate = UpdatePrimeTracking(passedAllTests, lastWasComposite);
bool printToConsole = TryAcquireConsoleSlot(primeCandidate, out bool primeFlag);
StringBuilder? builderToFlush = AppendToOutputBuffer(recordSpan);
if (builderToFlush is not null)
{
FlushBuffer(builderToFlush);
}
if (builderToFlush is not null)
{
FlushBuffer(builderToFlush);
}
if (printToConsole)
{
ReadOnlySpan<char> consoleSpan = recordSpan[..^1];
TextWriter consoleWriter = Console.Out;
if (primeFlag)
{
consoleWriter.Write(consoleSpan);
consoleWriter.Write(PrimeFoundSuffix);
consoleWriter.WriteLine();
}
else
{
consoleWriter.WriteLine(consoleSpan);
}
}
if (printToConsole)
{
ReadOnlySpan<char> consoleSpan = recordSpan[..^1];
TextWriter consoleWriter = Console.Out;
if (primeFlag)
{
consoleWriter.Write(consoleSpan);
consoleWriter.Write(PrimeFoundSuffix);
consoleWriter.WriteLine();
}
else
{
consoleWriter.WriteLine(consoleSpan);
}
}
if (rentedBuffer is not null)
{
ThreadStaticPools.BytePool.Return(rentedBuffer);
}
}
if (rentedBuffer is not null)
{
ThreadStaticPools.BytePool.Return(rentedBuffer);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void FlushBuffer()
{
// Program flow ensures the handler is initialized before this call. Do not uncomment the guard below.
// if (!IsInitialized)
// {
// return;
// }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void FlushBuffer()
{
// Program flow ensures the handler is initialized before this call. Do not uncomment the guard below.
// if (!IsInitialized)
// {
// return;
// }
StringBuilder? builderToFlush;
StringBuilder? builderToFlush;
lock (_sync)
{
if (_writeIndex == 0 || _outputBuilder is null || _outputBuilder.Length == 0)
{
return;
}
lock (_sync)
{
if (_writeIndex == 0 || _outputBuilder is null || _outputBuilder.Length == 0)
{
return;
}
builderToFlush = _outputBuilder;
_outputBuilder = StringBuilderPool.Rent();
_writeIndex = 0;
}
builderToFlush = _outputBuilder;
_outputBuilder = StringBuilderPool.Rent();
_writeIndex = 0;
}
FlushBuffer(builderToFlush!);
}
FlushBuffer(builderToFlush!);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Dispose()
{
// Program flow guarantees the file synchronization primitives are available here. Do not uncomment the guard below.
// if (!HasFileSync)
// {
// return;
// }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Dispose()
{
// Program flow guarantees the file synchronization primitives are available here. Do not uncomment the guard below.
// if (!HasFileSync)
// {
// return;
// }
lock (_fileWriteSync)
{
_resultsWriter?.Dispose();
_resultsWriter = null;
}
}
lock (_fileWriteSync)
{
_resultsWriter?.Dispose();
_resultsWriter = null;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool TryAcquireConsoleSlot(bool primeCandidate, out bool primeFlag)
{
int consoleInterval = PerfectNumberConstants.ConsoleInterval;
int counterValue = Interlocked.Increment(ref _consoleCounter);
if (counterValue >= consoleInterval)
{
int previous = Interlocked.Exchange(ref _consoleCounter, 0);
if (previous >= consoleInterval)
{
primeFlag = primeCandidate;
return true;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool TryAcquireConsoleSlot(bool primeCandidate, out bool primeFlag)
{
int consoleInterval = PerfectNumberConstants.ConsoleInterval;
int counterValue = Interlocked.Increment(ref _consoleCounter);
if (counterValue >= consoleInterval)
{
int previous = Interlocked.Exchange(ref _consoleCounter, 0);
if (previous >= consoleInterval)
{
primeFlag = primeCandidate;
return true;
}
}
primeFlag = false;
return false;
}
primeFlag = false;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private StringBuilder? AppendToOutputBuffer(ReadOnlySpan<char> text)
{
StringBuilder? builderToFlush = null;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private StringBuilder? AppendToOutputBuffer(ReadOnlySpan<char> text)
{
StringBuilder? builderToFlush = null;
lock (_sync)
{
StringBuilder outputBuilder = _outputBuilder!;
_ = outputBuilder.Append(text);
lock (_sync)
{
StringBuilder outputBuilder = _outputBuilder!;
_ = outputBuilder.Append(text);
int nextIndex = _writeIndex + 1;
if (nextIndex >= _writeBatchSize)
{
builderToFlush = outputBuilder;
_outputBuilder = StringBuilderPool.Rent();
_writeIndex = 0;
}
else
{
_writeIndex = nextIndex;
}
}
int nextIndex = _writeIndex + 1;
if (nextIndex >= _writeBatchSize)
{
builderToFlush = outputBuilder;
_outputBuilder = StringBuilderPool.Rent();
_writeIndex = 0;
}
else
{
_writeIndex = nextIndex;
}
}
return builderToFlush;
}
return builderToFlush;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int TryFormatResultLine(Span<byte> destination, ulong currentP, bool searchedMersenne, bool detailedCheck, bool passedAllTests)
{
int offset = 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private readonly int TryFormatResultLine(Span<byte> destination, ulong currentP, bool searchedMersenne, bool detailedCheck, bool passedAllTests)
{
int offset = 0;
if (!Utf8Formatter.TryFormat(currentP, destination[offset..], out int written))
{
return -1;
}
if (!Utf8Formatter.TryFormat(currentP, destination[offset..], out int written))
{
return -1;
}
offset += written;
if (offset >= destination.Length)
{
return -1;
}
offset += written;
if (offset >= destination.Length)
{
return -1;
}
destination[offset++] = (byte)',';
destination[offset++] = (byte)',';
if (!Utf8Formatter.TryFormat(searchedMersenne, destination[offset..], out written))
{
return -1;
}
if (!Utf8Formatter.TryFormat(searchedMersenne, destination[offset..], out written))
{
return -1;
}
offset += written;
if (offset >= destination.Length)
{
return -1;
}
offset += written;
if (offset >= destination.Length)
{
return -1;
}
destination[offset++] = (byte)',';
destination[offset++] = (byte)',';
if (!Utf8Formatter.TryFormat(detailedCheck, destination[offset..], out written))
{
return -1;
}
if (!Utf8Formatter.TryFormat(detailedCheck, destination[offset..], out written))
{
return -1;
}
offset += written;
if (offset >= destination.Length)
{
return -1;
}
offset += written;
if (offset >= destination.Length)
{
return -1;
}
destination[offset++] = (byte)',';
destination[offset++] = (byte)',';
if (!Utf8Formatter.TryFormat(passedAllTests, destination[offset..], out written))
{
return -1;
}
if (!Utf8Formatter.TryFormat(passedAllTests, destination[offset..], out written))
{
return -1;
}
offset += written;
if (offset >= destination.Length)
{
return -1;
}
offset += written;
if (offset >= destination.Length)
{
return -1;
}
destination[offset++] = (byte)'\n';
return offset;
}
destination[offset++] = (byte)'\n';
return offset;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void FlushBuffer(StringBuilder builderToFlush)
{
// Program flow guarantees the file synchronization primitives are available here. Do not uncomment the guard below.
// if (!HasFileSync)
// {
// builderToFlush.Clear();
// StringBuilderPool.Return(builderToFlush);
// return;
// }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void FlushBuffer(StringBuilder builderToFlush)
{
// Program flow guarantees the file synchronization primitives are available here. Do not uncomment the guard below.
// if (!HasFileSync)
// {
// builderToFlush.Clear();
// StringBuilderPool.Return(builderToFlush);
// return;
// }
try
{
lock (_fileWriteSync)
{
TextFileWriter writer = EnsureResultsWriter();
writer.Write(builderToFlush);
}
}
finally
{
builderToFlush.Clear();
StringBuilderPool.Return(builderToFlush);
}
}
try
{
lock (_fileWriteSync)
{
TextFileWriter writer = EnsureResultsWriter();
writer.Write(builderToFlush);
}
}
finally
{
builderToFlush.Clear();
StringBuilderPool.Return(builderToFlush);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private TextFileWriter EnsureResultsWriter()
{
TextFileWriter? writer = _resultsWriter;
if (writer is not null)
{
return writer;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private TextFileWriter EnsureResultsWriter()
{
TextFileWriter? writer = _resultsWriter;
if (writer is not null)
{
return writer;
}
writer = new TextFileWriter(_resultsFileName, FileMode.Append);
_resultsWriter = writer;
return writer;
}
writer = new TextFileWriter(_resultsFileName, FileMode.Append);
_resultsWriter = writer;
return writer;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool UpdatePrimeTracking(bool passedAllTests, bool lastWasComposite)
{
if (passedAllTests)
{
int newCount = Interlocked.Increment(ref _primeCount);
if (newCount >= 2)
{
Volatile.Write(ref _primeFoundAfterInit, true);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool UpdatePrimeTracking(bool passedAllTests, bool lastWasComposite)
{
if (passedAllTests)
{
int newCount = Interlocked.Increment(ref _primeCount);
if (newCount >= 2)
{
Volatile.Write(ref _primeFoundAfterInit, true);
}
}
return passedAllTests && !lastWasComposite && Volatile.Read(ref _primeFoundAfterInit);
}
return passedAllTests && !lastWasComposite && Volatile.Read(ref _primeFoundAfterInit);
}
}
+54 -58
View File
@@ -276,7 +276,7 @@ internal static class Program
orderWarmupLimitOverride ?? 5_000_000UL,
NttGpuMath.ReductionMode,
mersenneOnGpu ? "gpu" : "cpu",
(GpuContextPool.ForceCpu ? "cpu" : "gpu"),
GpuContextPool.ForceCpu ? "cpu" : "gpu",
orderOnGpu ? "gpu" : "cpu");
if (!string.IsNullOrEmpty(_cliArguments.ResultsPrefix))
@@ -418,6 +418,7 @@ internal static class Program
GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads);
// Configure batch size for GPU primality sieve
PrimeTester.GpuBatchSize = gpuPrimeBatch;
CalculationResultHandler resultHandler = _resultHandler;
Stopwatch? stopwatch = null;
if (testMode)
@@ -436,7 +437,7 @@ internal static class Program
_byDivisorStartPrime,
static () => _lastCompositeP = true,
static () => _lastCompositeP = false,
PrintResult,
static (candidate, searchedMersenne, detailedCheck, passedAllTests) => _resultHandler.HandleResult(candidate, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP),
threadCount);
_resultHandler.FlushBuffer();
_resultHandler.ReleaseOutputBuffer();
@@ -494,7 +495,7 @@ internal static class Program
{
p = buffer[j];
passedAllTests = IsEvenPerfectCandidate(p, divisorCyclesSearchLimit, out searchedMersenne, out detailedCheck);
PrintResult(p, searchedMersenne, detailedCheck, passedAllTests);
resultHandler.HandleResult(p, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP);
}
continue;
@@ -517,7 +518,7 @@ internal static class Program
}
passedAllTests = IsEvenPerfectCandidate(p, divisorCyclesSearchLimit, out searchedMersenne, out detailedCheck);
PrintResult(p, searchedMersenne, detailedCheck, passedAllTests);
resultHandler.HandleResult(p, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP);
if (p == maxP)
{
@@ -715,47 +716,47 @@ internal static class Program
}
private static void PrintHelp()
{
Console.WriteLine("Usage: EvenPerfectBitScanner [options]");
Console.WriteLine();
Console.WriteLine("Options:");
Console.WriteLine(" --prime=<value> starting exponent (p)");
Console.WriteLine(" --max-prime=<value> inclusive upper bound for primes from filter files");
Console.WriteLine(" --increment=bit|add exponent increment method");
Console.WriteLine(" --threads=<value> number of worker threads");
Console.WriteLine(" --block-size=<value> values processed per thread batch");
Console.WriteLine(" --mersenne=pow2mod|incremental|lucas|residue|divisor|bydivisor Mersenne test method");
Console.WriteLine(" --residue-max-k=<value> max k for residue Mersenne test (q = 2*p*k + 1)");
Console.WriteLine(" --mersenne-device=cpu|gpu Device for Mersenne method (default gpu)");
Console.WriteLine(" --primes-device=cpu|gpu Device for prime-scan kernels (default gpu)");
Console.WriteLine(" --gpu-prime-batch=<n> Batch size for GPU primality sieve (default 262144)");
Console.WriteLine(" --order-device=cpu|gpu Device for order computations (default gpu)");
Console.WriteLine(" --ntt=reference|staged GPU NTT backend (default staged)");
Console.WriteLine(" --mod-reduction=auto|uint128|mont64|barrett128 staged NTT reduction (default auto)");
Console.WriteLine(" --gpu-prime-threads=<value> max concurrent GPU prime checks (default 1)");
Console.WriteLine(" --ll-slice=<value> Lucas–Lehmer iterations per slice (default 32)");
Console.WriteLine(" --gpu-scan-batch=<value> GPU q-scan batch size (default 2_097_152)");
Console.WriteLine(" --order-warmup-limit=<value> Warm-up order candidates (default 5_000_000)");
Console.WriteLine(" --rle-blacklist=<path> enable RLE blacklist for p (hard filter up to --rle-hard-max)");
Console.WriteLine(" --rle-hard-max=<p> apply RLE blacklist only for p <= this (default ulong.MaxValue = no limit)");
Console.WriteLine(" --rle-only-last7=true|false apply RLE only when p % 10 == 7 (default true)");
Console.WriteLine(" --zero-hard=<f> hard reject if zero_fraction(p) > f (default off)");
Console.WriteLine(" --zero-conj=<f>:<r> hard reject if zero_fraction(p) > f AND max_zero_block >= r (default off)");
Console.WriteLine(" --results-dir=<path> directory for results file");
Console.WriteLine(" --results-prefix=<text> prefix to prepend to results filename");
Console.WriteLine(" --divisor-cycles=<path> divisor cycles data file");
Console.WriteLine(" --divisor-cycles-device=cpu|gpu device for cycles generation (default gpu)");
Console.WriteLine(" --divisor-cycles-batch=<value> batch size for cycles generation (default 512)");
Console.WriteLine(" --divisor-cycles-continue continue divisor cycles generation");
Console.WriteLine(" --divisor-cycles-limit=<value> cycle search iterations when --mersenne=divisor");
Console.WriteLine(" --use-order test primality via q order");
// mod-automaton removed
Console.WriteLine(" --filter-p=<path> process only p from previous run results (required for --mersenne=bydivisor)");
Console.WriteLine(" --write-batch-size=<value> overwrite frequency of disk writes (default 100 lines)");
Console.WriteLine(" --gcd-filter enable early sieve based on GCD");
Console.WriteLine(" --help, -help, --?, -?, /? show this help message");
}
// TODO: Move this to CliArguments
private static void PrintHelp()
{
Console.WriteLine("Usage: EvenPerfectBitScanner [options]");
Console.WriteLine();
Console.WriteLine("Options:");
Console.WriteLine(" --prime=<value> starting exponent (p)");
Console.WriteLine(" --max-prime=<value> inclusive upper bound for primes from filter files");
Console.WriteLine(" --increment=bit|add exponent increment method");
Console.WriteLine(" --threads=<value> number of worker threads");
Console.WriteLine(" --block-size=<value> values processed per thread batch");
Console.WriteLine(" --mersenne=pow2mod|incremental|lucas|residue|divisor|bydivisor Mersenne test method");
Console.WriteLine(" --residue-max-k=<value> max k for residue Mersenne test (q = 2*p*k + 1)");
Console.WriteLine(" --mersenne-device=cpu|gpu Device for Mersenne method (default gpu)");
Console.WriteLine(" --primes-device=cpu|gpu Device for prime-scan kernels (default gpu)");
Console.WriteLine(" --gpu-prime-batch=<n> Batch size for GPU primality sieve (default 262144)");
Console.WriteLine(" --order-device=cpu|gpu Device for order computations (default gpu)");
Console.WriteLine(" --ntt=reference|staged GPU NTT backend (default staged)");
Console.WriteLine(" --mod-reduction=auto|uint128|mont64|barrett128 staged NTT reduction (default auto)");
Console.WriteLine(" --gpu-prime-threads=<value> max concurrent GPU prime checks (default 1)");
Console.WriteLine(" --ll-slice=<value> Lucas–Lehmer iterations per slice (default 32)");
Console.WriteLine(" --gpu-scan-batch=<value> GPU q-scan batch size (default 2_097_152)");
Console.WriteLine(" --order-warmup-limit=<value> Warm-up order candidates (default 5_000_000)");
Console.WriteLine(" --rle-blacklist=<path> enable RLE blacklist for p (hard filter up to --rle-hard-max)");
Console.WriteLine(" --rle-hard-max=<p> apply RLE blacklist only for p <= this (default ulong.MaxValue = no limit)");
Console.WriteLine(" --rle-only-last7=true|false apply RLE only when p % 10 == 7 (default true)");
Console.WriteLine(" --zero-hard=<f> hard reject if zero_fraction(p) > f (default off)");
Console.WriteLine(" --zero-conj=<f>:<r> hard reject if zero_fraction(p) > f AND max_zero_block >= r (default off)");
Console.WriteLine(" --results-dir=<path> directory for results file");
Console.WriteLine(" --results-prefix=<text> prefix to prepend to results filename");
Console.WriteLine(" --divisor-cycles=<path> divisor cycles data file");
Console.WriteLine(" --divisor-cycles-device=cpu|gpu device for cycles generation (default gpu)");
Console.WriteLine(" --divisor-cycles-batch=<value> batch size for cycles generation (default 512)");
Console.WriteLine(" --divisor-cycles-continue continue divisor cycles generation");
Console.WriteLine(" --divisor-cycles-limit=<value> cycle search iterations when --mersenne=divisor");
Console.WriteLine(" --use-order test primality via q order");
Console.WriteLine(" --filter-p=<path> process only p from previous run results (required for --mersenne=bydivisor)");
Console.WriteLine(" --write-batch-size=<value> overwrite frequency of disk writes (default 100 lines)");
Console.WriteLine(" --gcd-filter enable early sieve based on GCD");
Console.WriteLine(" --help, -help, --?, -?, /? show this help message");
}
private static string BuildResultsFileName(bool bitInc, int threads, int block, GpuKernelType kernelType, bool useLucasFlag, bool useDivisorFlag, bool useByDivisorFlag, bool mersenneOnGpu, bool useOrder, bool useGcd, NttBackend nttBackend, int gpuPrimeThreads, int llSlice, int gpuScanBatch, ulong warmupLimit, ModReductionMode reduction, string mersenneDevice, string primesDevice, string orderDevice)
{
@@ -895,16 +896,6 @@ internal static class Program
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void PrintResult(ulong currentP, bool searchedMersenne, bool detailedCheck, bool passedAllTests)
{
bool lastWasComposite = _lastCompositeP;
_resultHandler.HandleResult(currentP, searchedMersenne, detailedCheck, passedAllTests, lastWasComposite);
}
private static void ReportTestTime(TimeSpan elapsed)
{
double elapsedSeconds = elapsed.TotalSeconds;
@@ -1242,6 +1233,7 @@ internal static class Program
return false;
}
// TODO: Convert this to extension method in ULongExtensions if one doesn't already exist. We may already calculate things like these
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ComputeBitStats(ulong value, out int bitLen, out int zeroCount, out int maxZeroBlock)
{
@@ -1403,9 +1395,11 @@ internal static class Program
}
}
// TODO: Convert this to extension method in ULongExtensions if one doesn't already exist
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool IsCompositeByGcd(ulong p)
{
{
// TODO: Can this ever be true on Program's execution path?
if (p < 2)
{
return true;
@@ -1415,9 +1409,11 @@ internal static class Program
return BinaryGcd(p, m) > 1UL;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
// TODO: Convert this to extension method in ULongExtensions if one doesn't already exist
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong BinaryGcd(ulong a, ulong b)
{
{
// TODO: Are these a & b conditions ever met on Program's execution path?
if (a == 0UL)
{
return b;