Add inclusive exponent range filters to results parser

This commit is contained in:
Marek Lemańczyk committed 2025-09-19 13:47:56 +02:00
1 parent cc4a6f6bed
commit 3a64478aab
1 file changed
+145 -69
+145 -69
View File
@@ -21,76 +21,147 @@ internal static class Program
private const string DefaultHeader = "p,searchedMersenne,detailedCheck,isPerfect"; private const string DefaultHeader = "p,searchedMersenne,detailedCheck,isPerfect";
private static int Main(string[] args) private static int Main(string[] args)
{ {
if (args.Length == 0) if (args.Length == 0)
{ {
PrintHelp(); PrintHelp();
return 1; return 1;
} }
if (args.Length == 1 && IsHelpOption(args[0])) if (args.Length == 1 && IsHelpOption(args[0]))
{ {
PrintHelp(); PrintHelp();
return 0; return 0;
} }
if (args.Length != 1) string inputPath = args[0];
{ if (IsHelpOption(inputPath))
Console.Error.WriteLine("Only a single CSV file path is supported."); {
PrintHelp(); PrintHelp();
return 1; return 0;
} }
string inputPath = args[0]; if (!File.Exists(inputPath))
{
Console.Error.WriteLine($"Input file '{inputPath}' was not found.");
return 1;
}
if (!File.Exists(inputPath)) if (!TryParseRangeArguments(args.AsSpan(1), out ulong pMin, out ulong pMax))
{ {
Console.Error.WriteLine($"Input file '{inputPath}' was not found."); return 1;
return 1; }
}
try if (pMin > pMax)
{ {
ProcessFile(inputPath); Console.Error.WriteLine("The --p-min value cannot be greater than --p-max.");
return 0; return 1;
} }
catch (Exception exception)
{ try
Console.Error.WriteLine($"Processing failed: {exception.Message}"); {
ProcessFile(inputPath, pMin, pMax);
return 0;
}
catch (Exception exception)
{
Console.Error.WriteLine($"Processing failed: {exception.Message}");
return 1; return 1;
} }
} }
private static bool IsHelpOption(string value) => HelpOptions.Contains(value); private static bool IsHelpOption(string value) => HelpOptions.Contains(value);
private static void PrintHelp() private static bool TryParseRangeArguments(ReadOnlySpan<string> arguments, out ulong pMin, out ulong pMax)
{ {
Console.WriteLine("EvenPerfectBitScanner.ResultsParser"); pMin = 0UL;
Console.WriteLine("Parses CSV results produced by the candidate scanner."); pMax = ulong.MaxValue;
Console.WriteLine(); if (arguments.Length == 0)
Console.WriteLine("Usage:"); {
Console.WriteLine(" EvenPerfectBitScanner.ResultsParser <results.csv>"); return true;
Console.WriteLine(); }
Console.WriteLine("The program accepts the following help switches:");
Console.WriteLine(" --?, -?, /?, -help, --help, --h, -h, /h, /help"); if ((arguments.Length & 1) == 1)
Console.WriteLine(); {
Console.WriteLine("Outputs prime results into four files placed next to the source file:"); Console.Error.WriteLine("Each range option must be followed by a numeric value.");
Console.WriteLine(" raw-primes-<name> - prime entries in the original order"); PrintHelp();
Console.WriteLine(" sorted-primes-<name> - prime entries sorted by p"); return false;
Console.WriteLine(" sorted-primes-passed-<name> - sorted prime entries with isPerfect = true"); }
Console.WriteLine(" sorted-primes-rejected-<name> - sorted prime entries with isPerfect = false");
for (int i = 0; i < arguments.Length; i += 2)
{
string option = arguments[i];
string value = arguments[i + 1];
try
{
switch (option)
{
case "--p-min":
pMin = ParseRangeValue(option, value);
break;
case "--p-max":
pMax = ParseRangeValue(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;
}
private static ulong ParseRangeValue(string option, 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;
}
private static void PrintHelp()
{
Console.WriteLine("EvenPerfectBitScanner.ResultsParser");
Console.WriteLine("Parses CSV results produced by the candidate scanner.");
Console.WriteLine();
Console.WriteLine("Usage:");
Console.WriteLine(" EvenPerfectBitScanner.ResultsParser <results.csv> [--p-min <value>] [--p-max <value>]");
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 <value> - include entries with p >= value");
Console.WriteLine(" --p-max <value> - include entries with p <= value");
Console.WriteLine();
Console.WriteLine("Outputs prime results into four files placed next to the source file:");
Console.WriteLine(" raw-primes-<name> - prime entries in the original order");
Console.WriteLine(" sorted-primes-<name> - prime entries sorted by p");
Console.WriteLine(" sorted-primes-passed-<name> - sorted prime entries with isPerfect = true");
Console.WriteLine(" sorted-primes-rejected-<name> - sorted prime entries with isPerfect = false");
Console.WriteLine(); Console.WriteLine();
Console.WriteLine("Example:"); Console.WriteLine("Example:");
Console.WriteLine(" EvenPerfectBitScanner.ResultsParser results.csv"); Console.WriteLine(" EvenPerfectBitScanner.ResultsParser results.csv --p-min 89 --p-max 107");
} }
private static void ProcessFile(string inputPath) private static void ProcessFile(string inputPath, ulong pMin, ulong pMax)
{ {
using IEnumerator<string> enumerator = File.ReadLines(inputPath).GetEnumerator(); using IEnumerator<string> enumerator = File.ReadLines(inputPath).GetEnumerator();
if (!enumerator.MoveNext()) if (!enumerator.MoveNext())
{ {
Console.WriteLine("Input file did not contain any data. Created empty outputs using the default header."); Console.WriteLine("Input file did not contain any data. Created empty outputs using the default header.");
string fallbackHeader = DefaultHeader; string fallbackHeader = DefaultHeader;
string rawFallbackPath = BuildOutputPath(inputPath, "raw-primes-"); string rawFallbackPath = BuildOutputPath(inputPath, "raw-primes-");
WriteCsv(rawFallbackPath, fallbackHeader, Array.Empty<CandidateResult>()); WriteCsv(rawFallbackPath, fallbackHeader, Array.Empty<CandidateResult>());
@@ -111,20 +182,25 @@ internal static class Program
List<CandidateResult> pendingResults = new(); List<CandidateResult> pendingResults = new();
List<CandidateResult> primeResults = new(); List<CandidateResult> primeResults = new();
int consoleProgress = 0; int consoleProgress = 0;
while (enumerator.MoveNext()) while (enumerator.MoveNext())
{ {
string currentLine = enumerator.Current; string currentLine = enumerator.Current;
if (string.IsNullOrWhiteSpace(currentLine)) if (string.IsNullOrWhiteSpace(currentLine))
{ {
continue; continue;
} }
CandidateResult result = ParseLine(currentLine); CandidateResult result = ParseLine(currentLine);
InsertPendingResult(pendingResults, result); if (result.P < pMin || result.P > pMax)
DrainPrimeMatches(pendingResults, primeResults, ref currentPrime, primeEnumerator, false); {
if (++consoleProgress == 10_000) continue;
{ }
Console.WriteLine($"Processed {result.P}");
InsertPendingResult(pendingResults, result);
DrainPrimeMatches(pendingResults, primeResults, ref currentPrime, primeEnumerator, false);
if (++consoleProgress == 10_000)
{
Console.WriteLine($"Processed {result.P}");
consoleProgress = 0; consoleProgress = 0;
} }
} }