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