Rescope loop variables for divisor workflows

This commit is contained in:
Marek Lemańczyk committed 2025-09-22 15:11:44 +02:00
1 parent b76fdccc1e
commit ace3fcbf34
4 files changed
+347 -275

No files matched your search

+252 -206
View File
@@ -115,11 +115,16 @@ internal static class Program
int cyclesBatchSize = 512; int cyclesBatchSize = 512;
bool continueCyclesGeneration = false; bool continueCyclesGeneration = false;
ulong divisorCyclesSearchLimit = PerfectNumberConstants.ExtraDivisorCycleSearchLimit; ulong divisorCyclesSearchLimit = PerfectNumberConstants.ExtraDivisorCycleSearchLimit;
int argIndex = 0;
string arg = string.Empty;
ReadOnlySpan<char> mersenneOption = default;
ulong parsedLimit = 0UL;
ulong parsedResidueMax = 0UL;
// NTT backend selection (GPU): reference vs staged // NTT backend selection (GPU): reference vs staged
for (int i = 0; i < args.Length; i++) for (; argIndex < args.Length; argIndex++)
{ {
string arg = args[i]; 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)) 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; showHelp = true;
@@ -127,8 +132,7 @@ internal static class Program
} }
else if (arg.StartsWith("--prime=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--prime=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); currentP = ulong.Parse(arg.AsSpan(arg.IndexOf('=') + 1));
currentP = ulong.Parse(arg.AsSpan(eq + 1));
} }
else if (arg.Equals("--increment=bit", StringComparison.OrdinalIgnoreCase)) else if (arg.Equals("--increment=bit", StringComparison.OrdinalIgnoreCase))
{ {
@@ -140,33 +144,31 @@ internal static class Program
} }
else if (arg.StartsWith("--threads=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--threads=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); threadCount = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1)));
threadCount = Math.Max(1, int.Parse(arg.AsSpan(eq + 1)));
} }
else if (arg.StartsWith("--mersenne=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--mersenne=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); mersenneOption = arg.AsSpan(arg.IndexOf('=') + 1);
ReadOnlySpan<char> value = arg.AsSpan(eq + 1); if (mersenneOption.Equals("pow2mod", StringComparison.OrdinalIgnoreCase))
if (value.Equals("pow2mod", StringComparison.OrdinalIgnoreCase))
{ {
kernelType = GpuKernelType.Pow2Mod; kernelType = GpuKernelType.Pow2Mod;
} }
else if (value.Equals("lucas", StringComparison.OrdinalIgnoreCase)) else if (mersenneOption.Equals("lucas", StringComparison.OrdinalIgnoreCase))
{ {
useLucas = true; useLucas = true;
} }
else if (value.Equals("residue", StringComparison.OrdinalIgnoreCase)) else if (mersenneOption.Equals("residue", StringComparison.OrdinalIgnoreCase))
{ {
useResidue = true; useResidue = true;
useLucas = false; useLucas = false;
} }
else if (value.Equals("divisor", StringComparison.OrdinalIgnoreCase)) else if (mersenneOption.Equals("divisor", StringComparison.OrdinalIgnoreCase))
{ {
useDivisor = true; useDivisor = true;
useLucas = false; useLucas = false;
useResidue = false; useResidue = false;
} }
else if (value.Equals("bydivisor", StringComparison.OrdinalIgnoreCase)) else if (mersenneOption.Equals("bydivisor", StringComparison.OrdinalIgnoreCase))
{ {
useByDivisor = true; useByDivisor = true;
useLucas = false; useLucas = false;
@@ -180,32 +182,27 @@ internal static class Program
} }
else if (arg.StartsWith("--divisor=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--divisor=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); divisor = UInt128.Parse(arg.AsSpan(arg.IndexOf('=') + 1));
divisor = UInt128.Parse(arg.AsSpan(eq + 1));
} }
else if (arg.StartsWith("--divisor-cycles-limit=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--divisor-cycles-limit=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); if (ulong.TryParse(arg.AsSpan(arg.IndexOf('=') + 1), out parsedLimit))
if (ulong.TryParse(arg.AsSpan(eq + 1), out var limit))
{ {
divisorCyclesSearchLimit = limit; divisorCyclesSearchLimit = parsedLimit;
} }
} }
else if (arg.StartsWith("--residue-max-k=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--residue-max-k=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); if (ulong.TryParse(arg.AsSpan(arg.IndexOf('=') + 1), out parsedResidueMax))
if (ulong.TryParse(arg.AsSpan(eq + 1), out var kmax))
{ {
residueKMax = kmax; residueKMax = parsedResidueMax;
} }
} }
// Replaces --lucas=cpu|gpu; controls device for Lucas and for // Replaces --lucas=cpu|gpu; controls device for Lucas and for
// incremental/pow2mod scanning // incremental/pow2mod scanning
else if (arg.StartsWith("--mersenne-device=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--mersenne-device=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); mersenneOnGpu = !arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase);
ReadOnlySpan<char> value = arg.AsSpan(eq + 1);
mersenneOnGpu = !value.Equals("cpu", StringComparison.OrdinalIgnoreCase);
} }
else if (arg.Equals("--workaround-mod", StringComparison.OrdinalIgnoreCase)) else if (arg.Equals("--workaround-mod", StringComparison.OrdinalIgnoreCase))
{ {
@@ -217,11 +214,11 @@ internal static class Program
} }
else if (arg.StartsWith("--order-warmup-limit=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--order-warmup-limit=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('=');
// stored and used below when initializing testers // stored and used below when initializing testers
if (ulong.TryParse(arg.AsSpan(eq + 1), out var limit)) // Reusing parsedLimit to capture the --order-warmup-limit argument.
if (ulong.TryParse(arg.AsSpan(arg.IndexOf('=') + 1), out parsedLimit))
{ {
_orderWarmupLimitOverride = limit; _orderWarmupLimitOverride = parsedLimit;
} }
} }
else if (arg.Equals("--gcd-filter", StringComparison.OrdinalIgnoreCase)) else if (arg.Equals("--gcd-filter", StringComparison.OrdinalIgnoreCase))
@@ -230,8 +227,7 @@ internal static class Program
} }
else if (arg.StartsWith("--ntt=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--ntt=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); ReadOnlySpan<char> value = arg.AsSpan(arg.IndexOf('=') + 1);
ReadOnlySpan<char> value = arg.AsSpan(eq + 1);
if (value.Equals("staged", StringComparison.OrdinalIgnoreCase)) if (value.Equals("staged", StringComparison.OrdinalIgnoreCase))
{ {
NttGpuMath.GpuTransformBackend = NttBackend.Staged; NttGpuMath.GpuTransformBackend = NttBackend.Staged;
@@ -243,8 +239,7 @@ internal static class Program
} }
else if (arg.StartsWith("--mod-reduction=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--mod-reduction=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); ReadOnlySpan<char> value = arg.AsSpan(arg.IndexOf('=') + 1);
ReadOnlySpan<char> value = arg.AsSpan(eq + 1);
if (value.Equals("mont64", StringComparison.OrdinalIgnoreCase)) if (value.Equals("mont64", StringComparison.OrdinalIgnoreCase))
{ {
NttGpuMath.ReductionMode = ModReductionMode.Mont64; NttGpuMath.ReductionMode = ModReductionMode.Mont64;
@@ -264,143 +259,119 @@ internal static class Program
} }
else if (arg.StartsWith("--primes-device=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--primes-device=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('=');
ReadOnlySpan<char> value = arg.AsSpan(eq + 1);
// Controls default device for library prime-related GPU kernels // Controls default device for library prime-related GPU kernels
// that are not explicitly parameterized (backward compatibility). // that are not explicitly parameterized (backward compatibility).
GpuContextPool.ForceCpu = value.Equals("cpu", StringComparison.OrdinalIgnoreCase); GpuContextPool.ForceCpu = arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase);
} }
// New: choose device for order computations (warm-ups and order scans) // New: choose device for order computations (warm-ups and order scans)
else if (arg.StartsWith("--order-device=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--order-device=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); orderOnGpu = !arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase);
ReadOnlySpan<char> value = arg.AsSpan(eq + 1);
orderOnGpu = !value.Equals("cpu", StringComparison.OrdinalIgnoreCase);
} }
// Optional: RLE blacklist for p // Optional: RLE blacklist for p
else if (arg.StartsWith("--rle-blacklist=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--rle-blacklist=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); _rleBlacklistPath = arg[(arg.IndexOf('=') + 1)..];
_rleBlacklistPath = arg.Substring(eq + 1);
} }
else if (arg.StartsWith("--rle-hard-max=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--rle-hard-max=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); ulong rleMaxP;
if (ulong.TryParse(arg.AsSpan(eq + 1), out var rleMaxP)) if (ulong.TryParse(arg.AsSpan(arg.IndexOf('=') + 1), out rleMaxP))
{ {
_rleHardMaxP = rleMaxP; _rleHardMaxP = rleMaxP;
} }
} }
else if (arg.StartsWith("--rle-only-last7=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--rle-only-last7=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); ReadOnlySpan<char> value = arg.AsSpan(arg.IndexOf('=') + 1);
ReadOnlySpan<char> value = arg.AsSpan(eq + 1);
_rleOnlyLast7 = !value.Equals("false", StringComparison.OrdinalIgnoreCase) && !value.Equals("0", StringComparison.OrdinalIgnoreCase); _rleOnlyLast7 = !value.Equals("false", StringComparison.OrdinalIgnoreCase) && !value.Equals("0", StringComparison.OrdinalIgnoreCase);
} }
else if (arg.StartsWith("--zero-hard=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--zero-hard=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); double zeroFraction;
if (double.TryParse(arg.AsSpan(eq + 1), out var z)) if (double.TryParse(arg.AsSpan(arg.IndexOf('=') + 1), out zeroFraction))
{ {
_zeroFracHard = z; _zeroFracHard = zeroFraction;
} }
} }
else if (arg.StartsWith("--zero-conj=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--zero-conj=", StringComparison.OrdinalIgnoreCase))
{ {
// format: <zeroFrac>:<maxZeroBlock> // format: <zeroFrac>:<maxZeroBlock>
int eq = arg.IndexOf('='); ReadOnlySpan<char> value = arg.AsSpan(arg.IndexOf('=') + 1);
ReadOnlySpan<char> value = arg.AsSpan(eq + 1);
int colon = value.IndexOf(':'); int colon = value.IndexOf(':');
if (colon > 0) if (colon > 0)
{ {
if (double.TryParse(value[..colon], out var zf) && int.TryParse(value[(colon + 1)..], out var mz)) double zeroFrac;
int maxZero;
if (double.TryParse(value[..colon], out zeroFrac) && int.TryParse(value[(colon + 1)..], out maxZero))
{ {
_zeroFracConj = zf; _zeroFracConj = zeroFrac;
_maxZeroConj = mz; _maxZeroConj = maxZero;
} }
} }
} }
// Backward-compat: accept deprecated device flags and map to new ones // Backward-compat: accept deprecated device flags and map to new ones
else if (arg.StartsWith("--lucas=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--lucas=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); mersenneOnGpu = !arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase);
ReadOnlySpan<char> value = arg.AsSpan(eq + 1);
mersenneOnGpu = !value.Equals("cpu", StringComparison.OrdinalIgnoreCase);
} }
else if (arg.StartsWith("--primes=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--primes=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); GpuContextPool.ForceCpu = arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase);
ReadOnlySpan<char> value = arg.AsSpan(eq + 1);
GpuContextPool.ForceCpu = value.Equals("cpu", StringComparison.OrdinalIgnoreCase);
} }
else if (arg.StartsWith("--gpu-kernels=", StringComparison.OrdinalIgnoreCase) || arg.StartsWith("--accelerator=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--gpu-kernels=", StringComparison.OrdinalIgnoreCase) || arg.StartsWith("--accelerator=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('=');
ReadOnlySpan<char> value = arg.AsSpan(eq + 1);
// Legacy alias: treat as primes-device for backward compat // Legacy alias: treat as primes-device for backward compat
GpuContextPool.ForceCpu = value.Equals("cpu", StringComparison.OrdinalIgnoreCase); GpuContextPool.ForceCpu = arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase);
} }
else if (arg.StartsWith("--results-dir=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--results-dir=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); _resultsDir = arg[(arg.IndexOf('=') + 1)..];
_resultsDir = arg.Substring(eq + 1);
} }
else if (arg.StartsWith("--results-prefix=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--results-prefix=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); _resultsPrefix = arg[(arg.IndexOf('=') + 1)..];
_resultsPrefix = arg.Substring(eq + 1);
} }
else if (arg.StartsWith("--gpu-prime-threads=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--gpu-prime-threads=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); gpuPrimeThreads = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1)));
gpuPrimeThreads = Math.Max(1, int.Parse(arg.AsSpan(eq + 1)));
} }
else if (arg.StartsWith("--gpu-prime-batch=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--gpu-prime-batch=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); gpuPrimeBatch = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1)));
gpuPrimeBatch = Math.Max(1, int.Parse(arg.AsSpan(eq + 1)));
} }
else if (arg.StartsWith("--ll-slice=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--ll-slice=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); sliceSize = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1)));
sliceSize = Math.Max(1, int.Parse(arg.AsSpan(eq + 1)));
} }
else if (arg.StartsWith("--gpu-scan-batch=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--gpu-scan-batch=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); scanBatchSize = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1)));
scanBatchSize = Math.Max(1, int.Parse(arg.AsSpan(eq + 1)));
} }
else if (arg.StartsWith("--block-size=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--block-size=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); blockSize = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1)));
blockSize = Math.Max(1, int.Parse(arg.AsSpan(eq + 1)));
} }
else if (arg.StartsWith("--filter-p=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--filter-p=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); filterFile = arg[(arg.IndexOf('=') + 1)..];
filterFile = arg.AsSpan(eq + 1).ToString();
} }
else if (arg.StartsWith("--write-batch-size=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--write-batch-size=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); _writeBatchSize = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1)));
_writeBatchSize = Math.Max(1, int.Parse(arg.AsSpan(eq + 1)));
} }
if (arg.StartsWith("--divisor-cycles=")) if (arg.StartsWith("--divisor-cycles="))
{ {
int eq = arg.IndexOf('='); cyclesPath = arg[(arg.IndexOf('=') + 1)..];
cyclesPath = arg.AsSpan(eq + 1).ToString();
} }
else if (arg.StartsWith("--divisor-cycles-device=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--divisor-cycles-device=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); useGpuCycles = !arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase);
ReadOnlySpan<char> value = arg.AsSpan(eq + 1);
useGpuCycles = !value.Equals("cpu", StringComparison.OrdinalIgnoreCase);
} }
else if (arg.StartsWith("--divisor-cycles-batch-size=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--divisor-cycles-batch-size=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); cyclesBatchSize = Math.Max(1, int.Parse(arg.AsSpan(arg.IndexOf('=') + 1)));
cyclesBatchSize = Math.Max(1, int.Parse(arg.AsSpan(eq + 1)));
} }
else if (arg.Equals("--divisor-cycles-continue", StringComparison.OrdinalIgnoreCase)) else if (arg.Equals("--divisor-cycles-continue", StringComparison.OrdinalIgnoreCase))
{ {
@@ -611,33 +582,35 @@ internal static class Program
HashSet<ulong> filter = []; HashSet<ulong> filter = [];
List<ulong> filterList = []; List<ulong> filterList = [];
ulong maxP = 0UL; ulong maxP = 0UL;
ulong[] localFilter = Array.Empty<ulong>();
int filterCount = 0;
if (useFilter) if (useFilter)
{ {
Console.WriteLine("Loading filter..."); Console.WriteLine("Loading filter...");
ulong[] localFilter = new ulong[1024]; localFilter = new ulong[1024];
int count = 0; filterCount = 0;
LoadResultsFile(filterFile, (p, detailedCheck, passedAllTests) => LoadResultsFile(filterFile, (p, detailedCheck, passedAllTests) =>
{ {
if (passedAllTests) if (passedAllTests)
{ {
localFilter[count++] = p; localFilter[filterCount++] = p;
if (p > maxP) if (p > maxP)
{ {
maxP = p; maxP = p;
} }
if (count == 1024) if (filterCount == 1024)
{ {
filter.AddRange(localFilter[..count]); filter.AddRange(localFilter[..filterCount]);
count = 0; filterCount = 0;
Console.WriteLine($"Added {p}"); Console.WriteLine($"Added {p}");
} }
} }
}); });
if (count > 0) if (filterCount > 0)
{ {
filter.AddRange(localFilter[..count]); filter.AddRange(localFilter[..filterCount]);
} }
filterList = [.. filter]; filterList = [.. filter];
} }
@@ -690,14 +663,16 @@ internal static class Program
_state = ((long)currentP << 3) | (long)remainder; _state = ((long)currentP << 3) | (long)remainder;
Task[] tasks = new Task[threadCount]; Task[] tasks = new Task[threadCount];
for (int i = 0; i < threadCount; i++) int taskIndex = 0;
for (; taskIndex < threadCount; taskIndex++)
{ {
tasks[i] = Task.Run(() => tasks[taskIndex] = Task.Run(() =>
{ {
int count, j; int count, j;
ulong p; ulong p;
bool passedAllTests, searchedMersenne, detailedCheck; bool passedAllTests, searchedMersenne, detailedCheck;
ulong[] buffer = new ulong[blockSize]; ulong[] buffer = new ulong[blockSize];
bool reachedMax = false;
while (!Volatile.Read(ref _limitReached)) while (!Volatile.Read(ref _limitReached))
{ {
count = ReserveBlock(buffer, blockSize); count = ReserveBlock(buffer, blockSize);
@@ -717,7 +692,7 @@ internal static class Program
} }
else else
{ {
bool reachedMax = false; reachedMax = false;
for (j = 0; j < count; j++) for (j = 0; j < count; j++)
{ {
@@ -765,32 +740,36 @@ internal static class Program
} }
List<ByDivisorPrimeState> states = new(primes.Count); List<ByDivisorPrimeState> states = new(primes.Count);
ByDivisorPrimeState stateToAdd;
bool searched = false;
bool detailed = false;
ulong allowedMax = 0UL;
_byDivisorPrecheckOnly = true; _byDivisorPrecheckOnly = true;
foreach (ulong prime in primes) foreach (ulong prime in primes)
{ {
bool passed = IsEvenPerfectCandidate(prime, divisorCyclesSearchLimit, out bool searched, out bool detailed); if (!IsEvenPerfectCandidate(prime, divisorCyclesSearchLimit, out searched, out detailed))
if (!passed)
{ {
PrintResult(prime, searched, detailed, false); PrintResult(prime, searched, detailed, false);
continue; continue;
} }
ulong allowedMax = _byDivisorTester!.GetAllowedMaxDivisor(prime); allowedMax = _byDivisorTester!.GetAllowedMaxDivisor(prime);
if (allowedMax < 3UL) if (allowedMax < 3UL)
{ {
PrintResult(prime, searchedMersenne: true, detailedCheck: true, passedAllTests: true); PrintResult(prime, searchedMersenne: true, detailedCheck: true, passedAllTests: true);
continue; continue;
} }
states.Add(new ByDivisorPrimeState stateToAdd = new ByDivisorPrimeState
{ {
Prime = prime, Prime = prime,
AllowedMax = allowedMax, AllowedMax = allowedMax,
Completed = false, Completed = false,
Composite = false, Composite = false,
DetailedCheck = false, DetailedCheck = false,
}); };
states.Add(stateToAdd);
} }
_byDivisorPrecheckOnly = false; _byDivisorPrecheckOnly = false;
@@ -807,12 +786,14 @@ internal static class Program
ulong[] allowedMaxValues = new ulong[stateCount]; ulong[] allowedMaxValues = new ulong[stateCount];
int[] stateFlags = new int[stateCount]; int[] stateFlags = new int[stateCount];
for (int i = 0; i < stateCount; i++) int stateIndex = 0;
ByDivisorPrimeState currentState;
for (; stateIndex < stateCount; stateIndex++)
{ {
var state = states[i]; currentState = states[stateIndex];
primeValues[i] = state.Prime; primeValues[stateIndex] = currentState.Prime;
allowedMaxValues[i] = state.AllowedMax; allowedMaxValues[stateIndex] = currentState.AllowedMax;
stateFlags[i] = ByDivisorStateActive; stateFlags[stateIndex] = ByDivisorStateActive;
} }
states.Clear(); states.Clear();
@@ -827,29 +808,40 @@ internal static class Program
int activeStartIndex = 0; int activeStartIndex = 0;
long[] activeStateMask = new long[(stateCount + 63) >> 6]; long[] activeStateMask = new long[(stateCount + 63) >> 6];
for (int i = 0; i < stateCount; i++) int maskStateIndex = 0;
int wordIndex = 0;
int bitIndex = 0;
for (; maskStateIndex < stateCount; maskStateIndex++)
{ {
int wordIndex = i >> 6; wordIndex = maskStateIndex >> 6;
int bitIndex = i & 63; bitIndex = maskStateIndex & 63;
activeStateMask[wordIndex] |= 1L << bitIndex; activeStateMask[wordIndex] |= 1L << bitIndex;
} }
Task[] workers = new Task[Math.Max(1, threadCount)]; Task[] workers = new Task[Math.Max(1, threadCount)];
for (int workerIndex = 0; workerIndex < workers.Length; workerIndex++) int workerIndex = 0;
for (; workerIndex < workers.Length; workerIndex++)
{ {
int capturedStateCount = stateCount;
workers[workerIndex] = Task.Run(() => workers[workerIndex] = Task.Run(() =>
{ {
using var session = _byDivisorTester!.CreateDivisorSession(); using var session = _byDivisorTester!.CreateDivisorSession();
byte[] hitsBuffer = ArrayPool<byte>.Shared.Rent(stateCount); byte[] hitsBuffer = ArrayPool<byte>.Shared.Rent(capturedStateCount);
ulong[] primeBuffer = ArrayPool<ulong>.Shared.Rent(stateCount); ulong[] primeBuffer = ArrayPool<ulong>.Shared.Rent(capturedStateCount);
int[] indexBuffer = ArrayPool<int>.Shared.Rent(stateCount); int[] indexBuffer = ArrayPool<int>.Shared.Rent(capturedStateCount);
PendingResult[] completionsBuffer = ArrayPool<PendingResult>.Shared.Rent(stateCount); PendingResult[] completionsBuffer = ArrayPool<PendingResult>.Shared.Rent(capturedStateCount);
PendingResult[] compositesBuffer = ArrayPool<PendingResult>.Shared.Rent(stateCount); PendingResult[] compositesBuffer = ArrayPool<PendingResult>.Shared.Rent(capturedStateCount);
int completionsCount = 0; int completionsCount = 0;
int compositesCount = 0; int compositesCount = 0;
ulong localDivisorCursor = 0UL; ulong localDivisorCursor = 0UL;
int localDivisorsRemaining = 0; int localDivisorsRemaining = 0;
bool exhausted = false;
ulong divisor = 0UL;
int activeCount = 0;
Span<byte> hitsSpan = default;
int hitIndex = 0;
int index = 0;
try try
{ {
@@ -863,8 +855,7 @@ internal static class Program
} }
} }
bool exhausted; divisor = AcquireNextDivisor(ref nextDivisor, divisorLimit, ref divisorsExhaustedFlag, ref finalDivisorBits, out exhausted, ref localDivisorCursor, ref localDivisorsRemaining);
ulong divisor = AcquireNextDivisor(ref nextDivisor, divisorLimit, ref divisorsExhaustedFlag, ref finalDivisorBits, out exhausted, ref localDivisorCursor, ref localDivisorsRemaining);
if (divisor == 0UL) if (divisor == 0UL)
{ {
@@ -906,7 +897,7 @@ internal static class Program
continue; continue;
} }
int activeCount = BuildPrimeBuffer(divisor, primeValues, allowedMaxValues, stateFlags, primeBuffer, indexBuffer, completionsBuffer, ref completionsCount, ref remainingStates, activeStateMask, ref activeStartIndex); activeCount = BuildPrimeBuffer(divisor, primeValues, allowedMaxValues, stateFlags, primeBuffer, indexBuffer, completionsBuffer, ref completionsCount, ref remainingStates, activeStateMask, ref activeStartIndex);
if (completionsCount > 0) if (completionsCount > 0)
{ {
@@ -918,18 +909,18 @@ internal static class Program
continue; continue;
} }
Span<byte> hitsSpan = hitsBuffer.AsSpan(0, activeCount); hitsSpan = hitsBuffer.AsSpan(0, activeCount);
hitsSpan.Clear(); hitsSpan.Clear();
session.CheckDivisor(divisor, primeBuffer.AsSpan(0, activeCount), hitsSpan); session.CheckDivisor(divisor, primeBuffer.AsSpan(0, activeCount), hitsSpan);
for (int j = 0; j < activeCount; j++) for (hitIndex = 0; hitIndex < activeCount; hitIndex++)
{ {
if (hitsSpan[j] == 0) if (hitsSpan[hitIndex] == 0)
{ {
continue; continue;
} }
int index = indexBuffer[j]; index = indexBuffer[hitIndex];
if (Interlocked.CompareExchange(ref stateFlags[index], ByDivisorStateComposite, ByDivisorStateActive) == ByDivisorStateActive) if (Interlocked.CompareExchange(ref stateFlags[index], ByDivisorStateComposite, ByDivisorStateActive) == ByDivisorStateActive)
{ {
ClearActiveMask(activeStateMask, index); ClearActiveMask(activeStateMask, index);
@@ -976,10 +967,18 @@ internal static class Program
private static ulong AcquireNextDivisor(ref ulong nextDivisor, ulong divisorLimit, ref int divisorsExhaustedFlag, ref long finalDivisorBits, out bool exhausted, ref ulong localDivisorCursor, ref int localDivisorsRemaining) 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<ulong, long>(ref nextDivisor); ref long nextDivisorBits = ref Unsafe.As<ulong, long>(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) if (localDivisorsRemaining > 0)
{ {
ulong currentValue = localDivisorCursor; currentValue = localDivisorCursor;
localDivisorCursor += 2UL; localDivisorCursor += 2UL;
localDivisorsRemaining--; localDivisorsRemaining--;
exhausted = false; exhausted = false;
@@ -994,8 +993,8 @@ internal static class Program
return 0UL; return 0UL;
} }
long currentBits = Volatile.Read(ref nextDivisorBits); currentBits = Volatile.Read(ref nextDivisorBits);
ulong currentValue = unchecked((ulong)currentBits); currentValue = unchecked((ulong)currentBits);
if (currentValue > divisorLimit) if (currentValue > divisorLimit)
{ {
if (Interlocked.CompareExchange(ref divisorsExhaustedFlag, 1, 0) == 0) if (Interlocked.CompareExchange(ref divisorsExhaustedFlag, 1, 0) == 0)
@@ -1007,27 +1006,26 @@ internal static class Program
return 0UL; return 0UL;
} }
ulong maximumNext = divisorLimit >= ulong.MaxValue - 1UL ? ulong.MaxValue : divisorLimit + 2UL; maximumNext = divisorLimit >= ulong.MaxValue - 1UL ? ulong.MaxValue : divisorLimit + 2UL;
ulong blockStride = unchecked((ulong)(DivisorAllocationBlockSize * 2)); requestedNext = currentValue > ulong.MaxValue - blockStride ? ulong.MaxValue : currentValue + blockStride;
ulong requestedNext = currentValue > ulong.MaxValue - blockStride ? ulong.MaxValue : currentValue + blockStride;
if (requestedNext > maximumNext) if (requestedNext > maximumNext)
{ {
requestedNext = maximumNext; requestedNext = maximumNext;
} }
long nextBits = unchecked((long)requestedNext); nextBits = unchecked((long)requestedNext);
if (Interlocked.CompareExchange(ref nextDivisorBits, nextBits, currentBits) != currentBits) if (Interlocked.CompareExchange(ref nextDivisorBits, nextBits, currentBits) != currentBits)
{ {
continue; continue;
} }
ulong available = requestedNext - currentValue; available = requestedNext - currentValue;
if (available == 0UL) if (available == 0UL)
{ {
continue; continue;
} }
int count = (int)(available >> 1); count = (int)(available >> 1);
if (count <= 0) if (count <= 0)
{ {
continue; continue;
@@ -1045,6 +1043,10 @@ internal static class Program
int length = primeValues.Length; int length = primeValues.Length;
int startIndex = Volatile.Read(ref activeStartIndex); int startIndex = Volatile.Read(ref activeStartIndex);
int index = startIndex; int index = startIndex;
int wordIndex = 0;
ulong word = 0UL;
int bitOffset = 0;
int candidateIndex = 0;
while (index < length && allowedMaxValues[index] < divisor) while (index < length && allowedMaxValues[index] < divisor)
{ {
@@ -1074,19 +1076,18 @@ internal static class Program
int activeCount = 0; int activeCount = 0;
while (index < length) while (index < length)
{ {
int wordIndex = index >> 6; wordIndex = index >> 6;
ulong word = unchecked((ulong)Volatile.Read(ref activeStateMask[wordIndex])); word = unchecked((ulong)Volatile.Read(ref activeStateMask[wordIndex]));
if (word == 0UL) if (word == 0UL)
{ {
index = (wordIndex + 1) << 6; index = (wordIndex + 1) << 6;
continue; continue;
} }
int bitOffset = index & 63; bitOffset = index & 63;
if (bitOffset != 0) if (bitOffset != 0)
{ {
ulong mask = ulong.MaxValue << bitOffset; word &= ulong.MaxValue << bitOffset;
word &= mask;
if (word == 0UL) if (word == 0UL)
{ {
index = (wordIndex + 1) << 6; index = (wordIndex + 1) << 6;
@@ -1096,15 +1097,13 @@ internal static class Program
while (word != 0UL) while (word != 0UL)
{ {
int bit = BitOperations.TrailingZeroCount(word); candidateIndex = (wordIndex << 6) + BitOperations.TrailingZeroCount(word);
int candidateIndex = (wordIndex << 6) + bit;
if (candidateIndex >= length) if (candidateIndex >= length)
{ {
return activeCount; return activeCount;
} }
int state = Volatile.Read(ref stateFlags[candidateIndex]); if (Volatile.Read(ref stateFlags[candidateIndex]) == ByDivisorStateActive)
if (state == ByDivisorStateActive)
{ {
primeBuffer[activeCount] = primeValues[candidateIndex]; primeBuffer[activeCount] = primeValues[candidateIndex];
indexBuffer[activeCount] = candidateIndex; indexBuffer[activeCount] = candidateIndex;
@@ -1126,38 +1125,38 @@ internal static class Program
private static void FinalizeRemainingStates(ulong[] primeValues, ulong[] allowedMaxValues, int[] stateFlags, ref int remainingStates, ref long finalDivisorBits, PendingResult[] completionsBuffer, ref int completionsCount, long[] activeStateMask) private static void FinalizeRemainingStates(ulong[] primeValues, ulong[] allowedMaxValues, int[] stateFlags, ref int remainingStates, ref long finalDivisorBits, PendingResult[] completionsBuffer, ref int completionsCount, long[] activeStateMask)
{ {
ulong finalDivisorValue = unchecked((ulong)Volatile.Read(ref finalDivisorBits)); int finalizeIndex = 0;
bool detailed = false;
for (int i = 0; i < primeValues.Length; i++) for (; finalizeIndex < primeValues.Length; finalizeIndex++)
{ {
if (Volatile.Read(ref stateFlags[i]) != ByDivisorStateActive) if (Volatile.Read(ref stateFlags[finalizeIndex]) != ByDivisorStateActive)
{ {
continue; continue;
} }
bool detailed = finalDivisorValue > allowedMaxValues[i]; detailed = unchecked((ulong)Volatile.Read(ref finalDivisorBits)) > allowedMaxValues[finalizeIndex];
int newState = detailed ? ByDivisorStateCompletedDetailed : ByDivisorStateCompleted;
if (Interlocked.CompareExchange(ref stateFlags[i], newState, ByDivisorStateActive) != ByDivisorStateActive) if (Interlocked.CompareExchange(ref stateFlags[finalizeIndex], detailed ? ByDivisorStateCompletedDetailed : ByDivisorStateCompleted, ByDivisorStateActive) != ByDivisorStateActive)
{ {
continue; continue;
} }
ClearActiveMask(activeStateMask, i); ClearActiveMask(activeStateMask, finalizeIndex);
Interlocked.Decrement(ref remainingStates); Interlocked.Decrement(ref remainingStates);
if (completionsCount == completionsBuffer.Length) if (completionsCount == completionsBuffer.Length)
{ {
FlushPendingResults(completionsBuffer, ref completionsCount); FlushPendingResults(completionsBuffer, ref completionsCount);
} }
completionsBuffer[completionsCount++] = new PendingResult(primeValues[i], detailedCheck: detailed, passedAllTests: true); completionsBuffer[completionsCount++] = new PendingResult(primeValues[finalizeIndex], detailedCheck: detailed, passedAllTests: true);
} }
} }
private static void FlushPendingResults(PendingResult[] buffer, ref int count) private static void FlushPendingResults(PendingResult[] buffer, ref int count)
{ {
for (int i = 0; i < count; i++) int flushIndex = 0;
for (; flushIndex < count; flushIndex++)
{ {
PendingResult result = buffer[i]; PendingResult result = buffer[flushIndex];
PrintResult(result.Prime, searchedMersenne: true, detailedCheck: result.DetailedCheck, passedAllTests: result.PassedAllTests); PrintResult(result.Prime, searchedMersenne: true, detailedCheck: result.DetailedCheck, passedAllTests: result.PassedAllTests);
} }
@@ -1177,8 +1176,7 @@ internal static class Program
return; return;
} }
long next = current & ~bit; if (Interlocked.CompareExchange(ref activeStateMask[wordIndex], current & ~bit, current) == current)
if (Interlocked.CompareExchange(ref activeStateMask[wordIndex], next, current) == current)
{ {
return; return;
} }
@@ -1191,6 +1189,15 @@ internal static class Program
using StreamReader reader = new(readStream); using StreamReader reader = new(readStream);
string? line; string? line;
bool headerSkipped = false; bool headerSkipped = false;
ReadOnlySpan<char> span;
int first = 0;
int second = 0;
int third = 0;
ulong parsedP = 0UL;
ReadOnlySpan<char> detailedSpan = default;
ReadOnlySpan<char> passedAllTestsSpan = default;
bool detailed = false;
bool passedAllTests = false;
while ((line = reader.ReadLine()) != null) while ((line = reader.ReadLine()) != null)
{ {
if (!headerSkipped) if (!headerSkipped)
@@ -1204,34 +1211,34 @@ internal static class Program
continue; continue;
} }
ReadOnlySpan<char> span = line.AsSpan(); span = line.AsSpan();
int first = span.IndexOf(','); first = span.IndexOf(',');
if (first < 0) if (first < 0)
{ {
continue; continue;
} }
ulong p = ulong.Parse(span[..first]); parsedP = ulong.Parse(span[..first]);
span = span[(first + 1)..]; span = span[(first + 1)..];
int second = span.IndexOf(','); second = span.IndexOf(',');
if (second < 0) if (second < 0)
{ {
continue; continue;
} }
span = span[(second + 1)..]; span = span[(second + 1)..];
int third = span.IndexOf(','); third = span.IndexOf(',');
if (third < 0) if (third < 0)
{ {
continue; continue;
} }
ReadOnlySpan<char> detailedSpan = span[..third]; detailedSpan = span[..third];
ReadOnlySpan<char> passedAllTestsSpan = span[(third + 1)..]; passedAllTestsSpan = span[(third + 1)..];
if (bool.TryParse(detailedSpan, out bool detailed) && bool.TryParse(passedAllTestsSpan, out bool passedAllTests)) if (bool.TryParse(detailedSpan, out detailed) && bool.TryParse(passedAllTestsSpan, out passedAllTests))
{ {
lineProcessorAction(p, detailed, passedAllTests); lineProcessorAction(parsedP, detailed, passedAllTests);
} }
} }
} }
@@ -1503,12 +1510,14 @@ internal static class Program
ulong primeCandidate = value; ulong primeCandidate = value;
bool advanceAdd = true; bool advanceAdd = true;
bool advancePrime = true; bool advancePrime = true;
ulong diff = 0UL;
ulong nextPrime = 0UL;
while (true) while (true)
{ {
if (advanceAdd) if (advanceAdd)
{ {
ulong diff = GetNextAddDiff(addRemainder); diff = GetNextAddDiff(addRemainder);
if (candidate > ulong.MaxValue - diff) if (candidate > ulong.MaxValue - diff)
{ {
Volatile.Write(ref _limitReached, true); Volatile.Write(ref _limitReached, true);
@@ -1526,7 +1535,6 @@ internal static class Program
if (advancePrime) if (advancePrime)
{ {
ulong nextPrime;
try try
{ {
nextPrime = PrimeIterator.Next(in primeCandidate); nextPrime = PrimeIterator.Next(in primeCandidate);
@@ -1589,10 +1597,13 @@ internal static class Program
if (_zeroFracHard >= 0 || (_zeroFracConj >= 0 && _maxZeroConj >= 0)) if (_zeroFracHard >= 0 || (_zeroFracConj >= 0 && _maxZeroConj >= 0))
{ {
double zf; double zf;
ComputeBitStats(p, out int bitLen, out int zeroCount, out int maxZeroBlock); int bitLength = 0;
int zeroCountValue = 0;
int maxZeroBlockValue = 0;
ComputeBitStats(p, out bitLength, out zeroCountValue, out maxZeroBlockValue);
if (_zeroFracHard >= 0) if (_zeroFracHard >= 0)
{ {
zf = (double)zeroCount / bitLen; zf = (double)zeroCountValue / bitLength;
if (zf > _zeroFracHard) if (zf > _zeroFracHard)
{ {
return false; return false;
@@ -1601,8 +1612,8 @@ internal static class Program
if (_zeroFracConj >= 0 && _maxZeroConj >= 0) if (_zeroFracConj >= 0 && _maxZeroConj >= 0)
{ {
zf = (double)zeroCount / bitLen; zf = (double)zeroCountValue / bitLength;
if (zf > _zeroFracConj && maxZeroBlock >= _maxZeroConj) if (zf > _zeroFracConj && maxZeroBlockValue >= _maxZeroConj)
{ {
return false; return false;
} }
@@ -1622,10 +1633,9 @@ internal static class Program
// TODO: Add deterministic Miller–Rabin rounds in GPU path for 64-bit range. // TODO: Add deterministic Miller–Rabin rounds in GPU path for 64-bit range.
if (!_useByDivisorMode) if (!_useByDivisorMode)
{ {
bool isPrimeP = GpuContextPool.ForceCpu if (!(GpuContextPool.ForceCpu
? PrimeTesters.Value!.IsPrime(p, CancellationToken.None) ? PrimeTesters.Value!.IsPrime(p, CancellationToken.None)
: PrimeTesters.Value!.IsPrimeGpu(p, CancellationToken.None); : PrimeTesters.Value!.IsPrimeGpu(p, CancellationToken.None)))
if (!isPrimeP)
{ {
_lastCompositeP = true; _lastCompositeP = true;
return false; return false;
@@ -1662,13 +1672,17 @@ internal static class Program
// Use the small prime list from PerfectNumbers.Core to the extent of sqrt(p) // Use the small prime list from PerfectNumbers.Core to the extent of sqrt(p)
var primes = PrimesGenerator.SmallPrimes; var primes = PrimesGenerator.SmallPrimes;
var primesPow2 = PrimesGenerator.SmallPrimesPow2; var primesPow2 = PrimesGenerator.SmallPrimesPow2;
int i, len = primes.Length; int len = primes.Length;
bool divisible; int primeIndex = 0;
for (i = 0; i < len; i++) bool divisible = false;
for (; primeIndex < len; primeIndex++)
{ {
if (primesPow2[i] > p) { break; } if (primesPow2[primeIndex] > p)
{
break;
}
if (tracker.MergeOrAppend(p, primes[i], out divisible) && divisible) if (tracker.MergeOrAppend(p, primes[primeIndex], out divisible) && divisible)
{ {
return true; return true;
} }
@@ -1691,20 +1705,27 @@ internal static class Program
int msbIndex = (bitLen - 1) >> 3; // 0..7 int msbIndex = (bitLen - 1) >> 3; // 0..7
int bitsInTopByte = ((bitLen - 1) & 7) + 1; // 1..8 int bitsInTopByte = ((bitLen - 1) & 7) + 1; // 1..8
int currentRun = 0; 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 (int bi = msbIndex; bi >= 0; bi--) for (; byteIndex >= 0; byteIndex--)
{ {
byte b = (byte)(value >> (bi * 8)); inspectedByte = (byte)(value >> (byteIndex * 8));
if (bi == msbIndex && bitsInTopByte < 8) if (byteIndex == msbIndex && bitsInTopByte < 8)
{ {
// Mask off leading unused bits by setting them to 1 (ignore) // Mask off leading unused bits by setting them to 1 (ignore)
b |= (byte)(0xFF << bitsInTopByte); inspectedByte |= (byte)(0xFF << bitsInTopByte);
} }
int zc = ByteZeroCount[b]; zeroCountInByte = ByteZeroCount[inspectedByte];
zeroCount += zc; zeroCount += zeroCountInByte;
if (zc == 8) if (zeroCountInByte == 8)
{ {
currentRun += 8; currentRun += 8;
if (currentRun > maxZeroBlock) if (currentRun > maxZeroBlock)
@@ -1715,22 +1736,22 @@ internal static class Program
continue; continue;
} }
int pref = BytePrefixZero[b]; prefixZeros = BytePrefixZero[inspectedByte];
int suff = ByteSuffixZero[b]; suffixZeros = ByteSuffixZero[inspectedByte];
int maxIn = ByteMaxZeroRun[b]; maxZeroRunInByte = ByteMaxZeroRun[inspectedByte];
int candidate = currentRun + pref; candidate = currentRun + prefixZeros;
if (candidate > maxZeroBlock) if (candidate > maxZeroBlock)
{ {
maxZeroBlock = candidate; maxZeroBlock = candidate;
} }
if (maxIn > maxZeroBlock) if (maxZeroRunInByte > maxZeroBlock)
{ {
maxZeroBlock = maxIn; maxZeroBlock = maxZeroRunInByte;
} }
currentRun = suff; currentRun = suffixZeros;
} }
if (currentRun > maxZeroBlock) if (currentRun > maxZeroBlock)
@@ -1747,18 +1768,28 @@ internal static class Program
static Program() static Program()
{ {
for (int v = 0; v < 256; v++) int valueIndex = 0;
{
int zeros = 0; int zeros = 0;
int pref = 0; int pref = 0;
int suff = 0; int suff = 0;
int maxIn = 0; int maxIn = 0;
int run = 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 // MSB-first within byte: bit 7 .. bit 0
for (int bit = 7; bit >= 0; bit--) for (bit = 7; bit >= 0; bit--)
{ {
bool isZero = ((v >> bit) & 1) == 0; isZero = ((valueIndex >> bit) & 1) == 0;
if (isZero) if (isZero)
{ {
zeros++; zeros++;
@@ -1776,30 +1807,45 @@ internal static class Program
if (bit == 7) if (bit == 7)
{ {
// leading zeros (prefix) // leading zeros (prefix)
int t = 0; boundaryCounter = 0;
for (int b2 = 7; b2 >= 0; b2--) for (boundaryBitIndex = 7; boundaryBitIndex >= 0; boundaryBitIndex--)
{ {
if (((v >> b2) & 1) == 0) { t++; } else { break; } if (((valueIndex >> boundaryBitIndex) & 1) == 0)
{
boundaryCounter++;
} }
pref = t; else
{
break;
}
}
pref = boundaryCounter;
} }
if (bit == 0) if (bit == 0)
{ {
// trailing zeros (suffix) // trailing zeros (suffix)
int t = 0; // Reusing boundaryCounter to count trailing zeros after prefix detection above.
for (int b2 = 0; b2 < 8; b2++) boundaryCounter = 0;
for (boundaryBitIndex = 0; boundaryBitIndex < 8; boundaryBitIndex++)
{ {
if (((v >> b2) & 1) == 0) { t++; } else { break; } if (((valueIndex >> boundaryBitIndex) & 1) == 0)
{
boundaryCounter++;
} }
suff = t; else
{
break;
}
}
suff = boundaryCounter;
} }
} }
ByteZeroCount[v] = (byte)zeros; ByteZeroCount[valueIndex] = (byte)zeros;
BytePrefixZero[v] = (byte)pref; BytePrefixZero[valueIndex] = (byte)pref;
ByteSuffixZero[v] = (byte)suff; ByteSuffixZero[valueIndex] = (byte)suff;
ByteMaxZeroRun[v] = (byte)maxIn; ByteMaxZeroRun[valueIndex] = (byte)maxIn;
} }
} }
+10 -10
View File
@@ -98,7 +98,7 @@ public static class UInt128Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsPrimeCandidate(this UInt128 n) public static bool IsPrimeCandidate(this UInt128 n)
{ {
UInt128 p, zero = UInt128.Zero; UInt128 p;
uint[] smallPrimes = PrimesGenerator.SmallPrimes; uint[] smallPrimes = PrimesGenerator.SmallPrimes;
ulong[] smallPrimesPow2 = PrimesGenerator.SmallPrimesPow2; ulong[] smallPrimesPow2 = PrimesGenerator.SmallPrimesPow2;
@@ -111,7 +111,7 @@ public static class UInt128Extensions
break; break;
} }
if (n % p == zero) if (n % p == UInt128.Zero)
{ {
return n == p; return n == p;
} }
@@ -138,8 +138,7 @@ public static class UInt128Extensions
uint mod5Value = PerfectNumbersMath.FastRemainder5(byteSum); uint mod5Value = PerfectNumbersMath.FastRemainder5(byteSum);
uint mod3Value = PerfectNumbersMath.FastRemainder3(byteSum); uint mod3Value = PerfectNumbersMath.FastRemainder3(byteSum);
ulong parity = mod8 & 1UL; mod10 = (mod8 & 1UL) == 0UL
mod10 = parity == 0UL
? mod5Value switch ? mod5Value switch
{ {
0U => 0UL, 0U => 0UL,
@@ -184,13 +183,13 @@ public static class UInt128Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static UInt128 ModPow(this UInt128 value, UInt128 exponent, UInt128 modulus) public static UInt128 ModPow(this UInt128 value, UInt128 exponent, UInt128 modulus)
{ {
UInt128 result, one = result = UInt128.One, UInt128 zero = UInt128.Zero;
zero = UInt128.Zero, UInt128 result = UInt128.One;
baseValue = value % modulus; UInt128 baseValue = value % modulus;
while (exponent != zero) while (exponent != zero)
{ {
if ((exponent & one) != zero) if ((exponent & UInt128.One) != zero)
{ {
result = MulMod(result, baseValue, modulus); result = MulMod(result, baseValue, modulus);
} }
@@ -278,11 +277,12 @@ public static class UInt128Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static UInt128 MulMod(this UInt128 a, UInt128 b, UInt128 modulus) public static UInt128 MulMod(this UInt128 a, UInt128 b, UInt128 modulus)
{ {
UInt128 zero, result = zero = UInt128.Zero, one = UInt128.One; UInt128 zero = UInt128.Zero;
UInt128 result = UInt128.Zero;
while (b != zero) while (b != zero)
{ {
if ((b & one) != zero) if ((b & UInt128.One) != zero)
{ {
result += a; result += a;
if (result >= modulus) if (result >= modulus)
+1 -2
View File
@@ -35,8 +35,7 @@ public static class UIntExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Mod10(this uint value) public static uint Mod10(this uint value)
{ {
uint quotient = (uint)(((ulong)value * 0xCCCCCCCDUL) >> 35); return value - (uint)(((ulong)value * 0xCCCCCCCDUL) >> 35) * 10U;
return value - quotient * 10U;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
+84 -57
View File
@@ -75,8 +75,7 @@ public static class ULongExtensions
uint mod5Value = PerfectNumbersMath.FastRemainder5(byteSum); uint mod5Value = PerfectNumbersMath.FastRemainder5(byteSum);
uint mod3Value = PerfectNumbersMath.FastRemainder3(byteSum); uint mod3Value = PerfectNumbersMath.FastRemainder3(byteSum);
ulong parity = mod8 & 1UL; mod10 = (mod8 & 1UL) == 0UL
mod10 = parity == 0UL
? mod5Value switch ? mod5Value switch
{ {
0U => 0UL, 0U => 0UL,
@@ -137,10 +136,9 @@ public static class ULongExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong Mod10(this ulong value) public static ulong Mod10(this ulong value)
{ {
ulong parity = value & 1UL;
ulong mod5 = value.Mod5(); ulong mod5 = value.Mod5();
if (parity == 0UL) if ((value & 1UL) == 0UL)
{ {
return mod5 switch return mod5 switch
{ {
@@ -267,20 +265,21 @@ public static class ULongExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static UInt128 PowMod(this ulong exponent, UInt128 modulus) public static UInt128 PowMod(this ulong exponent, UInt128 modulus)
{ {
UInt128 result = UInt128.One;
ulong exponentLoopIndex = 0UL;
// Return 1 because 2^0 = 1 // Return 1 because 2^0 = 1
if (exponent == 0UL) if (exponent == 0UL)
return UInt128.One; return result;
// Any number mod 1 is 0 // Any number mod 1 is 0
if (modulus == UInt128.One) if (modulus == UInt128.One)
return UInt128.Zero; return UInt128.Zero;
UInt128 result;
// For small exponents, do classic method // For small exponents, do classic method
if (exponent < 64 || modulus < 4) if (exponent < 64 || modulus < 4)
{ {
result = UInt128.One; for (; exponentLoopIndex < exponent; exponentLoopIndex++)
for (ulong i = 0; i < exponent; i++)
{ {
result <<= 1; result <<= 1;
if (result >= modulus) if (result >= modulus)
@@ -297,16 +296,18 @@ public static class ULongExtensions
return result & (modulus - 1); return result & (modulus - 1);
} }
// For general modulus, use classic left-to-right binary method for 2^exponent mod modulus // Reusing exponentLoopIndex to iterate again over the exponent range for the general-case accumulation.
UInt128 resultBin = UInt128.One; exponentLoopIndex = 0UL;
for (ulong i = 0; i < exponent; i++) // Reusing result after resetting it for the general modulus accumulation phase.
result = UInt128.One;
for (; exponentLoopIndex < exponent; exponentLoopIndex++)
{ {
resultBin <<= 1; result <<= 1;
if (resultBin >= modulus) if (result >= modulus)
resultBin -= modulus; result -= modulus;
} }
return resultBin; return result;
} }
/// <summary> /// <summary>
@@ -314,20 +315,22 @@ public static class ULongExtensions
/// </summary> /// </summary>
public static UInt128 PowModWithCycle(this ulong exponent, UInt128 modulus, ulong cycleLength) public static UInt128 PowModWithCycle(this ulong exponent, UInt128 modulus, ulong cycleLength)
{ {
UInt128 result = UInt128.One;
ulong exponentLoopIndex = 0UL;
ulong rotationCount = 0UL;
// Return 1 because 2^0 = 1 // Return 1 because 2^0 = 1
if (exponent == 0UL) if (exponent == 0UL)
return UInt128.One; return result;
// Any number mod 1 is 0 // Any number mod 1 is 0
if (modulus == UInt128.One) if (modulus == UInt128.One)
return UInt128.Zero; return UInt128.Zero;
// For small exponents, do classic method // For small exponents, do classic method
UInt128 result;
if (exponent < 64 || modulus < 4) if (exponent < 64 || modulus < 4)
{ {
result = UInt128.One; for (; exponentLoopIndex < exponent; exponentLoopIndex++)
for (ulong i = 0; i < exponent; i++)
{ {
result <<= 1; result <<= 1;
if (result >= modulus) if (result >= modulus)
@@ -344,10 +347,12 @@ public static class ULongExtensions
return result & (modulus - 1); return result & (modulus - 1);
} }
// For general modulus, use classic left-to-right binary method for 2^exponent mod modulus // Reusing exponentLoopIndex to iterate over the rotation count for the cycle-aware path.
exponentLoopIndex = 0UL;
// Reusing result after resetting it for the rotation-accumulation pass.
result = UInt128.One; result = UInt128.One;
UInt128 rotations = exponent % cycleLength; rotationCount = exponent % cycleLength;
for (ulong i = 0; i < rotations; i++) for (; exponentLoopIndex < rotationCount; exponentLoopIndex++)
{ {
result <<= 1; result <<= 1;
if (result >= modulus) if (result >= modulus)
@@ -362,20 +367,23 @@ public static class ULongExtensions
/// </summary> /// </summary>
public static UInt128 PowModWithCycle(this ulong exponent, UInt128 modulus, UInt128 cycleLength) public static UInt128 PowModWithCycle(this ulong exponent, UInt128 modulus, UInt128 cycleLength)
{ {
UInt128 result = UInt128.One;
ulong exponentLoopIndex = 0UL;
UInt128 rotationIndex = UInt128.Zero;
UInt128 rotationCount = UInt128.Zero;
// Return 1 because 2^0 = 1 // Return 1 because 2^0 = 1
if (exponent == UInt128.Zero) if (exponent == UInt128.Zero)
return UInt128.One; return result;
// Any number mod 1 is 0 // Any number mod 1 is 0
if (modulus == UInt128.One) if (modulus == UInt128.One)
return UInt128.Zero; return UInt128.Zero;
// For small exponents, do classic method // For small exponents, do classic method
UInt128 result;
if (exponent < UInt128Numbers.SixtyFour || modulus < UInt128Numbers.Four) if (exponent < UInt128Numbers.SixtyFour || modulus < UInt128Numbers.Four)
{ {
result = UInt128.One; for (; exponentLoopIndex < exponent; exponentLoopIndex++)
for (ulong i = 0; i < exponent; i++)
{ {
result <<= 1; result <<= 1;
if (result >= modulus) if (result >= modulus)
@@ -392,14 +400,17 @@ public static class ULongExtensions
return result & (modulus - 1); return result & (modulus - 1);
} }
// For general modulus, use classic left-to-right binary method for 2^exponent mod modulus // Reusing result after resetting it for the rotation-driven accumulation phase.
result = UInt128.One; result = UInt128.One;
UInt128 rotations = exponent % cycleLength; rotationCount = exponent % cycleLength;
for (UInt128 i = 0; i < rotations; i++) rotationIndex = UInt128.Zero;
while (rotationIndex < rotationCount)
{ {
result <<= 1; result <<= 1;
if (result >= modulus) if (result >= modulus)
result -= modulus; result -= modulus;
rotationIndex += UInt128.One;
} }
return result; return result;
@@ -410,21 +421,23 @@ public static class ULongExtensions
/// </summary> /// </summary>
public static UInt128 PowModWithCycle(this UInt128 exponent, UInt128 modulus, UInt128 cycleLength) public static UInt128 PowModWithCycle(this UInt128 exponent, UInt128 modulus, UInt128 cycleLength)
{ {
UInt128 result = UInt128.One;
ulong exponentLoopIndex = 0UL;
UInt128 rotationIndex = UInt128.Zero;
UInt128 rotationCount = UInt128.Zero;
// Return 1 because 2^0 = 1 // Return 1 because 2^0 = 1
if (exponent == UInt128.Zero) if (exponent == UInt128.Zero)
return UInt128.One; return result;
// Any number mod 1 is 0 // Any number mod 1 is 0
if (modulus == UInt128.One) if (modulus == UInt128.One)
return UInt128.Zero; return UInt128.Zero;
UInt128 result;
// For small exponents, do classic method // For small exponents, do classic method
if (exponent < UInt128Numbers.SixtyFour || modulus < UInt128Numbers.Four) if (exponent < UInt128Numbers.SixtyFour || modulus < UInt128Numbers.Four)
{ {
result = UInt128.One; for (; exponentLoopIndex < exponent; exponentLoopIndex++)
for (ulong i = 0; i < exponent; i++)
{ {
result <<= 1; result <<= 1;
if (result >= modulus) if (result >= modulus)
@@ -441,14 +454,17 @@ public static class ULongExtensions
return result & (modulus - 1); return result & (modulus - 1);
} }
// For general modulus, use classic left-to-right binary method for 2^exponent mod modulus // Reusing result after resetting it for the rotation-driven accumulation phase.
result = UInt128.One; result = UInt128.One;
UInt128 rotations = exponent % cycleLength; rotationCount = exponent % cycleLength;
for (UInt128 i = 0; i < rotations; i++) rotationIndex = UInt128.Zero;
while (rotationIndex < rotationCount)
{ {
result <<= 1; result <<= 1;
if (result >= modulus) if (result >= modulus)
result -= modulus; result -= modulus;
rotationIndex += UInt128.One;
} }
return result; return result;
@@ -465,14 +481,17 @@ public static class ULongExtensions
// Use cycle length 4 for mod 10 // Use cycle length 4 for mod 10
UInt128 result = PowModWithCycle(exponent, 10, 4); UInt128 result = PowModWithCycle(exponent, 10, 4);
UInt128 currentModulus = 10; UInt128 currentModulus = 10;
for (UInt128 m = 11; m <= modulus; m++) UInt128 modulusCandidate = 11;
UInt128 cycle = UInt128.Zero;
UInt128 remainderForCandidate = UInt128.Zero;
for (; modulusCandidate <= modulus; modulusCandidate++)
{ {
UInt128 cycle = MersenneDivisorCycles.GetCycle(m); cycle = MersenneDivisorCycles.GetCycle(modulusCandidate);
UInt128 remM = cycle > 0 remainderForCandidate = cycle > UInt128.Zero
? PowModWithCycle(exponent, m, cycle) ? PowModWithCycle(exponent, modulusCandidate, cycle)
: PowMod(exponent, m); : PowMod(exponent, modulusCandidate);
// Solve x ≡ result mod currentModulus // Solve x ≡ result mod currentModulus
// x ≡ remM mod m // x ≡ remM mod m
@@ -480,10 +499,8 @@ public static class ULongExtensions
// Since currentModulus and m are coprime, use CRT: // Since currentModulus and m are coprime, use CRT:
// x = result + currentModulus * t, where t ≡ (remM - result) * inv(currentModulus, m) mod m // x = result + currentModulus * t, where t ≡ (remM - result) * inv(currentModulus, m) mod m
UInt128 inv = ModInverse(currentModulus, m); result += currentModulus * ((remainderForCandidate + modulusCandidate - (result % modulusCandidate)) * ModInverse(currentModulus, modulusCandidate) % modulusCandidate);
UInt128 t = ((remM + m - (result % m)) * inv) % m; currentModulus *= modulusCandidate;
result += currentModulus * t;
currentModulus *= m;
if (currentModulus >= modulus) if (currentModulus >= modulus)
break; break;
@@ -495,20 +512,30 @@ public static class ULongExtensions
// Helper: modular inverse (extended Euclidean algorithm) // Helper: modular inverse (extended Euclidean algorithm)
private static UInt128 ModInverse(UInt128 a, UInt128 m) private static UInt128 ModInverse(UInt128 a, UInt128 m)
{ {
UInt128 m0 = m, t, q; UInt128 m0 = m, temp;
UInt128 x0 = 0, x1 = 1; UInt128 x0 = 0, x1 = 1;
if (m == 1) return 0; UInt128 originalA = UInt128.Zero;
while (a > 1) UInt128 originalM = UInt128.Zero;
if (m == 1)
{ {
q = a / m; return 0;
t = m;
m = a % m; a = t;
t = x0;
x0 = x1 - q * x0;
x1 = t;
} }
if (x1 < 0) x1 += m0; while (a > 1)
{
originalA = a;
originalM = m;
m = originalA % originalM;
a = originalM;
temp = x0;
x0 = x1 - (originalA / originalM) * x0;
x1 = temp;
}
if (x1 < 0)
{
x1 += m0;
}
return x1; return x1;
} }