BuildDivisorTransitions application added.
This commit is contained in:
1 parent
97fd041ec0
commit
046cfe7d26
12 files changed
+979
-4
No files matched your search
@@ -0,0 +1,211 @@
|
||||
using System.Globalization;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
|
||||
namespace BuildDivisorTransitions;
|
||||
|
||||
internal static class Program
|
||||
{
|
||||
private const string OutputFileName = "NextKForecastTraning.csv";
|
||||
|
||||
private static int Main(string[] args)
|
||||
{
|
||||
if (args.Length != 2)
|
||||
{
|
||||
PrintUsage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
string candidatesPath = args[0];
|
||||
string divisorsPath = args[1];
|
||||
|
||||
if (!File.Exists(candidatesPath))
|
||||
{
|
||||
Console.Error.WriteLine($"Candidates file not found: {candidatesPath}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!File.Exists(divisorsPath))
|
||||
{
|
||||
Console.Error.WriteLine($"Divisors file not found: {divisorsPath}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
List<long> candidatePs = LoadCandidates(candidatesPath);
|
||||
Dictionary<long, BigInteger> divisors = LoadDivisors(divisorsPath);
|
||||
List<Transition> transitions = BuildTransitions(candidatePs, divisors);
|
||||
|
||||
WriteOutput(transitions);
|
||||
|
||||
Console.WriteLine($"Saved {transitions.Count} transitions to {OutputFileName}.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void PrintUsage()
|
||||
{
|
||||
Console.WriteLine("Usage: BuildDivisorTransitions <candidatesCsv> <divisorsCsv>");
|
||||
Console.WriteLine(" candidatesCsv: CSV with columns p,searchedMersenne,detailedCheck,passedAllTests");
|
||||
Console.WriteLine(" divisorsCsv: CSV with columns p,divisor");
|
||||
}
|
||||
|
||||
private static List<long> LoadCandidates(string path)
|
||||
{
|
||||
var result = new List<long>();
|
||||
var seen = new HashSet<long>();
|
||||
|
||||
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
using var reader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
|
||||
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
string? line = reader.ReadLine();
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string trimmedLine = line.Trim();
|
||||
if (trimmedLine.StartsWith("p", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string[] parts = trimmedLine.Split(',', StringSplitOptions.TrimEntries);
|
||||
if (parts.Length == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!long.TryParse(parts[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out long p))
|
||||
{
|
||||
throw new FormatException($"Invalid p value '{parts[0]}' in {path}.");
|
||||
}
|
||||
|
||||
if (seen.Add(p))
|
||||
{
|
||||
result.Add(p);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Dictionary<long, BigInteger> LoadDivisors(string path)
|
||||
{
|
||||
var result = new Dictionary<long, BigInteger>();
|
||||
|
||||
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
using var reader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
|
||||
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
string? line = reader.ReadLine();
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string trimmedLine = line.Trim();
|
||||
if (trimmedLine.StartsWith("p", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string[] parts = trimmedLine.Split(',', StringSplitOptions.TrimEntries);
|
||||
if (parts.Length < 2)
|
||||
{
|
||||
throw new FormatException($"Line '{line}' in {path} does not contain both p and divisor columns.");
|
||||
}
|
||||
|
||||
if (!long.TryParse(parts[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out long p))
|
||||
{
|
||||
throw new FormatException($"Invalid p value '{parts[0]}' in {path}.");
|
||||
}
|
||||
|
||||
if (!BigInteger.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out BigInteger divisor))
|
||||
{
|
||||
throw new FormatException($"Invalid divisor value '{parts[1]}' for p={p} in {path}.");
|
||||
}
|
||||
|
||||
result[p] = divisor;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<Transition> BuildTransitions(IReadOnlyList<long> candidatePs, IReadOnlyDictionary<long, BigInteger> divisors)
|
||||
{
|
||||
var transitions = new List<Transition>();
|
||||
|
||||
for (int i = 0; i < candidatePs.Count - 1; i++)
|
||||
{
|
||||
long currentP = candidatePs[i];
|
||||
long nextP = candidatePs[i + 1];
|
||||
|
||||
if (!divisors.TryGetValue(currentP, out BigInteger currentDivisor))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!divisors.TryGetValue(nextP, out BigInteger nextDivisor))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BigInteger currentK = CalculateK(currentP, currentDivisor);
|
||||
BigInteger nextK = CalculateK(nextP, nextDivisor);
|
||||
bool isNextKHigher = nextK > currentK;
|
||||
transitions.Add(new Transition(currentP, currentDivisor, LastDigit(currentP), currentK, LastMersenneDigit(currentP), isNextKHigher));
|
||||
}
|
||||
|
||||
return transitions;
|
||||
}
|
||||
|
||||
private static BigInteger CalculateK(long p, BigInteger divisor)
|
||||
{
|
||||
BigInteger numerator = divisor - BigInteger.One;
|
||||
BigInteger denominator = new BigInteger(p) << 1;
|
||||
|
||||
if (denominator <= BigInteger.Zero)
|
||||
{
|
||||
throw new InvalidOperationException($"Computed 2p is not positive for p={p}.");
|
||||
}
|
||||
|
||||
BigInteger remainder = numerator % denominator;
|
||||
if (remainder != BigInteger.Zero)
|
||||
{
|
||||
throw new InvalidOperationException($"Divisor {divisor} is not congruent to 1 mod 2p for p={p}.");
|
||||
}
|
||||
|
||||
BigInteger k = numerator / denominator;
|
||||
if (k < BigInteger.One)
|
||||
{
|
||||
throw new InvalidOperationException($"Calculated k={k} is less than 1 for p={p}, divisor={divisor}.");
|
||||
}
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
private static void WriteOutput(IReadOnlyCollection<Transition> transitions)
|
||||
{
|
||||
using var writer = new StreamWriter(OutputFileName, false, Encoding.UTF8);
|
||||
writer.WriteLine("p,divisor,lastDigit,k,lastMersenneDigit,isNextKHigher");
|
||||
|
||||
foreach (Transition transition in transitions)
|
||||
{
|
||||
writer.WriteLine($"{transition.P.ToString(CultureInfo.InvariantCulture)},{FormatBigInteger(transition.Divisor)},{transition.LastDigit},{FormatBigInteger(transition.CurrentK)},{transition.LastMersenneDigit},{(transition.IsNextKHigher ? "1" : "0")}");
|
||||
}
|
||||
}
|
||||
|
||||
private static int LastDigit(long p) => (int)(Math.Abs(p) % 10);
|
||||
|
||||
private static int LastMersenneDigit(long p)
|
||||
{
|
||||
long mod = p & 3;
|
||||
return mod == 1 ? 1 : 7; // odd primes give p%4 == 1 or 3; 2^p mod 10 cycles -> 1 or 7 after subtracting 1
|
||||
}
|
||||
|
||||
private static string FormatBigInteger(in BigInteger value) => value.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
private sealed record Transition(long P, BigInteger Divisor, int LastDigit, BigInteger CurrentK, int LastMersenneDigit, bool IsNextKHigher);
|
||||
}
|
||||
Reference in new issue
Block a user