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;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user