From a39f7dda59e0fbd31d487b031ab031872603f77c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Wed, 24 Sep 2025 16:30:17 +0200 Subject: [PATCH] More TODOs added --- EvenPerfectBitScanner/Program.cs | 2279 +++++++++-------- ...MersenneNumberDivisorByDivisorGpuTester.cs | 1 + 2 files changed, 1141 insertions(+), 1139 deletions(-) diff --git a/EvenPerfectBitScanner/Program.cs b/EvenPerfectBitScanner/Program.cs index 43b0526..5006aa5 100644 --- a/EvenPerfectBitScanner/Program.cs +++ b/EvenPerfectBitScanner/Program.cs @@ -21,8 +21,8 @@ internal static class Program private static string ResultsFileName = "even_perfect_bit_scan_results.csv"; private const string PrimeFoundSuffix = " (FOUND VALID CANDIDATES)"; private static StringBuilder? _outputBuilder; - private static readonly object Sync = new(); - private static readonly object FileWriteSync = new(); + private static readonly object Sync = new(); + private static readonly object FileWriteSync = new(); private static int _consoleCounter; private static int _writeIndex; private static int _primeCount; @@ -56,18 +56,18 @@ internal static class Program private static double _zeroFracConj = -1.0; // disabled when < 0 private static int _maxZeroConj = -1; // disabled when < 0 - private const int ByDivisorStateActive = 0; - private const int ByDivisorStateComposite = 1; - private const int ByDivisorStateCompleted = 2; - private const int ByDivisorStateCompletedDetailed = 3; - private const int DivisorAllocationBlockSize = 64; + private const int ByDivisorStateActive = 0; + private const int ByDivisorStateComposite = 1; + private const int ByDivisorStateCompleted = 2; + private const int ByDivisorStateCompletedDetailed = 3; + private const int DivisorAllocationBlockSize = 64; - private struct ByDivisorPrimeState - { - internal ulong Prime; - internal ulong AllowedMax; - internal bool Completed; - internal bool Composite; + private struct ByDivisorPrimeState + { + internal ulong Prime; + internal ulong AllowedMax; + internal bool Completed; + internal bool Composite; internal bool DetailedCheck; } @@ -125,19 +125,19 @@ internal static class Program bool startPrimeProvided = false; // NTT backend selection (GPU): reference vs staged - for (; argIndex < args.Length; argIndex++) - { - arg = args[argIndex]; - if (arg.Equals("--?", StringComparison.OrdinalIgnoreCase) || arg.Equals("-?", StringComparison.OrdinalIgnoreCase) || arg.Equals("--help", StringComparison.OrdinalIgnoreCase) || arg.Equals("-help", StringComparison.OrdinalIgnoreCase) || arg.Equals("/?", StringComparison.OrdinalIgnoreCase)) - { - showHelp = true; - break; - } - else if (arg.StartsWith("--prime=", StringComparison.OrdinalIgnoreCase)) - { - currentP = ulong.Parse(arg.AsSpan(arg.IndexOf('=') + 1)); - startPrimeProvided = true; - } + for (; argIndex < args.Length; argIndex++) + { + arg = args[argIndex]; + if (arg.Equals("--?", StringComparison.OrdinalIgnoreCase) || arg.Equals("-?", StringComparison.OrdinalIgnoreCase) || arg.Equals("--help", StringComparison.OrdinalIgnoreCase) || arg.Equals("-help", StringComparison.OrdinalIgnoreCase) || arg.Equals("/?", StringComparison.OrdinalIgnoreCase)) + { + showHelp = true; + break; + } + else if (arg.StartsWith("--prime=", StringComparison.OrdinalIgnoreCase)) + { + currentP = ulong.Parse(arg.AsSpan(arg.IndexOf('=') + 1)); + startPrimeProvided = true; + } else if (arg.Equals("--increment=bit", StringComparison.OrdinalIgnoreCase)) { useBitTransform = true; @@ -146,75 +146,75 @@ internal static class Program { useBitTransform = false; } - else if (arg.StartsWith("--threads=", StringComparison.OrdinalIgnoreCase)) - { - threadCount = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1))); - } - else if (arg.StartsWith("--mersenne=", StringComparison.OrdinalIgnoreCase)) - { - mersenneOption = arg.AsSpan(arg.IndexOf('=') + 1); - if (mersenneOption.Equals("pow2mod", StringComparison.OrdinalIgnoreCase)) - { - kernelType = GpuKernelType.Pow2Mod; - } - else if (mersenneOption.Equals("lucas", StringComparison.OrdinalIgnoreCase)) - { - useLucas = true; - } - else if (mersenneOption.Equals("residue", StringComparison.OrdinalIgnoreCase)) - { - useResidue = true; - useLucas = false; - } - else if (mersenneOption.Equals("divisor", StringComparison.OrdinalIgnoreCase)) - { - useDivisor = true; - useLucas = false; - useResidue = false; - } - else if (mersenneOption.Equals("bydivisor", StringComparison.OrdinalIgnoreCase)) - { - useByDivisor = true; - useLucas = false; - useResidue = false; - useDivisor = false; + else if (arg.StartsWith("--threads=", StringComparison.OrdinalIgnoreCase)) + { + threadCount = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1))); + } + else if (arg.StartsWith("--mersenne=", StringComparison.OrdinalIgnoreCase)) + { + mersenneOption = arg.AsSpan(arg.IndexOf('=') + 1); + if (mersenneOption.Equals("pow2mod", StringComparison.OrdinalIgnoreCase)) + { + kernelType = GpuKernelType.Pow2Mod; + } + else if (mersenneOption.Equals("lucas", StringComparison.OrdinalIgnoreCase)) + { + useLucas = true; + } + else if (mersenneOption.Equals("residue", StringComparison.OrdinalIgnoreCase)) + { + useResidue = true; + useLucas = false; + } + else if (mersenneOption.Equals("divisor", StringComparison.OrdinalIgnoreCase)) + { + useDivisor = true; + useLucas = false; + useResidue = false; + } + else if (mersenneOption.Equals("bydivisor", StringComparison.OrdinalIgnoreCase)) + { + useByDivisor = true; + useLucas = false; + useResidue = false; + useDivisor = false; } else { kernelType = GpuKernelType.Incremental; } } - else if (arg.StartsWith("--divisor=", StringComparison.OrdinalIgnoreCase)) - { - divisor = UInt128.Parse(arg.AsSpan(arg.IndexOf('=') + 1)); - } - else if (arg.StartsWith("--use-divisor-cycles=", StringComparison.OrdinalIgnoreCase)) - { - if (bool.TryParse(arg.AsSpan(arg.IndexOf('=') + 1), out bool parsedBool)) - { - useDivisorCycles = parsedBool; - } - } - else if (arg.StartsWith("--divisor-cycles-limit=", StringComparison.OrdinalIgnoreCase)) - { - if (ulong.TryParse(arg.AsSpan(arg.IndexOf('=') + 1), out parsedLimit)) - { - divisorCyclesSearchLimit = parsedLimit; - } - } - else if (arg.StartsWith("--residue-max-k=", StringComparison.OrdinalIgnoreCase)) - { - if (ulong.TryParse(arg.AsSpan(arg.IndexOf('=') + 1), out parsedResidueMax)) - { - residueKMax = parsedResidueMax; - } - } + else if (arg.StartsWith("--divisor=", StringComparison.OrdinalIgnoreCase)) + { + divisor = UInt128.Parse(arg.AsSpan(arg.IndexOf('=') + 1)); + } + else if (arg.StartsWith("--use-divisor-cycles=", StringComparison.OrdinalIgnoreCase)) + { + if (bool.TryParse(arg.AsSpan(arg.IndexOf('=') + 1), out bool parsedBool)) + { + useDivisorCycles = parsedBool; + } + } + else if (arg.StartsWith("--divisor-cycles-limit=", StringComparison.OrdinalIgnoreCase)) + { + if (ulong.TryParse(arg.AsSpan(arg.IndexOf('=') + 1), out parsedLimit)) + { + divisorCyclesSearchLimit = parsedLimit; + } + } + else if (arg.StartsWith("--residue-max-k=", StringComparison.OrdinalIgnoreCase)) + { + if (ulong.TryParse(arg.AsSpan(arg.IndexOf('=') + 1), out parsedResidueMax)) + { + residueKMax = parsedResidueMax; + } + } // Replaces --lucas=cpu|gpu; controls device for Lucas and for // incremental/pow2mod scanning - else if (arg.StartsWith("--mersenne-device=", StringComparison.OrdinalIgnoreCase)) - { - mersenneOnGpu = !arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase); - } + else if (arg.StartsWith("--mersenne-device=", StringComparison.OrdinalIgnoreCase)) + { + mersenneOnGpu = !arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase); + } else if (arg.Equals("--workaround-mod", StringComparison.OrdinalIgnoreCase)) { useModuloWorkaround = true; @@ -223,37 +223,37 @@ internal static class Program { useOrder = true; } - else if (arg.StartsWith("--order-warmup-limit=", StringComparison.OrdinalIgnoreCase)) - { - // stored and used below when initializing testers - // Reusing parsedLimit to capture the --order-warmup-limit argument. - if (ulong.TryParse(arg.AsSpan(arg.IndexOf('=') + 1), out parsedLimit)) - { - _orderWarmupLimitOverride = parsedLimit; - } - } + else if (arg.StartsWith("--order-warmup-limit=", StringComparison.OrdinalIgnoreCase)) + { + // stored and used below when initializing testers + // Reusing parsedLimit to capture the --order-warmup-limit argument. + if (ulong.TryParse(arg.AsSpan(arg.IndexOf('=') + 1), out parsedLimit)) + { + _orderWarmupLimitOverride = parsedLimit; + } + } else if (arg.Equals("--gcd-filter", StringComparison.OrdinalIgnoreCase)) { _useGcdFilter = true; } - else if (arg.StartsWith("--ntt=", StringComparison.OrdinalIgnoreCase)) - { - ReadOnlySpan value = arg.AsSpan(arg.IndexOf('=') + 1); - if (value.Equals("staged", StringComparison.OrdinalIgnoreCase)) - { - NttGpuMath.GpuTransformBackend = NttBackend.Staged; + else if (arg.StartsWith("--ntt=", StringComparison.OrdinalIgnoreCase)) + { + ReadOnlySpan value = arg.AsSpan(arg.IndexOf('=') + 1); + if (value.Equals("staged", StringComparison.OrdinalIgnoreCase)) + { + NttGpuMath.GpuTransformBackend = NttBackend.Staged; } else { NttGpuMath.GpuTransformBackend = NttBackend.Reference; } } - else if (arg.StartsWith("--mod-reduction=", StringComparison.OrdinalIgnoreCase)) - { - ReadOnlySpan value = arg.AsSpan(arg.IndexOf('=') + 1); - if (value.Equals("mont64", StringComparison.OrdinalIgnoreCase)) - { - NttGpuMath.ReductionMode = ModReductionMode.Mont64; + else if (arg.StartsWith("--mod-reduction=", StringComparison.OrdinalIgnoreCase)) + { + ReadOnlySpan value = arg.AsSpan(arg.IndexOf('=') + 1); + if (value.Equals("mont64", StringComparison.OrdinalIgnoreCase)) + { + NttGpuMath.ReductionMode = ModReductionMode.Mont64; } else if (value.Equals("barrett128", StringComparison.OrdinalIgnoreCase)) { @@ -268,127 +268,127 @@ internal static class Program NttGpuMath.ReductionMode = ModReductionMode.Auto; } } - else if (arg.StartsWith("--primes-device=", StringComparison.OrdinalIgnoreCase)) - { - // Controls default device for library prime-related GPU kernels - // that are not explicitly parameterized (backward compatibility). - GpuContextPool.ForceCpu = arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase); - } + else if (arg.StartsWith("--primes-device=", StringComparison.OrdinalIgnoreCase)) + { + // Controls default device for library prime-related GPU kernels + // that are not explicitly parameterized (backward compatibility). + GpuContextPool.ForceCpu = arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase); + } // New: choose device for order computations (warm-ups and order scans) - else if (arg.StartsWith("--order-device=", StringComparison.OrdinalIgnoreCase)) - { - orderOnGpu = !arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase); - } + else if (arg.StartsWith("--order-device=", StringComparison.OrdinalIgnoreCase)) + { + orderOnGpu = !arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase); + } // Optional: RLE blacklist for p - else if (arg.StartsWith("--rle-blacklist=", StringComparison.OrdinalIgnoreCase)) - { - _rleBlacklistPath = arg[(arg.IndexOf('=') + 1)..]; - } - else if (arg.StartsWith("--rle-hard-max=", StringComparison.OrdinalIgnoreCase)) - { - ulong rleMaxP; - if (ulong.TryParse(arg.AsSpan(arg.IndexOf('=') + 1), out rleMaxP)) - { - _rleHardMaxP = rleMaxP; - } - } - else if (arg.StartsWith("--rle-only-last7=", StringComparison.OrdinalIgnoreCase)) - { - ReadOnlySpan value = arg.AsSpan(arg.IndexOf('=') + 1); - _rleOnlyLast7 = !value.Equals("false", StringComparison.OrdinalIgnoreCase) && !value.Equals("0", StringComparison.OrdinalIgnoreCase); - } - else if (arg.StartsWith("--zero-hard=", StringComparison.OrdinalIgnoreCase)) - { - double zeroFraction; - if (double.TryParse(arg.AsSpan(arg.IndexOf('=') + 1), out zeroFraction)) - { - _zeroFracHard = zeroFraction; - } - } - else if (arg.StartsWith("--zero-conj=", StringComparison.OrdinalIgnoreCase)) - { - // format: : - ReadOnlySpan value = arg.AsSpan(arg.IndexOf('=') + 1); - int colon = value.IndexOf(':'); - if (colon > 0) - { - double zeroFrac; - int maxZero; - if (double.TryParse(value[..colon], out zeroFrac) && int.TryParse(value[(colon + 1)..], out maxZero)) - { - _zeroFracConj = zeroFrac; - _maxZeroConj = maxZero; - } - } + else if (arg.StartsWith("--rle-blacklist=", StringComparison.OrdinalIgnoreCase)) + { + _rleBlacklistPath = arg[(arg.IndexOf('=') + 1)..]; + } + else if (arg.StartsWith("--rle-hard-max=", StringComparison.OrdinalIgnoreCase)) + { + ulong rleMaxP; + if (ulong.TryParse(arg.AsSpan(arg.IndexOf('=') + 1), out rleMaxP)) + { + _rleHardMaxP = rleMaxP; + } + } + else if (arg.StartsWith("--rle-only-last7=", StringComparison.OrdinalIgnoreCase)) + { + ReadOnlySpan value = arg.AsSpan(arg.IndexOf('=') + 1); + _rleOnlyLast7 = !value.Equals("false", StringComparison.OrdinalIgnoreCase) && !value.Equals("0", StringComparison.OrdinalIgnoreCase); + } + else if (arg.StartsWith("--zero-hard=", StringComparison.OrdinalIgnoreCase)) + { + double zeroFraction; + if (double.TryParse(arg.AsSpan(arg.IndexOf('=') + 1), out zeroFraction)) + { + _zeroFracHard = zeroFraction; + } + } + else if (arg.StartsWith("--zero-conj=", StringComparison.OrdinalIgnoreCase)) + { + // format: : + ReadOnlySpan value = arg.AsSpan(arg.IndexOf('=') + 1); + int colon = value.IndexOf(':'); + if (colon > 0) + { + double zeroFrac; + int maxZero; + if (double.TryParse(value[..colon], out zeroFrac) && int.TryParse(value[(colon + 1)..], out maxZero)) + { + _zeroFracConj = zeroFrac; + _maxZeroConj = maxZero; + } + } } // Backward-compat: accept deprecated device flags and map to new ones - else if (arg.StartsWith("--lucas=", StringComparison.OrdinalIgnoreCase)) - { - mersenneOnGpu = !arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase); - } - else if (arg.StartsWith("--primes=", StringComparison.OrdinalIgnoreCase)) - { - GpuContextPool.ForceCpu = arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase); - } - else if (arg.StartsWith("--gpu-kernels=", StringComparison.OrdinalIgnoreCase) || arg.StartsWith("--accelerator=", StringComparison.OrdinalIgnoreCase)) - { - // Legacy alias: treat as primes-device for backward compat - GpuContextPool.ForceCpu = arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase); - } - else if (arg.StartsWith("--results-dir=", StringComparison.OrdinalIgnoreCase)) - { - _resultsDir = arg[(arg.IndexOf('=') + 1)..]; - } - else if (arg.StartsWith("--results-prefix=", StringComparison.OrdinalIgnoreCase)) - { - _resultsPrefix = arg[(arg.IndexOf('=') + 1)..]; - } + else if (arg.StartsWith("--lucas=", StringComparison.OrdinalIgnoreCase)) + { + mersenneOnGpu = !arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase); + } + else if (arg.StartsWith("--primes=", StringComparison.OrdinalIgnoreCase)) + { + GpuContextPool.ForceCpu = arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase); + } + else if (arg.StartsWith("--gpu-kernels=", StringComparison.OrdinalIgnoreCase) || arg.StartsWith("--accelerator=", StringComparison.OrdinalIgnoreCase)) + { + // Legacy alias: treat as primes-device for backward compat + GpuContextPool.ForceCpu = arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase); + } + else if (arg.StartsWith("--results-dir=", StringComparison.OrdinalIgnoreCase)) + { + _resultsDir = arg[(arg.IndexOf('=') + 1)..]; + } + else if (arg.StartsWith("--results-prefix=", StringComparison.OrdinalIgnoreCase)) + { + _resultsPrefix = arg[(arg.IndexOf('=') + 1)..]; + } - else if (arg.StartsWith("--gpu-prime-threads=", StringComparison.OrdinalIgnoreCase)) - { - gpuPrimeThreads = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1))); - } - else if (arg.StartsWith("--gpu-prime-batch=", StringComparison.OrdinalIgnoreCase)) - { - gpuPrimeBatch = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1))); - } - else if (arg.StartsWith("--ll-slice=", StringComparison.OrdinalIgnoreCase)) - { - sliceSize = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1))); - } - else if (arg.StartsWith("--gpu-scan-batch=", StringComparison.OrdinalIgnoreCase)) - { - scanBatchSize = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1))); - } - else if (arg.StartsWith("--block-size=", StringComparison.OrdinalIgnoreCase)) - { - blockSize = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1))); - } - else if (arg.StartsWith("--filter-p=", StringComparison.OrdinalIgnoreCase)) - { - filterFile = arg[(arg.IndexOf('=') + 1)..]; - } - else if (arg.StartsWith("--write-batch-size=", StringComparison.OrdinalIgnoreCase)) - { - _writeBatchSize = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1))); - } - if (arg.StartsWith("--divisor-cycles=")) - { - cyclesPath = arg[(arg.IndexOf('=') + 1)..]; - } - else if (arg.StartsWith("--divisor-cycles-device=", StringComparison.OrdinalIgnoreCase)) - { - useGpuCycles = !arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase); - } - else if (arg.StartsWith("--divisor-cycles-batch-size=", StringComparison.OrdinalIgnoreCase)) - { - cyclesBatchSize = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1))); - } + else if (arg.StartsWith("--gpu-prime-threads=", StringComparison.OrdinalIgnoreCase)) + { + gpuPrimeThreads = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1))); + } + else if (arg.StartsWith("--gpu-prime-batch=", StringComparison.OrdinalIgnoreCase)) + { + gpuPrimeBatch = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1))); + } + else if (arg.StartsWith("--ll-slice=", StringComparison.OrdinalIgnoreCase)) + { + sliceSize = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1))); + } + else if (arg.StartsWith("--gpu-scan-batch=", StringComparison.OrdinalIgnoreCase)) + { + scanBatchSize = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1))); + } + else if (arg.StartsWith("--block-size=", StringComparison.OrdinalIgnoreCase)) + { + blockSize = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1))); + } + else if (arg.StartsWith("--filter-p=", StringComparison.OrdinalIgnoreCase)) + { + filterFile = arg[(arg.IndexOf('=') + 1)..]; + } + else if (arg.StartsWith("--write-batch-size=", StringComparison.OrdinalIgnoreCase)) + { + _writeBatchSize = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1))); + } + if (arg.StartsWith("--divisor-cycles=")) + { + cyclesPath = arg[(arg.IndexOf('=') + 1)..]; + } + else if (arg.StartsWith("--divisor-cycles-device=", StringComparison.OrdinalIgnoreCase)) + { + useGpuCycles = !arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase); + } + else if (arg.StartsWith("--divisor-cycles-batch-size=", StringComparison.OrdinalIgnoreCase)) + { + cyclesBatchSize = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1))); + } else if (arg.Equals("--divisor-cycles-continue", StringComparison.OrdinalIgnoreCase)) { continueCyclesGeneration = true; } - } + } if (useByDivisor) { @@ -531,7 +531,7 @@ internal static class Program RleBlacklist.Load(_rleBlacklistPath!); } - ulong remainder = currentP % 6UL; + ulong remainder = currentP % 6UL; if (currentP == InitialP && string.IsNullOrEmpty(filterFile)) { // bool passedAllTests = IsEvenPerfectCandidate(InitialP, out bool searchedMersenne, out bool detailedCheck); @@ -599,42 +599,42 @@ internal static class Program File.WriteAllText(ResultsFileName, $"p,searchedMersenne,detailedCheck,passedAllTests{Environment.NewLine}"); } - bool useFilter = !string.IsNullOrEmpty(filterFile); - HashSet filter = []; - List filterList = []; - ulong maxP = 0UL; - ulong[] localFilter = Array.Empty(); - int filterCount = 0; - if (useFilter) - { - Console.WriteLine("Loading filter..."); - localFilter = new ulong[1024]; - filterCount = 0; - LoadResultsFile(filterFile, (p, detailedCheck, passedAllTests) => - { - if (passedAllTests) - { - localFilter[filterCount++] = p; - if (p > maxP) - { - maxP = p; - } + bool useFilter = !string.IsNullOrEmpty(filterFile); + HashSet filter = []; + List filterList = []; + ulong maxP = 0UL; + ulong[] localFilter = Array.Empty(); + int filterCount = 0; + if (useFilter) + { + Console.WriteLine("Loading filter..."); + localFilter = new ulong[1024]; + filterCount = 0; + LoadResultsFile(filterFile, (p, detailedCheck, passedAllTests) => + { + if (passedAllTests) + { + localFilter[filterCount++] = p; + if (p > maxP) + { + maxP = p; + } - if (filterCount == 1024) - { - filter.AddRange(localFilter[..filterCount]); - filterCount = 0; - Console.WriteLine($"Added {p}"); - } - } - }); + if (filterCount == 1024) + { + filter.AddRange(localFilter[..filterCount]); + filterCount = 0; + Console.WriteLine($"Added {p}"); + } + } + }); - if (filterCount > 0) - { - filter.AddRange(localFilter[..filterCount]); - } - filterList = [.. filter]; - } + if (filterCount > 0) + { + filter.AddRange(localFilter[..filterCount]); + } + filterList = [.. filter]; + } if (useByDivisor) { @@ -681,43 +681,43 @@ internal static class Program } } - _state = ((long)currentP << 3) | (long)remainder; - Task[] tasks = new Task[threadCount]; + _state = ((long)currentP << 3) | (long)remainder; + Task[] tasks = new Task[threadCount]; - int taskIndex = 0; - for (; taskIndex < threadCount; taskIndex++) - { - tasks[taskIndex] = Task.Run(() => - { - int count, j; - ulong p; - bool passedAllTests, searchedMersenne, detailedCheck; - ulong[] buffer = new ulong[blockSize]; - bool reachedMax = false; - while (!Volatile.Read(ref _limitReached)) - { - count = ReserveBlock(buffer, blockSize); - if (count == 0) - { + int taskIndex = 0; + for (; taskIndex < threadCount; taskIndex++) + { + tasks[taskIndex] = Task.Run(() => + { + int count, j; + ulong p; + bool passedAllTests, searchedMersenne, detailedCheck; + ulong[] buffer = new ulong[blockSize]; + bool reachedMax = false; + while (!Volatile.Read(ref _limitReached)) + { + count = ReserveBlock(buffer, blockSize); + if (count == 0) + { break; } - if (!useFilter) - { - for (j = 0; j < count && !Volatile.Read(ref _limitReached); j++) - { - p = buffer[j]; + if (!useFilter) + { + for (j = 0; j < count && !Volatile.Read(ref _limitReached); j++) + { + p = buffer[j]; passedAllTests = IsEvenPerfectCandidate(p, divisorCyclesSearchLimit, out searchedMersenne, out detailedCheck); PrintResult(p, searchedMersenne, detailedCheck, passedAllTests); } - } - else - { - reachedMax = false; + } + else + { + reachedMax = false; - for (j = 0; j < count; j++) - { - p = buffer[j]; + for (j = 0; j < count; j++) + { + p = buffer[j]; if (Volatile.Read(ref _limitReached) && p > maxP) { @@ -753,538 +753,539 @@ internal static class Program StringBuilderPool.Return(_outputBuilder!); } - private static void RunByDivisorMode(List primes, ulong divisorCyclesSearchLimit, int threadCount) - { - if (primes.Count == 0) - { - return; - } - - List states = new(primes.Count); - ByDivisorPrimeState stateToAdd; - bool searched = false; - bool detailed = false; - ulong allowedMax = 0UL; - - _byDivisorPrecheckOnly = true; - ulong startPrime = _byDivisorStartPrime; - bool applyStartPrime = startPrime > 0UL; - - foreach (ulong prime in primes) - { - if (applyStartPrime && prime < startPrime) - { - continue; - } - - if (!IsEvenPerfectCandidate(prime, divisorCyclesSearchLimit, out searched, out detailed)) - { - PrintResult(prime, searched, detailed, false); - continue; - } - - allowedMax = _byDivisorTester!.GetAllowedMaxDivisor(prime); - if (allowedMax < 3UL) - { - PrintResult(prime, searchedMersenne: true, detailedCheck: true, passedAllTests: true); - continue; - } - - stateToAdd = new ByDivisorPrimeState - { - Prime = prime, - AllowedMax = allowedMax, - Completed = false, - Composite = false, - DetailedCheck = false, - }; - states.Add(stateToAdd); - } - - _byDivisorPrecheckOnly = false; - - if (states.Count == 0) - { - if (applyStartPrime) - { - Console.WriteLine($"No primes greater than or equal to {startPrime.ToString(CultureInfo.InvariantCulture)} were found for --mersenne=bydivisor."); - } - - return; - } - - states.Sort(static (left, right) => - { - int compare = left.AllowedMax.CompareTo(right.AllowedMax); - if (compare != 0) - { - return compare; - } - - return left.Prime.CompareTo(right.Prime); - }); - - int stateCount = states.Count; - ulong[] primeValues = new ulong[stateCount]; - ulong[] allowedMaxValues = new ulong[stateCount]; - int[] stateFlags = new int[stateCount]; - - int stateIndex = 0; - ByDivisorPrimeState currentState; - for (; stateIndex < stateCount; stateIndex++) - { - currentState = states[stateIndex]; - primeValues[stateIndex] = currentState.Prime; - allowedMaxValues[stateIndex] = currentState.AllowedMax; - stateFlags[stateIndex] = ByDivisorStateActive; - } - - states.Clear(); - - ulong divisorLimit = _byDivisorTester!.DivisorLimit; - ulong nextDivisor = 3UL; - long finalDivisorBits = unchecked((long)3UL); - int divisorsExhaustedFlag = 0; - int finalizerState = 0; - int finalizationCompleted = 0; - int remainingStates = stateCount; - int activeStartIndex = 0; - long[] activeStateMask = new long[(stateCount + 63) >> 6]; - - int maskStateIndex = 0; - int wordIndex = 0; - int bitIndex = 0; - for (; maskStateIndex < stateCount; maskStateIndex++) - { - wordIndex = maskStateIndex >> 6; - bitIndex = maskStateIndex & 63; - activeStateMask[wordIndex] |= 1L << bitIndex; - } - - Task[] workers = new Task[Math.Max(1, threadCount)]; - - int workerIndex = 0; - for (; workerIndex < workers.Length; workerIndex++) - { - int capturedStateCount = stateCount; - workers[workerIndex] = Task.Run(() => - { - using var session = _byDivisorTester!.CreateDivisorSession(); - byte[] hitsBuffer = ArrayPool.Shared.Rent(capturedStateCount); - ulong[] primeBuffer = ArrayPool.Shared.Rent(capturedStateCount); - int[] indexBuffer = ArrayPool.Shared.Rent(capturedStateCount); - PendingResult[] completionsBuffer = ArrayPool.Shared.Rent(capturedStateCount); - PendingResult[] compositesBuffer = ArrayPool.Shared.Rent(capturedStateCount); - int completionsCount = 0; - int compositesCount = 0; - ulong localDivisorCursor = 0UL; - int localDivisorsRemaining = 0; - bool exhausted = false; - ulong divisor = 0UL; - int activeCount = 0; - Span hitsSpan = default; - int hitIndex = 0; - int index = 0; - - try - { - while (true) - { - if (Volatile.Read(ref remainingStates) == 0) - { - if (Volatile.Read(ref finalizationCompleted) != 0 || Volatile.Read(ref divisorsExhaustedFlag) == 0) - { - break; - } - } - - divisor = AcquireNextDivisor(ref nextDivisor, divisorLimit, ref divisorsExhaustedFlag, ref finalDivisorBits, out exhausted, ref localDivisorCursor, ref localDivisorsRemaining); - - if (divisor == 0UL) - { - if (!exhausted) - { - if (Volatile.Read(ref remainingStates) == 0) - { - break; - } - - Thread.Yield(); - continue; - } - - if (Interlocked.CompareExchange(ref finalizerState, 1, 0) == 0) - { - FinalizeRemainingStates(primeValues, allowedMaxValues, stateFlags, ref remainingStates, ref finalDivisorBits, completionsBuffer, ref completionsCount, activeStateMask); - - if (completionsCount > 0) - { - FlushPendingResults(completionsBuffer, ref completionsCount); - } - - Volatile.Write(ref finalizationCompleted, 1); - } - else - { - while (Volatile.Read(ref finalizationCompleted) == 0 && Volatile.Read(ref remainingStates) > 0) - { - Thread.Yield(); - } - } - - if (Volatile.Read(ref remainingStates) == 0) - { - break; - } - - continue; - } - - activeCount = BuildPrimeBuffer(divisor, primeValues, allowedMaxValues, stateFlags, primeBuffer, indexBuffer, completionsBuffer, ref completionsCount, ref remainingStates, activeStateMask, ref activeStartIndex); - - if (completionsCount > 0) - { - FlushPendingResults(completionsBuffer, ref completionsCount); - } - - if (activeCount == 0) - { - continue; - } - - hitsSpan = hitsBuffer.AsSpan(0, activeCount); - hitsSpan.Clear(); - session.CheckDivisor(divisor, primeBuffer.AsSpan(0, activeCount), hitsSpan); - - for (hitIndex = 0; hitIndex < activeCount; hitIndex++) - { - if (hitsSpan[hitIndex] == 0) - { - continue; - } - - index = indexBuffer[hitIndex]; - if (Interlocked.CompareExchange(ref stateFlags[index], ByDivisorStateComposite, ByDivisorStateActive) == ByDivisorStateActive) - { - ClearActiveMask(activeStateMask, index); - Interlocked.Decrement(ref remainingStates); - compositesBuffer[compositesCount++] = new PendingResult(primeValues[index], detailedCheck: true, passedAllTests: false); - } - - if (compositesCount == compositesBuffer.Length) - { - FlushPendingResults(compositesBuffer, ref compositesCount); - } - } - - if (compositesCount > 0) - { - FlushPendingResults(compositesBuffer, ref compositesCount); - } - } - - if (completionsCount > 0) - { - FlushPendingResults(completionsBuffer, ref completionsCount); - } - - if (compositesCount > 0) - { - FlushPendingResults(compositesBuffer, ref compositesCount); - } - } - finally - { - ArrayPool.Shared.Return(compositesBuffer, clearArray: true); - ArrayPool.Shared.Return(completionsBuffer, clearArray: true); - ArrayPool.Shared.Return(indexBuffer, clearArray: true); - ArrayPool.Shared.Return(primeBuffer, clearArray: true); - ArrayPool.Shared.Return(hitsBuffer, clearArray: true); - } - }); - } - - Task.WaitAll(workers); - } - - private static ulong AcquireNextDivisor(ref ulong nextDivisor, ulong divisorLimit, ref int divisorsExhaustedFlag, ref long finalDivisorBits, out bool exhausted, ref ulong localDivisorCursor, ref int localDivisorsRemaining) - { - ref long nextDivisorBits = ref Unsafe.As(ref nextDivisor); - ulong currentValue = 0UL; - long currentBits = 0L; - ulong maximumNext = 0UL; - ulong blockStride = unchecked((ulong)(DivisorAllocationBlockSize * 2)); - ulong requestedNext = 0UL; - long nextBits = 0L; - ulong available = 0UL; - int count = 0; - - if (localDivisorsRemaining > 0) - { - currentValue = localDivisorCursor; - localDivisorCursor += 2UL; - localDivisorsRemaining--; - exhausted = false; - return currentValue; - } - - while (true) - { - if (Volatile.Read(ref divisorsExhaustedFlag) != 0) - { - exhausted = true; - return 0UL; - } - - currentBits = Volatile.Read(ref nextDivisorBits); - currentValue = unchecked((ulong)currentBits); - if (currentValue > divisorLimit) - { - if (Interlocked.CompareExchange(ref divisorsExhaustedFlag, 1, 0) == 0) - { - Volatile.Write(ref finalDivisorBits, currentBits); - } - - exhausted = true; - return 0UL; - } - - maximumNext = divisorLimit >= ulong.MaxValue - 1UL ? ulong.MaxValue : divisorLimit + 2UL; - requestedNext = currentValue > ulong.MaxValue - blockStride ? ulong.MaxValue : currentValue + blockStride; - if (requestedNext > maximumNext) - { - requestedNext = maximumNext; - } - - nextBits = unchecked((long)requestedNext); - if (Interlocked.CompareExchange(ref nextDivisorBits, nextBits, currentBits) != currentBits) - { - continue; - } - - available = requestedNext - currentValue; - if (available == 0UL) - { - continue; - } - - count = (int)(available >> 1); - if (count <= 0) - { - continue; - } - - localDivisorCursor = currentValue + 2UL; - localDivisorsRemaining = count - 1; - exhausted = false; - return currentValue; - } - } - - private static int BuildPrimeBuffer(ulong divisor, ulong[] primeValues, ulong[] allowedMaxValues, int[] stateFlags, ulong[] primeBuffer, int[] indexBuffer, PendingResult[] completionsBuffer, ref int completionsCount, ref int remainingStates, long[] activeStateMask, ref int activeStartIndex) - { - int length = primeValues.Length; - int startIndex = Volatile.Read(ref activeStartIndex); - int index = startIndex; - int wordIndex = 0; - ulong word = 0UL; - int bitOffset = 0; - int candidateIndex = 0; - - while (index < length && allowedMaxValues[index] < divisor) - { - if (Interlocked.CompareExchange(ref stateFlags[index], ByDivisorStateCompletedDetailed, ByDivisorStateActive) == ByDivisorStateActive) - { - ClearActiveMask(activeStateMask, index); - Interlocked.Decrement(ref remainingStates); - if (completionsCount == completionsBuffer.Length) - { - FlushPendingResults(completionsBuffer, ref completionsCount); - } - completionsBuffer[completionsCount++] = new PendingResult(primeValues[index], detailedCheck: true, passedAllTests: true); - } - else - { - ClearActiveMask(activeStateMask, index); - } - - index++; - } - - if (index != startIndex) - { - Volatile.Write(ref activeStartIndex, index); - } - - int activeCount = 0; - while (index < length) - { - wordIndex = index >> 6; - word = unchecked((ulong)Volatile.Read(ref activeStateMask[wordIndex])); - if (word == 0UL) - { - index = (wordIndex + 1) << 6; - continue; - } - - bitOffset = index & 63; - if (bitOffset != 0) - { - word &= ulong.MaxValue << bitOffset; - if (word == 0UL) - { - index = (wordIndex + 1) << 6; - continue; - } - } - - while (word != 0UL) - { - candidateIndex = (wordIndex << 6) + BitOperations.TrailingZeroCount(word); - if (candidateIndex >= length) - { - return activeCount; - } - - if (Volatile.Read(ref stateFlags[candidateIndex]) == ByDivisorStateActive) - { - primeBuffer[activeCount] = primeValues[candidateIndex]; - indexBuffer[activeCount] = candidateIndex; - activeCount++; - } - else - { - ClearActiveMask(activeStateMask, candidateIndex); - } - - word &= word - 1UL; - } - - index = (wordIndex + 1) << 6; - } - - return activeCount; - } - - private static void FinalizeRemainingStates(ulong[] primeValues, ulong[] allowedMaxValues, int[] stateFlags, ref int remainingStates, ref long finalDivisorBits, PendingResult[] completionsBuffer, ref int completionsCount, long[] activeStateMask) - { - int finalizeIndex = 0; - bool detailed = false; - for (; finalizeIndex < primeValues.Length; finalizeIndex++) - { - if (Volatile.Read(ref stateFlags[finalizeIndex]) != ByDivisorStateActive) - { - continue; - } - - detailed = unchecked((ulong)Volatile.Read(ref finalDivisorBits)) > allowedMaxValues[finalizeIndex]; - - if (Interlocked.CompareExchange(ref stateFlags[finalizeIndex], detailed ? ByDivisorStateCompletedDetailed : ByDivisorStateCompleted, ByDivisorStateActive) != ByDivisorStateActive) - { - continue; - } - - ClearActiveMask(activeStateMask, finalizeIndex); - Interlocked.Decrement(ref remainingStates); - if (completionsCount == completionsBuffer.Length) - { - FlushPendingResults(completionsBuffer, ref completionsCount); - } - completionsBuffer[completionsCount++] = new PendingResult(primeValues[finalizeIndex], detailedCheck: detailed, passedAllTests: true); - } - } - - private static void FlushPendingResults(PendingResult[] buffer, ref int count) - { - int flushIndex = 0; - for (; flushIndex < count; flushIndex++) - { - PendingResult result = buffer[flushIndex]; - PrintResult(result.Prime, searchedMersenne: true, detailedCheck: result.DetailedCheck, passedAllTests: result.PassedAllTests); - } - - count = 0; - } - - private static void ClearActiveMask(long[] activeStateMask, int index) - { - int wordIndex = index >> 6; - long bit = 1L << (index & 63); - - while (true) - { - long current = Volatile.Read(ref activeStateMask[wordIndex]); - if ((current & bit) == 0) - { - return; - } - - if (Interlocked.CompareExchange(ref activeStateMask[wordIndex], current & ~bit, current) == current) - { - return; - } - } - } - - private static unsafe void LoadResultsFile(string resultsFileName, Action lineProcessorAction) - { - using FileStream readStream = new(resultsFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - using StreamReader reader = new(readStream); - string? line; - bool headerSkipped = false; - ReadOnlySpan span; - int first = 0; - int second = 0; - int third = 0; - ulong parsedP = 0UL; - ReadOnlySpan detailedSpan = default; - ReadOnlySpan passedAllTestsSpan = default; - bool detailed = false; - bool passedAllTests = false; - while ((line = reader.ReadLine()) != null) - { - if (!headerSkipped) - { - headerSkipped = true; + private static void RunByDivisorMode(List primes, ulong divisorCyclesSearchLimit, int threadCount) + { + if (primes.Count == 0) + { + return; + } + + List states = new(primes.Count); + ByDivisorPrimeState stateToAdd; + bool searched = false; + bool detailed = false; + ulong allowedMax = 0UL; + + _byDivisorPrecheckOnly = true; + ulong startPrime = _byDivisorStartPrime; + bool applyStartPrime = startPrime > 0UL; + + foreach (ulong prime in primes) + { + if (applyStartPrime && prime < startPrime) + { + continue; + } + + if (!IsEvenPerfectCandidate(prime, divisorCyclesSearchLimit, out searched, out detailed)) + { + PrintResult(prime, searched, detailed, false); + continue; + } + + allowedMax = _byDivisorTester!.GetAllowedMaxDivisor(prime); + if (allowedMax < 3UL) + { + PrintResult(prime, searchedMersenne: true, detailedCheck: true, passedAllTests: true); + continue; + } + + stateToAdd = new ByDivisorPrimeState + { + Prime = prime, + AllowedMax = allowedMax, + Completed = false, + Composite = false, + DetailedCheck = false, + }; + states.Add(stateToAdd); + } + + _byDivisorPrecheckOnly = false; + + if (states.Count == 0) + { + if (applyStartPrime) + { + Console.WriteLine($"No primes greater than or equal to {startPrime.ToString(CultureInfo.InvariantCulture)} were found for --mersenne=bydivisor."); + } + + return; + } + + states.Sort(static (left, right) => + { + int compare = left.AllowedMax.CompareTo(right.AllowedMax); + if (compare != 0) + { + return compare; + } + + return left.Prime.CompareTo(right.Prime); + }); + + int stateCount = states.Count; + ulong[] primeValues = new ulong[stateCount]; + ulong[] allowedMaxValues = new ulong[stateCount]; + int[] stateFlags = new int[stateCount]; + + int stateIndex = 0; + ByDivisorPrimeState currentState; + for (; stateIndex < stateCount; stateIndex++) + { + currentState = states[stateIndex]; + primeValues[stateIndex] = currentState.Prime; + allowedMaxValues[stateIndex] = currentState.AllowedMax; + stateFlags[stateIndex] = ByDivisorStateActive; + } + + states.Clear(); + + ulong divisorLimit = _byDivisorTester!.DivisorLimit; + ulong nextDivisor = 3UL; + long finalDivisorBits = unchecked((long)3UL); + int divisorsExhaustedFlag = 0; + int finalizerState = 0; + int finalizationCompleted = 0; + int remainingStates = stateCount; + int activeStartIndex = 0; + long[] activeStateMask = new long[(stateCount + 63) >> 6]; + + int maskStateIndex = 0; + int wordIndex = 0; + int bitIndex = 0; + for (; maskStateIndex < stateCount; maskStateIndex++) + { + wordIndex = maskStateIndex >> 6; + bitIndex = maskStateIndex & 63; + activeStateMask[wordIndex] |= 1L << bitIndex; + } + + Task[] workers = new Task[Math.Max(1, threadCount)]; + + int workerIndex = 0; + for (; workerIndex < workers.Length; workerIndex++) + { + int capturedStateCount = stateCount; + workers[workerIndex] = Task.Run(() => + { + using var session = _byDivisorTester!.CreateDivisorSession(); + byte[] hitsBuffer = ArrayPool.Shared.Rent(capturedStateCount); + ulong[] primeBuffer = ArrayPool.Shared.Rent(capturedStateCount); + int[] indexBuffer = ArrayPool.Shared.Rent(capturedStateCount); + PendingResult[] completionsBuffer = ArrayPool.Shared.Rent(capturedStateCount); + PendingResult[] compositesBuffer = ArrayPool.Shared.Rent(capturedStateCount); + int completionsCount = 0; + int compositesCount = 0; + ulong localDivisorCursor = 0UL; + int localDivisorsRemaining = 0; + bool exhausted = false; + ulong divisor = 0UL; + int activeCount = 0; + Span hitsSpan = default; + int hitIndex = 0; + int index = 0; + + try + { + while (true) + { + if (Volatile.Read(ref remainingStates) == 0) + { + if (Volatile.Read(ref finalizationCompleted) != 0 || Volatile.Read(ref divisorsExhaustedFlag) == 0) + { + break; + } + } + + divisor = AcquireNextDivisor(ref nextDivisor, divisorLimit, ref divisorsExhaustedFlag, ref finalDivisorBits, out exhausted, ref localDivisorCursor, ref localDivisorsRemaining); + + if (divisor == 0UL) + { + if (!exhausted) + { + if (Volatile.Read(ref remainingStates) == 0) + { + break; + } + + Thread.Yield(); + continue; + } + + if (Interlocked.CompareExchange(ref finalizerState, 1, 0) == 0) + { + FinalizeRemainingStates(primeValues, allowedMaxValues, stateFlags, ref remainingStates, ref finalDivisorBits, completionsBuffer, ref completionsCount, activeStateMask); + + if (completionsCount > 0) + { + FlushPendingResults(completionsBuffer, ref completionsCount); + } + + Volatile.Write(ref finalizationCompleted, 1); + } + else + { + while (Volatile.Read(ref finalizationCompleted) == 0 && Volatile.Read(ref remainingStates) > 0) + { + Thread.Yield(); + } + } + + if (Volatile.Read(ref remainingStates) == 0) + { + break; + } + + continue; + } + + // Can we prepare as many buffers as many threads were requested upfront and have them prepared in separate task, so that they're always ready when needed? + activeCount = BuildPrimeBuffer(divisor, primeValues, allowedMaxValues, stateFlags, primeBuffer, indexBuffer, completionsBuffer, ref completionsCount, ref remainingStates, activeStateMask, ref activeStartIndex); + + if (completionsCount > 0) + { + FlushPendingResults(completionsBuffer, ref completionsCount); + } + + if (activeCount == 0) + { + continue; + } + + hitsSpan = hitsBuffer.AsSpan(0, activeCount); + hitsSpan.Clear(); + session.CheckDivisor(divisor, primeBuffer.AsSpan(0, activeCount), hitsSpan); + + for (hitIndex = 0; hitIndex < activeCount; hitIndex++) + { + if (hitsSpan[hitIndex] == 0) + { + continue; + } + + index = indexBuffer[hitIndex]; + if (Interlocked.CompareExchange(ref stateFlags[index], ByDivisorStateComposite, ByDivisorStateActive) == ByDivisorStateActive) + { + ClearActiveMask(activeStateMask, index); + Interlocked.Decrement(ref remainingStates); + compositesBuffer[compositesCount++] = new PendingResult(primeValues[index], detailedCheck: true, passedAllTests: false); + } + + if (compositesCount == compositesBuffer.Length) + { + FlushPendingResults(compositesBuffer, ref compositesCount); + } + } + + if (compositesCount > 0) + { + FlushPendingResults(compositesBuffer, ref compositesCount); + } + } + + if (completionsCount > 0) + { + FlushPendingResults(completionsBuffer, ref completionsCount); + } + + if (compositesCount > 0) + { + FlushPendingResults(compositesBuffer, ref compositesCount); + } + } + finally + { + ArrayPool.Shared.Return(compositesBuffer, clearArray: true); + ArrayPool.Shared.Return(completionsBuffer, clearArray: true); + ArrayPool.Shared.Return(indexBuffer, clearArray: true); + ArrayPool.Shared.Return(primeBuffer, clearArray: true); + ArrayPool.Shared.Return(hitsBuffer, clearArray: true); + } + }); + } + + Task.WaitAll(workers); + } + + private static ulong AcquireNextDivisor(ref ulong nextDivisor, ulong divisorLimit, ref int divisorsExhaustedFlag, ref long finalDivisorBits, out bool exhausted, ref ulong localDivisorCursor, ref int localDivisorsRemaining) + { + ref long nextDivisorBits = ref Unsafe.As(ref nextDivisor); + ulong currentValue = 0UL; + long currentBits = 0L; + ulong maximumNext = 0UL; + ulong blockStride = unchecked((ulong)(DivisorAllocationBlockSize * 2)); + ulong requestedNext = 0UL; + long nextBits = 0L; + ulong available = 0UL; + int count = 0; + + if (localDivisorsRemaining > 0) + { + currentValue = localDivisorCursor; + localDivisorCursor += 2UL; + localDivisorsRemaining--; + exhausted = false; + return currentValue; + } + + while (true) + { + if (Volatile.Read(ref divisorsExhaustedFlag) != 0) + { + exhausted = true; + return 0UL; + } + + currentBits = Volatile.Read(ref nextDivisorBits); + currentValue = unchecked((ulong)currentBits); + if (currentValue > divisorLimit) + { + if (Interlocked.CompareExchange(ref divisorsExhaustedFlag, 1, 0) == 0) + { + Volatile.Write(ref finalDivisorBits, currentBits); + } + + exhausted = true; + return 0UL; + } + + maximumNext = divisorLimit >= ulong.MaxValue - 1UL ? ulong.MaxValue : divisorLimit + 2UL; + requestedNext = currentValue > ulong.MaxValue - blockStride ? ulong.MaxValue : currentValue + blockStride; + if (requestedNext > maximumNext) + { + requestedNext = maximumNext; + } + + nextBits = unchecked((long)requestedNext); + if (Interlocked.CompareExchange(ref nextDivisorBits, nextBits, currentBits) != currentBits) + { + continue; + } + + available = requestedNext - currentValue; + if (available == 0UL) + { + continue; + } + + count = (int)(available >> 1); + if (count <= 0) + { + continue; + } + + localDivisorCursor = currentValue + 2UL; + localDivisorsRemaining = count - 1; + exhausted = false; + return currentValue; + } + } + + private static int BuildPrimeBuffer(ulong divisor, ulong[] primeValues, ulong[] allowedMaxValues, int[] stateFlags, ulong[] primeBuffer, int[] indexBuffer, PendingResult[] completionsBuffer, ref int completionsCount, ref int remainingStates, long[] activeStateMask, ref int activeStartIndex) + { + int length = primeValues.Length; + int startIndex = Volatile.Read(ref activeStartIndex); + int index = startIndex; + int wordIndex = 0; + ulong word = 0UL; + int bitOffset = 0; + int candidateIndex = 0; + + while (index < length && allowedMaxValues[index] < divisor) + { + if (Interlocked.CompareExchange(ref stateFlags[index], ByDivisorStateCompletedDetailed, ByDivisorStateActive) == ByDivisorStateActive) + { + ClearActiveMask(activeStateMask, index); + Interlocked.Decrement(ref remainingStates); + if (completionsCount == completionsBuffer.Length) + { + FlushPendingResults(completionsBuffer, ref completionsCount); + } + completionsBuffer[completionsCount++] = new PendingResult(primeValues[index], detailedCheck: true, passedAllTests: true); + } + else + { + ClearActiveMask(activeStateMask, index); + } + + index++; + } + + if (index != startIndex) + { + Volatile.Write(ref activeStartIndex, index); + } + + int activeCount = 0; + while (index < length) + { + wordIndex = index >> 6; + word = unchecked((ulong)Volatile.Read(ref activeStateMask[wordIndex])); + if (word == 0UL) + { + index = (wordIndex + 1) << 6; + continue; + } + + bitOffset = index & 63; + if (bitOffset != 0) + { + word &= ulong.MaxValue << bitOffset; + if (word == 0UL) + { + index = (wordIndex + 1) << 6; + continue; + } + } + + while (word != 0UL) + { + candidateIndex = (wordIndex << 6) + BitOperations.TrailingZeroCount(word); + if (candidateIndex >= length) + { + return activeCount; + } + + if (Volatile.Read(ref stateFlags[candidateIndex]) == ByDivisorStateActive) + { + primeBuffer[activeCount] = primeValues[candidateIndex]; + indexBuffer[activeCount] = candidateIndex; + activeCount++; + } + else + { + ClearActiveMask(activeStateMask, candidateIndex); + } + + word &= word - 1UL; + } + + index = (wordIndex + 1) << 6; + } + + return activeCount; + } + + private static void FinalizeRemainingStates(ulong[] primeValues, ulong[] allowedMaxValues, int[] stateFlags, ref int remainingStates, ref long finalDivisorBits, PendingResult[] completionsBuffer, ref int completionsCount, long[] activeStateMask) + { + int finalizeIndex = 0; + bool detailed = false; + for (; finalizeIndex < primeValues.Length; finalizeIndex++) + { + if (Volatile.Read(ref stateFlags[finalizeIndex]) != ByDivisorStateActive) + { + continue; + } + + detailed = unchecked((ulong)Volatile.Read(ref finalDivisorBits)) > allowedMaxValues[finalizeIndex]; + + if (Interlocked.CompareExchange(ref stateFlags[finalizeIndex], detailed ? ByDivisorStateCompletedDetailed : ByDivisorStateCompleted, ByDivisorStateActive) != ByDivisorStateActive) + { + continue; + } + + ClearActiveMask(activeStateMask, finalizeIndex); + Interlocked.Decrement(ref remainingStates); + if (completionsCount == completionsBuffer.Length) + { + FlushPendingResults(completionsBuffer, ref completionsCount); + } + completionsBuffer[completionsCount++] = new PendingResult(primeValues[finalizeIndex], detailedCheck: detailed, passedAllTests: true); + } + } + + private static void FlushPendingResults(PendingResult[] buffer, ref int count) + { + int flushIndex = 0; + for (; flushIndex < count; flushIndex++) + { + PendingResult result = buffer[flushIndex]; + PrintResult(result.Prime, searchedMersenne: true, detailedCheck: result.DetailedCheck, passedAllTests: result.PassedAllTests); + } + + count = 0; + } + + private static void ClearActiveMask(long[] activeStateMask, int index) + { + int wordIndex = index >> 6; + long bit = 1L << (index & 63); + + while (true) + { + long current = Volatile.Read(ref activeStateMask[wordIndex]); + if ((current & bit) == 0) + { + return; + } + + if (Interlocked.CompareExchange(ref activeStateMask[wordIndex], current & ~bit, current) == current) + { + return; + } + } + } + + private static unsafe void LoadResultsFile(string resultsFileName, Action lineProcessorAction) + { + using FileStream readStream = new(resultsFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using StreamReader reader = new(readStream); + string? line; + bool headerSkipped = false; + ReadOnlySpan span; + int first = 0; + int second = 0; + int third = 0; + ulong parsedP = 0UL; + ReadOnlySpan detailedSpan = default; + ReadOnlySpan passedAllTestsSpan = default; + bool detailed = false; + bool passedAllTests = false; + while ((line = reader.ReadLine()) != null) + { + if (!headerSkipped) + { + headerSkipped = true; continue; } if (string.IsNullOrWhiteSpace(line)) { - continue; - } + continue; + } - span = line.AsSpan(); - first = span.IndexOf(','); - if (first < 0) - { - continue; - } + span = line.AsSpan(); + first = span.IndexOf(','); + if (first < 0) + { + continue; + } - parsedP = ulong.Parse(span[..first]); - span = span[(first + 1)..]; - second = span.IndexOf(','); - if (second < 0) - { - continue; - } + parsedP = ulong.Parse(span[..first]); + span = span[(first + 1)..]; + second = span.IndexOf(','); + if (second < 0) + { + continue; + } - span = span[(second + 1)..]; - third = span.IndexOf(','); - if (third < 0) - { - continue; - } + span = span[(second + 1)..]; + third = span.IndexOf(','); + if (third < 0) + { + continue; + } - detailedSpan = span[..third]; - passedAllTestsSpan = span[(third + 1)..]; + detailedSpan = span[..third]; + passedAllTestsSpan = span[(third + 1)..]; - if (bool.TryParse(detailedSpan, out detailed) && bool.TryParse(passedAllTestsSpan, out passedAllTests)) - { - lineProcessorAction(parsedP, detailed, passedAllTests); - } - } - } + if (bool.TryParse(detailedSpan, out detailed) && bool.TryParse(passedAllTestsSpan, out passedAllTests)) + { + lineProcessorAction(parsedP, detailed, passedAllTests); + } + } + } private static void PrintHelp() { @@ -1383,137 +1384,137 @@ internal static class Program } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static 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 static 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; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static StringBuilder? AppendToOutputBuffer(StringBuilder source) - { - StringBuilder? builderToFlush = null; - - lock (Sync) - { - StringBuilder outputBuilder = _outputBuilder!; - _ = outputBuilder.Append(source); - - int nextIndex = _writeIndex + 1; - if (nextIndex >= _writeBatchSize) - { - builderToFlush = outputBuilder; - _outputBuilder = StringBuilderPool.Rent(); - _writeIndex = 0; - } - else - { - _writeIndex = nextIndex; - } - } - - return builderToFlush; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void PrintResult(ulong currentP, bool searchedMersenne, bool detailedCheck, bool passedAllTests) - { - if (passedAllTests) - { - int newCount = Interlocked.Increment(ref _primeCount); - if (newCount >= 2) - { - Volatile.Write(ref _primeFoundAfterInit, true); - } - } - - bool lastWasComposite = _lastCompositeP; - bool primeCandidate = passedAllTests && !lastWasComposite && Volatile.Read(ref _primeFoundAfterInit); - - StringBuilder localBuilder = StringBuilderPool.Rent(); - _ = localBuilder - .Append(currentP).Append(',') - .Append(searchedMersenne).Append(',') - .Append(detailedCheck).Append(',') - .Append(passedAllTests).Append('\n'); - - bool printToConsole = TryAcquireConsoleSlot(primeCandidate, out bool primeFlag); - StringBuilder? builderToFlush = AppendToOutputBuffer(localBuilder); - - if (builderToFlush is not null) - { - FlushBuffer(builderToFlush); - } - - if (printToConsole) - { - if (primeFlag) - { - _ = localBuilder - .Remove(localBuilder.Length - 1, 1) - .Append(PrimeFoundSuffix) - .Append('\n'); - } - - Console.WriteLine(localBuilder.Remove(localBuilder.Length - 1, 1).ToString()); - } - - localBuilder.Clear(); - StringBuilderPool.Return(localBuilder); - } + primeFlag = false; + return false; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void FlushBuffer() - { - StringBuilder? builderToFlush = null; + private static StringBuilder? AppendToOutputBuffer(StringBuilder source) + { + StringBuilder? builderToFlush = null; - lock (Sync) - { - if (_writeIndex == 0 || _outputBuilder is null || _outputBuilder.Length == 0) - { - return; - } + lock (Sync) + { + StringBuilder outputBuilder = _outputBuilder!; + _ = outputBuilder.Append(source); - builderToFlush = _outputBuilder; - _outputBuilder = StringBuilderPool.Rent(); - _writeIndex = 0; - } + int nextIndex = _writeIndex + 1; + if (nextIndex >= _writeBatchSize) + { + builderToFlush = outputBuilder; + _outputBuilder = StringBuilderPool.Rent(); + _writeIndex = 0; + } + else + { + _writeIndex = nextIndex; + } + } - FlushBuffer(builderToFlush!); - } + return builderToFlush; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void FlushBuffer(StringBuilder builderToFlush) - { - try - { - lock (FileWriteSync) - { - using FileStream stream = new(ResultsFileName, FileMode.Append, FileAccess.Write, FileShare.Read); - using StreamWriter writer = new(stream) { AutoFlush = false }; - writer.Write(builderToFlush); - writer.Flush(); - } - } - finally - { - builderToFlush.Clear(); - StringBuilderPool.Return(builderToFlush); - } - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void PrintResult(ulong currentP, bool searchedMersenne, bool detailedCheck, bool passedAllTests) + { + if (passedAllTests) + { + int newCount = Interlocked.Increment(ref _primeCount); + if (newCount >= 2) + { + Volatile.Write(ref _primeFoundAfterInit, true); + } + } + + bool lastWasComposite = _lastCompositeP; + bool primeCandidate = passedAllTests && !lastWasComposite && Volatile.Read(ref _primeFoundAfterInit); + + StringBuilder localBuilder = StringBuilderPool.Rent(); + _ = localBuilder + .Append(currentP).Append(',') + .Append(searchedMersenne).Append(',') + .Append(detailedCheck).Append(',') + .Append(passedAllTests).Append('\n'); + + bool printToConsole = TryAcquireConsoleSlot(primeCandidate, out bool primeFlag); + StringBuilder? builderToFlush = AppendToOutputBuffer(localBuilder); + + if (builderToFlush is not null) + { + FlushBuffer(builderToFlush); + } + + if (printToConsole) + { + if (primeFlag) + { + _ = localBuilder + .Remove(localBuilder.Length - 1, 1) + .Append(PrimeFoundSuffix) + .Append('\n'); + } + + Console.WriteLine(localBuilder.Remove(localBuilder.Length - 1, 1).ToString()); + } + + localBuilder.Clear(); + StringBuilderPool.Return(localBuilder); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void FlushBuffer() + { + StringBuilder? builderToFlush = null; + + lock (Sync) + { + if (_writeIndex == 0 || _outputBuilder is null || _outputBuilder.Length == 0) + { + return; + } + + builderToFlush = _outputBuilder; + _outputBuilder = StringBuilderPool.Rent(); + _writeIndex = 0; + } + + FlushBuffer(builderToFlush!); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void FlushBuffer(StringBuilder builderToFlush) + { + try + { + lock (FileWriteSync) + { + using FileStream stream = new(ResultsFileName, FileMode.Append, FileAccess.Write, FileShare.Read); + using StreamWriter writer = new(stream) { AutoFlush = false }; + writer.Write(builderToFlush); + writer.Flush(); + } + } + finally + { + builderToFlush.Clear(); + StringBuilderPool.Return(builderToFlush); + } + } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static ulong CountOnes(ulong value) @@ -1599,27 +1600,27 @@ internal static class Program } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ulong TransformPAddPrimes(ulong value, ref ulong remainder) - { - ulong originalRemainder = remainder; - ulong addRemainder = remainder; - ulong candidate = value; - ulong primeCandidate = value; - bool advanceAdd = true; - bool advancePrime = true; - ulong diff = 0UL; - ulong nextPrime = 0UL; + internal static ulong TransformPAddPrimes(ulong value, ref ulong remainder) + { + ulong originalRemainder = remainder; + ulong addRemainder = remainder; + ulong candidate = value; + ulong primeCandidate = value; + bool advanceAdd = true; + bool advancePrime = true; + ulong diff = 0UL; + ulong nextPrime = 0UL; - while (true) - { - if (advanceAdd) - { - diff = GetNextAddDiff(addRemainder); - if (candidate > ulong.MaxValue - diff) - { - Volatile.Write(ref _limitReached, true); - remainder = originalRemainder; - return candidate; + while (true) + { + if (advanceAdd) + { + diff = GetNextAddDiff(addRemainder); + if (candidate > ulong.MaxValue - diff) + { + Volatile.Write(ref _limitReached, true); + remainder = originalRemainder; + return candidate; } candidate += diff; @@ -1630,12 +1631,12 @@ internal static class Program } } - if (advancePrime) - { - try - { - nextPrime = PrimeIterator.Next(in primeCandidate); - } + if (advancePrime) + { + try + { + nextPrime = PrimeIterator.Next(in primeCandidate); + } catch (InvalidOperationException) { Volatile.Write(ref _limitReached, true); @@ -1682,7 +1683,7 @@ internal static class Program // Optional: RLE blacklist and binary-threshold filters on p (safe only when configured) if (p <= _rleHardMaxP) { - if (!_rleOnlyLast7 || p.Mod10() == 7UL) + if (!_rleOnlyLast7 || p.Mod10() == 7UL) { if (RleBlacklist.IsLoaded() && RleBlacklist.Matches(p)) { @@ -1691,31 +1692,31 @@ internal static class Program } } - if (_zeroFracHard >= 0 || (_zeroFracConj >= 0 && _maxZeroConj >= 0)) - { - double zf; - int bitLength = 0; - int zeroCountValue = 0; - int maxZeroBlockValue = 0; - ComputeBitStats(p, out bitLength, out zeroCountValue, out maxZeroBlockValue); - if (_zeroFracHard >= 0) - { - zf = (double)zeroCountValue / bitLength; - if (zf > _zeroFracHard) - { - return false; - } - } + if (_zeroFracHard >= 0 || (_zeroFracConj >= 0 && _maxZeroConj >= 0)) + { + double zf; + int bitLength = 0; + int zeroCountValue = 0; + int maxZeroBlockValue = 0; + ComputeBitStats(p, out bitLength, out zeroCountValue, out maxZeroBlockValue); + if (_zeroFracHard >= 0) + { + zf = (double)zeroCountValue / bitLength; + if (zf > _zeroFracHard) + { + return false; + } + } - if (_zeroFracConj >= 0 && _maxZeroConj >= 0) - { - zf = (double)zeroCountValue / bitLength; - if (zf > _zeroFracConj && maxZeroBlockValue >= _maxZeroConj) - { - return false; - } - } - } + if (_zeroFracConj >= 0 && _maxZeroConj >= 0) + { + zf = (double)zeroCountValue / bitLength; + if (zf > _zeroFracConj && maxZeroBlockValue >= _maxZeroConj) + { + return false; + } + } + } // Fast residue-based composite check for p using small primes if (!_useDivisor && !_useByDivisorMode && IsCompositeByResidues(p)) @@ -1728,16 +1729,16 @@ internal static class Program // If primes-device=gpu, route p primality through GPU-assisted sieve (optional MR later) // TODO: Add deterministic Miller–Rabin rounds in GPU path for 64-bit range. - if (!_useByDivisorMode) - { - if (!(GpuContextPool.ForceCpu - ? PrimeTesters.Value!.IsPrime(p, CancellationToken.None) - : PrimeTesters.Value!.IsPrimeGpu(p, CancellationToken.None))) - { - _lastCompositeP = true; - return false; - } - } + if (!_useByDivisorMode) + { + if (!(GpuContextPool.ForceCpu + ? PrimeTesters.Value!.IsPrime(p, CancellationToken.None) + : PrimeTesters.Value!.IsPrimeGpu(p, CancellationToken.None))) + { + _lastCompositeP = true; + return false; + } + } searchedMersenne = true; if (_useByDivisorMode) @@ -1762,136 +1763,136 @@ internal static class Program } // Use ModResidueTracker with a small set of primes to pre-filter composite p. - private static bool IsCompositeByResidues(ulong p) - { - var tracker = PResidue.Value!; - tracker.BeginMerge(p); - // Use the small prime list from PerfectNumbers.Core to the extent of sqrt(p) - var primes = PrimesGenerator.SmallPrimes; - var primesPow2 = PrimesGenerator.SmallPrimesPow2; - int len = primes.Length; - int primeIndex = 0; - bool divisible = false; - for (; primeIndex < len; primeIndex++) - { - if (primesPow2[primeIndex] > p) - { - break; - } + private static bool IsCompositeByResidues(ulong p) + { + var tracker = PResidue.Value!; + tracker.BeginMerge(p); + // Use the small prime list from PerfectNumbers.Core to the extent of sqrt(p) + var primes = PrimesGenerator.SmallPrimes; + var primesPow2 = PrimesGenerator.SmallPrimesPow2; + int len = primes.Length; + int primeIndex = 0; + bool divisible = false; + for (; primeIndex < len; primeIndex++) + { + if (primesPow2[primeIndex] > p) + { + break; + } - if (tracker.MergeOrAppend(p, primes[primeIndex], out divisible) && divisible) - { - return true; - } - } + if (tracker.MergeOrAppend(p, primes[primeIndex], out divisible) && divisible) + { + return true; + } + } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ComputeBitStats(ulong value, out int bitLen, out int zeroCount, out int maxZeroBlock) - { - bitLen = 64 - int.CreateChecked(ulong.LeadingZeroCount(value)); - zeroCount = 0; - maxZeroBlock = 0; - if (bitLen <= 0) - { - return; - } + private static void ComputeBitStats(ulong value, out int bitLen, out int zeroCount, out int maxZeroBlock) + { + bitLen = 64 - int.CreateChecked(ulong.LeadingZeroCount(value)); + zeroCount = 0; + maxZeroBlock = 0; + if (bitLen <= 0) + { + return; + } - int msbIndex = (bitLen - 1) >> 3; // 0..7 - int bitsInTopByte = ((bitLen - 1) & 7) + 1; // 1..8 - int currentRun = 0; - byte inspectedByte = 0; - int zeroCountInByte = 0; - int prefixZeros = 0; - int suffixZeros = 0; - int maxZeroRunInByte = 0; - int candidate = 0; - int byteIndex = msbIndex; + int msbIndex = (bitLen - 1) >> 3; // 0..7 + int bitsInTopByte = ((bitLen - 1) & 7) + 1; // 1..8 + int currentRun = 0; + byte inspectedByte = 0; + int zeroCountInByte = 0; + int prefixZeros = 0; + int suffixZeros = 0; + int maxZeroRunInByte = 0; + int candidate = 0; + int byteIndex = msbIndex; - for (; byteIndex >= 0; byteIndex--) - { - inspectedByte = (byte)(value >> (byteIndex * 8)); - if (byteIndex == msbIndex && bitsInTopByte < 8) - { - // Mask off leading unused bits by setting them to 1 (ignore) - inspectedByte |= (byte)(0xFF << bitsInTopByte); - } + for (; byteIndex >= 0; byteIndex--) + { + inspectedByte = (byte)(value >> (byteIndex * 8)); + if (byteIndex == msbIndex && bitsInTopByte < 8) + { + // Mask off leading unused bits by setting them to 1 (ignore) + inspectedByte |= (byte)(0xFF << bitsInTopByte); + } - zeroCountInByte = ByteZeroCount[inspectedByte]; - zeroCount += zeroCountInByte; + zeroCountInByte = ByteZeroCount[inspectedByte]; + zeroCount += zeroCountInByte; - if (zeroCountInByte == 8) - { - currentRun += 8; - if (currentRun > maxZeroBlock) - { - maxZeroBlock = currentRun; + if (zeroCountInByte == 8) + { + currentRun += 8; + if (currentRun > maxZeroBlock) + { + maxZeroBlock = currentRun; } - continue; - } + continue; + } - prefixZeros = BytePrefixZero[inspectedByte]; - suffixZeros = ByteSuffixZero[inspectedByte]; - maxZeroRunInByte = ByteMaxZeroRun[inspectedByte]; + prefixZeros = BytePrefixZero[inspectedByte]; + suffixZeros = ByteSuffixZero[inspectedByte]; + maxZeroRunInByte = ByteMaxZeroRun[inspectedByte]; - candidate = currentRun + prefixZeros; - if (candidate > maxZeroBlock) - { - maxZeroBlock = candidate; - } + candidate = currentRun + prefixZeros; + if (candidate > maxZeroBlock) + { + maxZeroBlock = candidate; + } - if (maxZeroRunInByte > maxZeroBlock) - { - maxZeroBlock = maxZeroRunInByte; - } + if (maxZeroRunInByte > maxZeroBlock) + { + maxZeroBlock = maxZeroRunInByte; + } - currentRun = suffixZeros; - } + currentRun = suffixZeros; + } - if (currentRun > maxZeroBlock) - { - maxZeroBlock = currentRun; + if (currentRun > maxZeroBlock) + { + maxZeroBlock = currentRun; } } // LUTs for fast per-byte zero stats (MSB-first in each byte) private static readonly byte[] ByteZeroCount = new byte[256]; private static readonly byte[] BytePrefixZero = new byte[256]; - private static readonly byte[] ByteSuffixZero = new byte[256]; - private static readonly byte[] ByteMaxZeroRun = new byte[256]; + private static readonly byte[] ByteSuffixZero = new byte[256]; + private static readonly byte[] ByteMaxZeroRun = new byte[256]; - static Program() - { - int valueIndex = 0; - int zeros = 0; - int pref = 0; - int suff = 0; - int maxIn = 0; - int run = 0; - int bit = 0; - bool isZero = false; - int boundaryCounter = 0; - int boundaryBitIndex = 0; - for (; valueIndex < 256; valueIndex++) - { - zeros = 0; - pref = 0; - suff = 0; - maxIn = 0; - run = 0; + static Program() + { + int valueIndex = 0; + int zeros = 0; + int pref = 0; + int suff = 0; + int maxIn = 0; + int run = 0; + int bit = 0; + bool isZero = false; + int boundaryCounter = 0; + int boundaryBitIndex = 0; + for (; valueIndex < 256; valueIndex++) + { + zeros = 0; + pref = 0; + suff = 0; + maxIn = 0; + run = 0; - // MSB-first within byte: bit 7 .. bit 0 - for (bit = 7; bit >= 0; bit--) - { - isZero = ((valueIndex >> bit) & 1) == 0; - if (isZero) - { - zeros++; - run++; - if (run > maxIn) + // MSB-first within byte: bit 7 .. bit 0 + for (bit = 7; bit >= 0; bit--) + { + isZero = ((valueIndex >> bit) & 1) == 0; + if (isZero) + { + zeros++; + run++; + if (run > maxIn) { maxIn = run; } @@ -1899,52 +1900,52 @@ internal static class Program else { run = 0; - } + } - if (bit == 7) - { - // leading zeros (prefix) - boundaryCounter = 0; - for (boundaryBitIndex = 7; boundaryBitIndex >= 0; boundaryBitIndex--) - { - if (((valueIndex >> boundaryBitIndex) & 1) == 0) - { - boundaryCounter++; - } - else - { - break; - } - } - pref = boundaryCounter; - } + if (bit == 7) + { + // leading zeros (prefix) + boundaryCounter = 0; + for (boundaryBitIndex = 7; boundaryBitIndex >= 0; boundaryBitIndex--) + { + if (((valueIndex >> boundaryBitIndex) & 1) == 0) + { + boundaryCounter++; + } + else + { + break; + } + } + pref = boundaryCounter; + } - if (bit == 0) - { - // trailing zeros (suffix) - // Reusing boundaryCounter to count trailing zeros after prefix detection above. - boundaryCounter = 0; - for (boundaryBitIndex = 0; boundaryBitIndex < 8; boundaryBitIndex++) - { - if (((valueIndex >> boundaryBitIndex) & 1) == 0) - { - boundaryCounter++; - } - else - { - break; - } - } - suff = boundaryCounter; - } - } + if (bit == 0) + { + // trailing zeros (suffix) + // Reusing boundaryCounter to count trailing zeros after prefix detection above. + boundaryCounter = 0; + for (boundaryBitIndex = 0; boundaryBitIndex < 8; boundaryBitIndex++) + { + if (((valueIndex >> boundaryBitIndex) & 1) == 0) + { + boundaryCounter++; + } + else + { + break; + } + } + suff = boundaryCounter; + } + } - ByteZeroCount[valueIndex] = (byte)zeros; - BytePrefixZero[valueIndex] = (byte)pref; - ByteSuffixZero[valueIndex] = (byte)suff; - ByteMaxZeroRun[valueIndex] = (byte)maxIn; - } - } + ByteZeroCount[valueIndex] = (byte)zeros; + BytePrefixZero[valueIndex] = (byte)pref; + ByteSuffixZero[valueIndex] = (byte)suff; + ByteMaxZeroRun[valueIndex] = (byte)maxIn; + } + } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool IsCompositeByGcd(ulong p) diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs index ade797c..d0e2f76 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs @@ -478,6 +478,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester accelerator.Synchronize(); resultView.CopyToCPU(ref MemoryMarshal.GetReference(exponentSlice), computeCount); + // TODO: Can we move this somehow to GPU? MulMod64 takes half of the time in CheckDivisor for (int i = 0; i < computeCount; i++) { int position = positionSpan[i];