From 22be048c15f49768c8a948763fb501d1a63f7cf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Fri, 19 Sep 2025 23:58:57 +0200 Subject: [PATCH] Tiny cleanup --- .../Program.cs | 1041 ++++++++--------- 1 file changed, 517 insertions(+), 524 deletions(-) diff --git a/EvenPerfectBitScanner.ResultsParser/Program.cs b/EvenPerfectBitScanner.ResultsParser/Program.cs index 28ba604..68c078b 100644 --- a/EvenPerfectBitScanner.ResultsParser/Program.cs +++ b/EvenPerfectBitScanner.ResultsParser/Program.cs @@ -22,11 +22,11 @@ internal static class Program private const string DefaultHeader = "p,searchedMersenne,detailedCheck,passedAllTests"; - private static int Main(string[] args) - { - if (args.Length == 0) - { - PrintHelp(); + private static int Main(string[] args) + { + if (args.Length == 0) + { + PrintHelp(); return 1; } @@ -49,149 +49,149 @@ internal static class Program return 1; } - if (!TryParseProcessingArguments( - args.AsSpan(1), - out ulong pMin, - out ulong pMax, - out int threadCount, - out int bufferSize)) - { - return 1; - } + if (!TryParseProcessingArguments( + args.AsSpan(1), + out ulong pMin, + out ulong pMax, + out int threadCount, + out int bufferSize)) + { + return 1; + } - if (pMin > pMax) - { - Console.Error.WriteLine("The --p-min value cannot be greater than --p-max."); - return 1; - } + if (pMin > pMax) + { + Console.Error.WriteLine("The --p-min value cannot be greater than --p-max."); + return 1; + } - if (threadCount < 1) - { - Console.Error.WriteLine("The --threads value must be a positive integer."); - return 1; - } + if (threadCount < 1) + { + Console.Error.WriteLine("The --threads value must be a positive integer."); + return 1; + } - if (bufferSize < 1) - { - Console.Error.WriteLine("The --buffer-size value must be a positive integer."); - return 1; - } + if (bufferSize < 1) + { + Console.Error.WriteLine("The --buffer-size value must be a positive integer."); + return 1; + } - try - { - ProcessFile(inputPath, pMin, pMax, threadCount, bufferSize); - return 0; - } - catch (Exception exception) - { - Console.Error.WriteLine($"Processing failed: {exception.Message}"); + try + { + ProcessFile(inputPath, pMin, pMax, threadCount, bufferSize); + return 0; + } + catch (Exception exception) + { + Console.Error.WriteLine($"Processing failed: {exception.Message}"); return 1; } } private static bool IsHelpOption(string value) => HelpOptions.Contains(value); - private static bool TryParseProcessingArguments( - ReadOnlySpan arguments, - out ulong pMin, - out ulong pMax, - out int threadCount, - out int bufferSize) - { - pMin = 0UL; - pMax = ulong.MaxValue; - threadCount = Environment.ProcessorCount; - bufferSize = 4096; + private static bool TryParseProcessingArguments( + ReadOnlySpan arguments, + out ulong pMin, + out ulong pMax, + out int threadCount, + out int bufferSize) + { + pMin = 0UL; + pMax = ulong.MaxValue; + threadCount = Environment.ProcessorCount; + bufferSize = 4096; - if (arguments.Length == 0) - { - return true; - } + if (arguments.Length == 0) + { + return true; + } - int index = 0; - while (index < arguments.Length) - { - string argument = arguments[index]; - if (!TryReadOptionValue(arguments, ref index, out string option, out string value)) - { - return false; - } + int index = 0; + while (index < arguments.Length) + { + string argument = arguments[index]; + if (!TryReadOptionValue(arguments, ref index, out string option, out string value)) + { + return false; + } - try - { - switch (option) - { - case "--p-min": - pMin = ParseRangeValue(option, value); - break; - case "--p-max": - pMax = ParseRangeValue(option, value); - break; - case "--threads": - threadCount = ParsePositiveInt(option, value); - break; - case "--buffer-size": - bufferSize = ParsePositiveInt(option, value); - break; - default: - Console.Error.WriteLine($"Unsupported option '{option}'."); - PrintHelp(); - return false; - } - } - catch (FormatException exception) - { - Console.Error.WriteLine(exception.Message); - PrintHelp(); - return false; - } - } + try + { + switch (option) + { + case "--p-min": + pMin = ParseRangeValue(option, value); + break; + case "--p-max": + pMax = ParseRangeValue(option, value); + break; + case "--threads": + threadCount = ParsePositiveInt(option, value); + break; + case "--buffer-size": + bufferSize = ParsePositiveInt(option, value); + break; + default: + Console.Error.WriteLine($"Unsupported option '{option}'."); + PrintHelp(); + return false; + } + } + catch (FormatException exception) + { + Console.Error.WriteLine(exception.Message); + PrintHelp(); + return false; + } + } - return true; - } + return true; + } - private static ulong ParseRangeValue(in string option, in string value) - { - if (!ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out ulong parsed)) - { - throw new FormatException($"Value '{value}' for option '{option}' is not a valid non-negative integer."); - } + private static ulong ParseRangeValue(in string option, in string value) + { + if (!ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out ulong parsed)) + { + throw new FormatException($"Value '{value}' for option '{option}' is not a valid non-negative integer."); + } - return parsed; - } + return parsed; + } - private static int ParsePositiveInt(in string option, in string value) - { - if (!int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out int parsed) || parsed < 1) - { - throw new FormatException($"Value '{value}' for option '{option}' is not a valid positive integer."); - } + private static int ParsePositiveInt(in string option, in string value) + { + if (!int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out int parsed) || parsed < 1) + { + throw new FormatException($"Value '{value}' for option '{option}' is not a valid positive integer."); + } - return parsed; - } + return parsed; + } - private static bool TryReadOptionValue( - ReadOnlySpan arguments, - ref int index, - out string option, - out string value) - { - option = string.Empty; - value = string.Empty; - string current = arguments[index]; - int equalsIndex = current.IndexOf('='); - if (equalsIndex <= 0 || equalsIndex == current.Length - 1) - { - Console.Error.WriteLine($"Option '{current}' must use the --name=value syntax."); - PrintHelp(); - return false; - } + private static bool TryReadOptionValue( + ReadOnlySpan arguments, + ref int index, + out string option, + out string value) + { + option = string.Empty; + value = string.Empty; + string current = arguments[index]; + int equalsIndex = current.IndexOf('='); + if (equalsIndex <= 0 || equalsIndex == current.Length - 1) + { + Console.Error.WriteLine($"Option '{current}' must use the --name=value syntax."); + PrintHelp(); + return false; + } - option = current[..equalsIndex]; - value = current[(equalsIndex + 1)..]; - index++; - return true; - } + option = current[..equalsIndex]; + value = current[(equalsIndex + 1)..]; + index++; + return true; + } private static void PrintHelp() { @@ -199,88 +199,89 @@ internal static class Program Console.WriteLine("Parses CSV results produced by the candidate scanner."); Console.WriteLine(); Console.WriteLine("Usage:"); - Console.WriteLine( - " EvenPerfectBitScanner.ResultsParser [--p-min=] [--p-max=] [--threads=] [--buffer-size=]"); - Console.WriteLine(); - Console.WriteLine("The program accepts the following help switches:"); - Console.WriteLine(" --?, -?, /?, -help, --help, --h, -h, /h, /help"); - Console.WriteLine(); - Console.WriteLine("Optional range arguments allow filtering primes by exponent:"); - Console.WriteLine(" --p-min= - include entries with p >= value"); - Console.WriteLine(" --p-max= - include entries with p <= value"); - Console.WriteLine(); - Console.WriteLine("Parallel processing options:"); - Console.WriteLine(" --threads= - number of worker threads used for parsing and splitting (default: logical cores)"); - Console.WriteLine(" --buffer-size= - number of records read into each parsing batch (default: 4096)"); - Console.WriteLine("Values must be provided using the --option=value syntax."); - Console.WriteLine(); - Console.WriteLine("Outputs prime results into four files placed next to the source file:"); + Console.WriteLine( + " EvenPerfectBitScanner.ResultsParser [--p-min=] [--p-max=] [--threads=] [--buffer-size=]"); + Console.WriteLine(); + Console.WriteLine("The program accepts the following help switches:"); + Console.WriteLine(" --?, -?, /?, -help, --help, --h, -h, /h, /help"); + Console.WriteLine(); + Console.WriteLine("Optional range arguments allow filtering primes by exponent:"); + Console.WriteLine(" --p-min= - include entries with p >= value"); + Console.WriteLine(" --p-max= - include entries with p <= value"); + Console.WriteLine(); + Console.WriteLine("Parallel processing options:"); + Console.WriteLine(" --threads= - number of worker threads used for parsing and splitting (default: logical cores)"); + Console.WriteLine(" --buffer-size= - number of records read into each parsing batch (default: 4096)"); + Console.WriteLine("Values must be provided using the --option=value syntax."); + Console.WriteLine(); + Console.WriteLine("Outputs prime results into four files placed next to the source file:"); Console.WriteLine(" raw-primes- - prime entries in the original order"); Console.WriteLine(" sorted-primes- - prime entries sorted by p"); Console.WriteLine(" sorted-primes-passed- - sorted prime entries with passedAllTests = true"); Console.WriteLine(" sorted-primes-rejected- - sorted prime entries with passedAllTests = false"); Console.WriteLine(); Console.WriteLine("Example:"); - Console.WriteLine(" EvenPerfectBitScanner.ResultsParser results.csv --p-min=89 --p-max=107"); + Console.WriteLine(" EvenPerfectBitScanner.ResultsParser results.csv --p-min=89 --p-max=107"); } - private static void ProcessFile(in string inputPath, ulong pMin, ulong pMax, int threadCount, int bufferSize) - { - using StreamReader reader = new(inputPath); - string? headerLine = reader.ReadLine(); - if (headerLine is null) - { - Console.WriteLine("Input file did not contain any data. Created empty outputs using the default header."); - string fallbackHeader = DefaultHeader; - string rawFallbackPath = BuildOutputPath(inputPath, "raw-primes-"); - WriteCsv(rawFallbackPath, fallbackHeader, File.Exists(rawFallbackPath), []); - CreateEmptyOutputs(inputPath, fallbackHeader); - return; - } + private static void ProcessFile(in string inputPath, ulong pMin, ulong pMax, int threadCount, int bufferSize) + { + using StreamReader reader = new(inputPath); + string? headerLine = reader.ReadLine(); + if (headerLine is null) + { + Console.WriteLine("Input file did not contain any data. Created empty outputs using the default header."); + string fallbackHeader = DefaultHeader; + string rawFallbackPath = BuildOutputPath(inputPath, "raw-primes-"); + WriteCsv(rawFallbackPath, fallbackHeader, File.Exists(rawFallbackPath), []); + CreateEmptyOutputs(inputPath, fallbackHeader); + return; + } - string header = string.IsNullOrWhiteSpace(headerLine) ? DefaultHeader : headerLine.Trim(); + string header = string.IsNullOrWhiteSpace(headerLine) ? DefaultHeader : headerLine.Trim(); - List candidates = LoadCandidates(reader, pMin, pMax, threadCount, bufferSize); + List candidates = LoadCandidates(reader, pMin, pMax, threadCount, bufferSize); + Console.WriteLine("Sorting..."); + List sortedPrimeResults = ExtractSortedPrimeCandidates(candidates); + Console.WriteLine("Building primes list..."); + List primeResults = BuildRawPrimeResults(candidates, sortedPrimeResults); - List sortedPrimeResults = ExtractSortedPrimeCandidates(candidates); - List primeResults = BuildRawPrimeResults(candidates, sortedPrimeResults); + string rawOutputPath = BuildOutputPath(inputPath, "raw-primes-"); + bool append = File.Exists(rawOutputPath); + WriteCsv(rawOutputPath, header, append, primeResults); - string rawOutputPath = BuildOutputPath(inputPath, "raw-primes-"); - bool append = File.Exists(rawOutputPath); - WriteCsv(rawOutputPath, header, append, primeResults); + if (sortedPrimeResults.Count == 0) + { + Console.WriteLine("No prime entries were found in the input file."); + Console.WriteLine($"Created an empty prime snapshot at: {rawOutputPath}"); + CreateEmptyOutputs(inputPath, header); + return; + } - if (sortedPrimeResults.Count == 0) - { - Console.WriteLine("No prime entries were found in the input file."); - Console.WriteLine($"Created an empty prime snapshot at: {rawOutputPath}"); - CreateEmptyOutputs(inputPath, header); - return; - } + Console.WriteLine("Sorting candidates..."); + SortByPrime(sortedPrimeResults); - Console.WriteLine("Sorting candidates..."); - SortByPrime(sortedPrimeResults); + Console.WriteLine("Splitting results..."); + SplitResults(sortedPrimeResults, threadCount, out List passedResults, out List rejectedResults); - Console.WriteLine("Splitting results..."); - SplitResults(sortedPrimeResults, threadCount, out List passedResults, out List rejectedResults); + Console.WriteLine("Saving files..."); + string sortedOutputPath = BuildOutputPath(inputPath, "sorted-primes-"); + string passedOutputPath = BuildOutputPath(inputPath, "sorted-primes-passed-"); + string rejectedOutputPath = BuildOutputPath(inputPath, "sorted-primes-rejected-"); - Console.WriteLine("Saving files..."); - string sortedOutputPath = BuildOutputPath(inputPath, "sorted-primes-"); - string passedOutputPath = BuildOutputPath(inputPath, "sorted-primes-passed-"); - string rejectedOutputPath = BuildOutputPath(inputPath, "sorted-primes-rejected-"); + append = File.Exists(sortedOutputPath); + WriteCsv(sortedOutputPath, header, append, sortedPrimeResults); + append = File.Exists(passedOutputPath); + WriteCsv(passedOutputPath, header, append, passedResults); + append = File.Exists(rejectedOutputPath); + WriteCsv(rejectedOutputPath, header, append, rejectedResults); - append = File.Exists(sortedOutputPath); - WriteCsv(sortedOutputPath, header, append, sortedPrimeResults); - append = File.Exists(passedOutputPath); - WriteCsv(passedOutputPath, header, append, passedResults); - append = File.Exists(rejectedOutputPath); - WriteCsv(rejectedOutputPath, header, append, rejectedResults); - - Console.WriteLine($"Processed {sortedPrimeResults.Count} prime entr{(sortedPrimeResults.Count == 1 ? "y" : "ies")}."); - Console.WriteLine($"Prime results (raw order): {rawOutputPath}"); - Console.WriteLine($"Prime results (sorted): {sortedOutputPath}"); - Console.WriteLine($"Prime results (passed): {passedOutputPath}"); - Console.WriteLine($"Prime results (rejected): {rejectedOutputPath}"); - } + Console.WriteLine($"Processed {sortedPrimeResults.Count} prime entr{(sortedPrimeResults.Count == 1 ? "y" : "ies")}."); + Console.WriteLine($"Prime results (raw order): {rawOutputPath}"); + Console.WriteLine($"Prime results (sorted): {sortedOutputPath}"); + Console.WriteLine($"Prime results (passed): {passedOutputPath}"); + Console.WriteLine($"Prime results (rejected): {rejectedOutputPath}"); + } private static void CreateEmptyOutputs(in string inputPath, in string header) { @@ -326,392 +327,384 @@ internal static class Program return Path.Combine(directory, prefixedName); } - private static void WriteCsv(in string path, in string header, bool append, IEnumerable entries) - { - using StreamWriter writer = new(path, append, Encoding.UTF8); - if (!append) - { - writer.WriteLine(header); - } + private static void WriteCsv(in string path, in string header, bool append, IEnumerable entries) + { + using StreamWriter writer = new(path, append, Encoding.UTF8); + if (!append) + { + writer.WriteLine(header); + } - foreach (CandidateResult entry in entries) - { - writer.WriteLine(entry.Csv); - } - } + foreach (CandidateResult entry in entries) + { + writer.WriteLine(entry.Csv); + } + } - private static List LoadCandidates( - StreamReader reader, - ulong pMin, - ulong pMax, - int threadCount, - int bufferSize) - { - List candidates = []; - if (bufferSize < 1) - { - return candidates; - } + private static List LoadCandidates( + StreamReader reader, + ulong pMin, + ulong pMax, + int threadCount, + int bufferSize) + { + List candidates = []; + if (bufferSize < 1) + { + return candidates; + } - int workerLimit = Math.Max(1, threadCount); - List> activeChunks = new(workerLimit); - Dictionary completedChunks = new(); - int nextChunkToFlush = 0; - int consoleProgress = 0; - ulong lastReportedCandidate = 0UL; + int workerLimit = Math.Max(1, threadCount); + List> activeChunks = new(workerLimit); + Dictionary completedChunks = new(); + int nextChunkToFlush = 0; + int consoleProgress = 0; + ulong lastReportedCandidate = 0UL; - string[] buffer = new string[bufferSize]; - int buffered = 0; - int chunkIndex = 0; + string[] buffer = new string[bufferSize]; + int buffered = 0; + int chunkIndex = 0; - void FlushChunk(in ChunkResult chunk) - { - if (chunk.Count == 0) - { - return; - } + void FlushChunk(in ChunkResult chunk) + { + if (chunk.Count == 0) + { + return; + } - CandidateResult[] items = chunk.Items; - int count = chunk.Count; - candidates.EnsureCapacity(candidates.Count + count); - for (int i = 0; i < count; i++) - { - CandidateResult candidate = items[i]; - candidates.Add(candidate); - lastReportedCandidate = candidate.P; - if (++consoleProgress == 10_000) - { - Console.WriteLine($"Processed {lastReportedCandidate}"); - consoleProgress = 0; - } - } - } + CandidateResult[] items = chunk.Items; + int count = chunk.Count; + candidates.EnsureCapacity(candidates.Count + count); + for (int i = 0; i < count; i++) + { + CandidateResult candidate = items[i]; + candidates.Add(candidate); + lastReportedCandidate = candidate.P; + if (++consoleProgress == 10_000) + { + Console.WriteLine($"Processed {lastReportedCandidate}"); + consoleProgress = 0; + } + } + } - void TryFlush() - { - while (completedChunks.Remove(nextChunkToFlush, out ChunkResult ready)) - { - FlushChunk(in ready); - nextChunkToFlush++; - } - } + void TryFlush() + { + while (completedChunks.Remove(nextChunkToFlush, out ChunkResult ready)) + { + FlushChunk(in ready); + nextChunkToFlush++; + } + } - void WaitForAny() - { - Task completed = Task.WhenAny(activeChunks).GetAwaiter().GetResult(); - activeChunks.Remove(completed); - ChunkResult chunk = completed.GetAwaiter().GetResult(); - completedChunks.Add(chunk.Index, chunk); - TryFlush(); - } + void WaitForAny() + { + Task completed = Task.WhenAny(activeChunks).GetAwaiter().GetResult(); + activeChunks.Remove(completed); + ChunkResult chunk = completed.GetAwaiter().GetResult(); + completedChunks.Add(chunk.Index, chunk); + TryFlush(); + } - void ScheduleChunk(string[] chunkLines) - { - int currentIndex = chunkIndex++; - Task task = Task.Run(() => ProcessChunk(chunkLines, currentIndex, pMin, pMax)); - activeChunks.Add(task); - if (activeChunks.Count == workerLimit) - { - WaitForAny(); - } - } + void ScheduleChunk(string[] chunkLines) + { + int currentIndex = chunkIndex++; + Task task = Task.Run(() => ProcessChunk(chunkLines, currentIndex, pMin, pMax)); + activeChunks.Add(task); + if (activeChunks.Count == workerLimit) + { + WaitForAny(); + } + } - void DispatchBuffer(int length) - { - string[] chunkLines; - if (length == buffer.Length) - { - chunkLines = buffer; - buffer = new string[bufferSize]; - } - else - { - chunkLines = new string[length]; - Array.Copy(buffer, chunkLines, length); - } + void DispatchBuffer(int length) + { + string[] chunkLines; + if (length == buffer.Length) + { + chunkLines = buffer; + buffer = new string[bufferSize]; + } + else + { + chunkLines = new string[length]; + Array.Copy(buffer, chunkLines, length); + } - buffered = 0; - ScheduleChunk(chunkLines); - } + buffered = 0; + ScheduleChunk(chunkLines); + } - string? line; - while ((line = reader.ReadLine()) is not null) - { - if (string.IsNullOrWhiteSpace(line)) - { - continue; - } + string? line; + while ((line = reader.ReadLine()) is not null) + { + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } - buffer[buffered++] = line; - if (buffered == bufferSize) - { - DispatchBuffer(buffered); - } - } + buffer[buffered++] = line; + if (buffered == bufferSize) + { + DispatchBuffer(buffered); + } + } - if (buffered > 0) - { - DispatchBuffer(buffered); - } + if (buffered > 0) + { + DispatchBuffer(buffered); + } - while (activeChunks.Count > 0) - { - WaitForAny(); - } + while (activeChunks.Count > 0) + { + WaitForAny(); + } - TryFlush(); - return candidates; - } + TryFlush(); + return candidates; + } - private static ChunkResult ProcessChunk(string[] lines, int index, ulong pMin, ulong pMax) - { - CandidateResult[] items = new CandidateResult[lines.Length]; - int count = 0; - ulong pEmpty = CandidateResult.Empty.P; - for (int i = 0; i < lines.Length; i++) - { - string line = lines[i]; - if (string.IsNullOrWhiteSpace(line)) - { - continue; - } + private static ChunkResult ProcessChunk(string[] lines, int index, ulong pMin, ulong pMax) + { + CandidateResult[] items = new CandidateResult[lines.Length]; + int count = 0; + ulong pEmpty = CandidateResult.Empty.P; + for (int i = 0; i < lines.Length; i++) + { + string line = lines[i]; + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } - CandidateResult candidate = ParseLine(line, pMin, pMax); - if (candidate.P == pEmpty) - { - continue; - } + CandidateResult candidate = ParseLine(line, pMin, pMax); + if (candidate.P == pEmpty) + { + continue; + } - items[count++] = candidate; - } + items[count++] = candidate; + } - return new ChunkResult(index, items, count); - } + return new ChunkResult(index, items, count); + } - private static List ExtractSortedPrimeCandidates(List candidates) - { - if (candidates.Count == 0) - { - return []; - } + private static List ExtractSortedPrimeCandidates(List candidates) + { + if (candidates.Count == 0) + { + return []; + } - List sortedCandidates = [.. candidates]; - sortedCandidates.Sort(CandidateResultComparer.Instance); + List sortedCandidates = [.. candidates]; + sortedCandidates.Sort(CandidateResultComparer.Instance); - List primeCandidates = []; - using IEnumerator primeEnumerator = Prime.Numbers.GetEnumerator(); - if (!primeEnumerator.MoveNext()) - { - throw new InvalidOperationException("Prime generator did not provide any values."); - } + List primeCandidates = []; + using IEnumerator primeEnumerator = Prime.Numbers.GetEnumerator(); + if (!primeEnumerator.MoveNext()) + { + throw new InvalidOperationException("Prime generator did not provide any values."); + } - ulong currentPrime = primeEnumerator.Current; - foreach (CandidateResult candidate in sortedCandidates) - { - ulong candidateP = candidate.P; - while (currentPrime < candidateP) - { - if (!primeEnumerator.MoveNext()) - { - return primeCandidates; - } + ulong currentPrime = primeEnumerator.Current; + foreach (CandidateResult candidate in sortedCandidates) + { + ulong candidateP = candidate.P; + while (currentPrime < candidateP) + { + if (!primeEnumerator.MoveNext()) + { + return primeCandidates; + } - currentPrime = primeEnumerator.Current; - } + currentPrime = primeEnumerator.Current; + } - if (currentPrime == candidateP) - { - primeCandidates.Add(candidate); - } - } + if (currentPrime == candidateP) + { + primeCandidates.Add(candidate); + } + } - return primeCandidates; - } + return primeCandidates; + } - private static List BuildRawPrimeResults( - List candidates, - List sortedPrimeResults) - { - int primeCount = sortedPrimeResults.Count; - if (primeCount == 0) - { - return []; - } + private static List BuildRawPrimeResults( + List candidates, + List sortedPrimeResults) + { + int primeCount = sortedPrimeResults.Count; + if (primeCount == 0) + { + return []; + } - Dictionary remainingCounts = new(primeCount); - for (int i = 0; i < primeCount; i++) - { - CandidateResult candidate = sortedPrimeResults[i]; - if (remainingCounts.TryGetValue(candidate.P, out int count)) - { - remainingCounts[candidate.P] = count + 1; - continue; - } + Dictionary remainingCounts = new(primeCount); + for (int i = 0; i < primeCount; i++) + { + CandidateResult candidate = sortedPrimeResults[i]; + if (remainingCounts.TryGetValue(candidate.P, out int count)) + { + remainingCounts[candidate.P] = count + 1; + continue; + } - remainingCounts.Add(candidate.P, 1); - } + remainingCounts.Add(candidate.P, 1); + } - List rawPrimes = new(primeCount); - for (int i = 0; i < candidates.Count; i++) - { - CandidateResult candidate = candidates[i]; - if (!remainingCounts.TryGetValue(candidate.P, out int count)) - { - continue; - } + List rawPrimes = new(primeCount); + for (int i = 0; i < candidates.Count; i++) + { + CandidateResult candidate = candidates[i]; + if (!remainingCounts.TryGetValue(candidate.P, out int count)) + { + continue; + } - rawPrimes.Add(candidate); - if (count == 1) - { - remainingCounts.Remove(candidate.P); - continue; - } + rawPrimes.Add(candidate); + if (count == 1) + { + remainingCounts.Remove(candidate.P); + continue; + } - remainingCounts[candidate.P] = count - 1; - } + remainingCounts[candidate.P] = count - 1; + } - return rawPrimes; - } + return rawPrimes; + } - private static void SortByPrime(List results) - { - if (results.Count < 2) - { - return; - } + private static void SortByPrime(List results) + { + if (results.Count < 2) + { + return; + } - results.Sort(CandidateResultComparer.Instance); - } + results.Sort(CandidateResultComparer.Instance); + } - private static void SplitResults( - List sortedResults, - int threadCount, - out List passedResults, - out List rejectedResults) - { - int count = sortedResults.Count; - if (count == 0) - { - passedResults = []; - rejectedResults = []; - return; - } + private static void SplitResults( + List sortedResults, + int threadCount, + out List passedResults, + out List rejectedResults) + { + int count = sortedResults.Count; + if (count == 0) + { + passedResults = []; + rejectedResults = []; + return; + } - if (threadCount <= 1 || count < threadCount * 4) - { - passedResults = new List(count); - rejectedResults = new List(count); - for (int i = 0; i < count; i++) - { - CandidateResult candidate = sortedResults[i]; - if (candidate.PassedAllTests) - { - passedResults.Add(candidate); - continue; - } + if (threadCount <= 1 || count < threadCount * 4) + { + passedResults = new List(count); + rejectedResults = new List(count); + for (int i = 0; i < count; i++) + { + CandidateResult candidate = sortedResults[i]; + if (candidate.PassedAllTests) + { + passedResults.Add(candidate); + continue; + } - rejectedResults.Add(candidate); - } + rejectedResults.Add(candidate); + } - return; - } + return; + } - int workerCount = Math.Min(threadCount, count); - List[] passedSegments = new List[workerCount]; - List[] rejectedSegments = new List[workerCount]; - Task[] tasks = new Task[workerCount]; + int workerCount = Math.Min(threadCount, count); + List[] passedSegments = new List[workerCount]; + List[] rejectedSegments = new List[workerCount]; + Task[] tasks = new Task[workerCount]; - int start = 0; - int baseChunk = count / workerCount; - int remainder = count % workerCount; - for (int worker = 0; worker < workerCount; worker++) - { - int length = baseChunk + (worker < remainder ? 1 : 0); - int chunkStart = start; - start += length; + int start = 0; + int baseChunk = count / workerCount; + int remainder = count % workerCount; + for (int worker = 0; worker < workerCount; worker++) + { + int length = baseChunk + (worker < remainder ? 1 : 0); + int chunkStart = start; + start += length; - if (length == 0) - { - passedSegments[worker] = []; - rejectedSegments[worker] = []; - tasks[worker] = Task.CompletedTask; - continue; - } + if (length == 0) + { + passedSegments[worker] = []; + rejectedSegments[worker] = []; + tasks[worker] = Task.CompletedTask; + continue; + } - tasks[worker] = Task.Run(() => - { - List localPassed = new(length); - List localRejected = new(length); - int end = chunkStart + length; - for (int i = chunkStart; i < end; i++) - { - CandidateResult candidate = sortedResults[i]; - if (candidate.PassedAllTests) - { - localPassed.Add(candidate); - continue; - } + tasks[worker] = Task.Run(() => + { + List localPassed = new(length); + List localRejected = new(length); + int end = chunkStart + length; + for (int i = chunkStart; i < end; i++) + { + CandidateResult candidate = sortedResults[i]; + if (candidate.PassedAllTests) + { + localPassed.Add(candidate); + continue; + } - localRejected.Add(candidate); - } + localRejected.Add(candidate); + } - passedSegments[worker] = localPassed; - rejectedSegments[worker] = localRejected; - }); - } + passedSegments[worker] = localPassed; + rejectedSegments[worker] = localRejected; + }); + } - Task.WaitAll(tasks); + Task.WaitAll(tasks); - passedResults = new List(count); - rejectedResults = new List(count); - for (int worker = 0; worker < workerCount; worker++) - { - if (passedSegments[worker].Count > 0) - { - passedResults.AddRange(passedSegments[worker]); - } + passedResults = new List(count); + rejectedResults = new List(count); + for (int worker = 0; worker < workerCount; worker++) + { + if (passedSegments[worker].Count > 0) + { + passedResults.AddRange(passedSegments[worker]); + } - if (rejectedSegments[worker].Count > 0) - { - rejectedResults.AddRange(rejectedSegments[worker]); - } - } - } + if (rejectedSegments[worker].Count > 0) + { + rejectedResults.AddRange(rejectedSegments[worker]); + } + } + } - private sealed class CandidateResultComparer : IComparer - { - public static readonly CandidateResultComparer Instance = new(); + private sealed class CandidateResultComparer : IComparer + { + public static readonly CandidateResultComparer Instance = new(); - public int Compare(CandidateResult x, CandidateResult y) - { - if (x.P == y.P) - { - return 0; - } + public int Compare(CandidateResult x, CandidateResult y) => x.P == y.P ? 0 : x.P < y.P ? -1 : 1; + } - return x.P < y.P ? -1 : 1; - } - } + private readonly struct ChunkResult + { + public readonly int Index; + public readonly CandidateResult[] Items; + public readonly int Count; - private readonly struct ChunkResult - { - public readonly int Index; - public readonly CandidateResult[] Items; - public readonly int Count; + public ChunkResult(int index, CandidateResult[] items, int count) + { + Index = index; + Items = items; + Count = count; + } + } - public ChunkResult(int index, CandidateResult[] items, int count) - { - Index = index; - Items = items; - Count = count; - } - } + private readonly record struct CandidateResult(ulong P, bool PassedAllTests, string Csv) + { + public static readonly CandidateResult Empty = new(0UL, false, ""); - private readonly record struct CandidateResult(ulong P, bool PassedAllTests, string Csv) - { - public static readonly CandidateResult Empty = new(0UL, false, ""); - - public readonly string Csv = Csv; + public readonly string Csv = Csv; public readonly ulong P = P; public readonly bool PassedAllTests = PassedAllTests; }