Predictive scanning implemented for bydivisor method.

This commit is contained in:
mlemanczyk committed 2026-01-03 20:01:22 +01:00
1 parent ef8034b258
commit a6d408f2aa
10 files changed
+1562 -40

No files matched your search

@@ -0,0 +1,344 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace PerfectNumbers.Core.ByDivisor;
public sealed class ByDivisorClassEntry
{
public double K50 { get; set; }
public double K75 { get; set; }
public double GapProbability { get; set; }
public int SampleCount { get; set; }
public int GapCount { get; set; }
public string Stability { get; set; } = "unknown";
}
public sealed class ByDivisorScanPlan
{
public double CheapLimit { get; init; }
public double Start { get; init; }
public double Target { get; init; }
public double Rho1 { get; init; }
public double Rho2 { get; init; }
}
public sealed class ByDivisorScanTuning
{
public double CheapLimit { get; init; } = 10_000d;
public double Gamma50 { get; init; } = 0.8d;
public double Gamma75 { get; init; } = 1.2d;
public double AggressiveGamma50 { get; init; } = 1.0d;
public double AggressiveGamma75 { get; init; } = 1.4d;
public double Rho1 { get; init; } = 1.35d;
public double Rho2 { get; init; } = 1.6d;
public double AggressiveRho2 { get; init; } = 1.8d;
public double HardGapThreshold { get; init; } = 0.75d;
[JsonIgnore]
public static ByDivisorScanTuning Default => new();
}
public sealed class ByDivisorClassModel
{
private const int ClassCount = 1024;
public int Version { get; set; } = 1;
public double CheapLimit { get; set; } = 10_000d;
public ByDivisorClassEntry[] Entries { get; set; } = Enumerable.Range(0, ClassCount)
.Select(_ => new ByDivisorClassEntry { K50 = 1d, K75 = 1d })
.ToArray();
public ByDivisorScanPlan BuildPlan(ulong prime, BigInteger allowedMax, in ByDivisorScanTuning tuning)
{
int suffix = (int)(prime & 1023UL);
ByDivisorClassEntry entry = Entries[suffix];
double k50 = entry.K50 > 1d ? entry.K50 : 1d;
double k75 = entry.K75 > 1d ? entry.K75 : Math.Max(k50, 1d);
bool isHard = entry.GapProbability >= tuning.HardGapThreshold || string.Equals(entry.Stability, "escaping", StringComparison.OrdinalIgnoreCase);
double gamma50 = isHard ? tuning.AggressiveGamma50 : tuning.Gamma50;
double gamma75 = isHard ? tuning.AggressiveGamma75 : tuning.Gamma75;
double start = Math.Ceiling(Math.Max(1d, gamma50 * k50));
double target = Math.Ceiling(Math.Max(start + 1d, gamma75 * k75));
double cheap = tuning.CheapLimit > 1d ? tuning.CheapLimit : CheapLimit;
double rho2 = isHard ? tuning.AggressiveRho2 : tuning.Rho2;
double maxKAllowed = ComputeMaxKAllowed(prime, allowedMax);
if (maxKAllowed > 0d)
{
cheap = Math.Min(cheap, maxKAllowed);
start = Math.Min(start, maxKAllowed);
target = Math.Min(target, maxKAllowed);
}
return new ByDivisorScanPlan
{
CheapLimit = cheap,
Start = start,
Target = target,
Rho1 = tuning.Rho1,
Rho2 = rho2,
};
}
private static double ComputeMaxKAllowed(ulong prime, in BigInteger allowedMax)
{
if (prime == 0UL || allowedMax.IsZero)
{
return 0d;
}
BigInteger step = ((BigInteger)prime) << 1;
if (step.IsZero)
{
return 0d;
}
BigInteger maxK = (allowedMax - BigInteger.One) / step;
if (maxK <= BigInteger.Zero)
{
return 0d;
}
double value = (double)maxK;
return double.IsInfinity(value) || value <= 0d ? 0d : value;
}
}
public static class ByDivisorClassModelLoader
{
public static ByDivisorClassModel LoadOrBuild(string modelPath, string resultsPath, in ByDivisorScanTuning tuning, double cheapLimit, bool rebuildWhenResultsNewer = true)
{
DateTime modelTimestamp = File.Exists(modelPath) ? File.GetLastWriteTimeUtc(modelPath) : DateTime.MinValue;
DateTime resultsTimestamp = File.Exists(resultsPath) ? File.GetLastWriteTimeUtc(resultsPath) : DateTime.MinValue;
if (File.Exists(modelPath) && (!rebuildWhenResultsNewer || resultsTimestamp <= modelTimestamp))
{
try
{
ByDivisorClassModel? loaded = JsonSerializer.Deserialize<ByDivisorClassModel>(File.ReadAllText(modelPath));
if (loaded is not null)
{
loaded.CheapLimit = cheapLimit;
return loaded;
}
}
catch
{
// fall back to rebuild
}
}
ByDivisorClassModel model = BuildFromResults(resultsPath, cheapLimit);
Save(modelPath, model);
return model;
}
public static void RebuildAndSave(string modelPath, string resultsPath, double cheapLimit)
{
ByDivisorClassModel model = BuildFromResults(resultsPath, cheapLimit);
Save(modelPath, model);
}
private static void Save(string modelPath, ByDivisorClassModel model)
{
string? directory = Path.GetDirectoryName(modelPath);
if (!string.IsNullOrEmpty(directory))
{
Directory.CreateDirectory(directory);
}
var options = new JsonSerializerOptions
{
WriteIndented = true,
};
string json = JsonSerializer.Serialize(model, options);
string tempPath = modelPath + ".tmp";
File.WriteAllText(tempPath, json);
File.Copy(tempPath, modelPath, overwrite: true);
File.Delete(tempPath);
}
private static ByDivisorClassModel BuildFromResults(string resultsPath, double cheapLimit)
{
var model = new ByDivisorClassModel
{
CheapLimit = cheapLimit,
};
if (!File.Exists(resultsPath))
{
return model;
}
var samples = new List<double>[1024];
Span<int> sampleCounts = stackalloc int[1024];
Span<int> gapCounts = stackalloc int[1024];
using FileStream stream = new(resultsPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using StreamReader reader = new(stream);
string? line;
bool headerSkipped = false;
while ((line = reader.ReadLine()) is not null)
{
if (!headerSkipped)
{
headerSkipped = true;
continue;
}
if (string.IsNullOrWhiteSpace(line))
{
continue;
}
string[] parts = line.Split(',');
if (parts.Length < 5)
{
continue;
}
if (!ulong.TryParse(parts[0], NumberStyles.None, CultureInfo.InvariantCulture, out ulong p))
{
continue;
}
if (!BigInteger.TryParse(parts[1], NumberStyles.None, CultureInfo.InvariantCulture, out BigInteger divisor))
{
continue;
}
if (!bool.TryParse(parts[3], out bool detailedCheck))
{
detailedCheck = false;
}
if (!bool.TryParse(parts[4], out bool passedAllTests))
{
passedAllTests = false;
}
int suffix = (int)(p & 1023UL);
bool validDivisor = divisor > BigInteger.One && IsValidDivisor(p, divisor);
if (validDivisor)
{
double kValue = ComputeK(divisor, p);
if (!double.IsNaN(kValue) && !double.IsInfinity(kValue))
{
samples[suffix] ??= new List<double>();
samples[suffix]!.Add(kValue);
}
}
if (detailedCheck)
{
sampleCounts[suffix]++;
if (!validDivisor || passedAllTests)
{
gapCounts[suffix]++;
}
}
}
for (int i = 0; i < 1024; i++)
{
List<double>? classSamples = samples[i];
double k50 = 1d;
double k75 = 1d;
if (classSamples is not null && classSamples.Count > 0)
{
classSamples.Sort();
k50 = ComputePercentile(classSamples, 0.5d);
k75 = ComputePercentile(classSamples, 0.75d);
}
int total = sampleCounts[i];
int gaps = gapCounts[i];
double gapProb = total > 0 ? (double)gaps / total : 0d;
model.Entries[i] = new ByDivisorClassEntry
{
K50 = k50,
K75 = k75,
GapProbability = gapProb,
SampleCount = total,
GapCount = gaps,
Stability = model.Entries[i].Stability,
};
}
return model;
}
private static bool IsValidDivisor(ulong prime, in BigInteger divisor)
{
if (divisor <= BigInteger.One)
{
return false;
}
BigInteger modulus = ((BigInteger)prime) << 1;
if (modulus.IsZero)
{
return false;
}
if ((divisor - BigInteger.One) % modulus != BigInteger.Zero)
{
return false;
}
return BigInteger.ModPow(2, prime, divisor) == BigInteger.One;
}
private static double ComputeK(in BigInteger divisor, ulong prime)
{
BigInteger numerator = divisor - BigInteger.One;
BigInteger denominator = ((BigInteger)prime) << 1;
if (denominator.IsZero)
{
return 0d;
}
BigInteger k = numerator / denominator;
if (k <= BigInteger.Zero)
{
return 0d;
}
double value = (double)k;
if (double.IsInfinity(value) || double.IsNaN(value) || value <= 0d)
{
return double.MaxValue;
}
return value;
}
private static double ComputePercentile(List<double> values, double percentile)
{
if (values.Count == 0)
{
return 1d;
}
double index = (values.Count - 1) * percentile;
int lower = (int)Math.Floor(index);
int upper = (int)Math.Ceiling(index);
if (lower == upper)
{
return values[lower];
}
double fraction = index - lower;
return values[lower] + ((values[upper] - values[lower]) * fraction);
}
}
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Collections.Concurrent;
using PerfectNumbers.Core.Gpu;
using PerfectNumbers.Core.Gpu.Accelerators;
using PerfectNumbers.Core.ByDivisor;
namespace PerfectNumbers.Core.Cpu;
@@ -13,6 +14,7 @@ public enum DivisorSet
{
OneByOne,
Pow2Groups,
Predictive,
}
[EnumDependentTemplate(typeof(DivisorSet))]
@@ -71,19 +73,24 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsMersenneValue(ulong prime, in BigInteger divisor)
{
if (divisor <= BigInteger.One || prime == 0UL || prime > int.MaxValue)
{
return false;
}
int divisorBitLength = GetBitLengthPortable(divisor);
if (divisorBitLength != (int)prime)
if (divisor <= BigInteger.One || prime == 0UL)
{
return false;
}
BigInteger plusOne = divisor + BigInteger.One;
return plusOne > BigInteger.One && IsPowerOfTwoBig(plusOne);
if (plusOne <= BigInteger.One || !IsPowerOfTwoBig(plusOne))
{
return false;
}
int exponentBits = GetBitLengthPortable(plusOne) - 1;
if (exponentBits <= 0)
{
return false;
}
return prime == (ulong)exponentBits;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -331,8 +338,36 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
step,
out processedAll,
out foundDivisor);
// Console.WriteLine($"[by-divisor-plan] p={prime} suffix={(prime & 1023UL)} using pow2-groups plan; model skipped.");
return compositePow2;
#else
#if DivisorSet_Predictive
ByDivisorClassModel? classifier = EnvironmentConfiguration.ByDivisorClassModel;
if (classifier is not null)
{
bool compositePlanned = TryExecutePlannedScan(
classifier,
prime,
allowedMax,
normalizedMinK,
out processedAll,
out foundDivisor);
if (compositePlanned)
{
return true;
}
if (processedAll)
{
return false;
}
}
else
{
Console.WriteLine($"[by-divisor-plan] p={prime} suffix={(prime & 1023UL)} model unavailable, falling back to sequential scan.");
}
#endif
if (fits64 && composite64)
{
allowedMax64 = unlimited ? ulong.MaxValue : (ulong)allowedMax;
@@ -556,6 +591,400 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
}
#endif
#if !DivisorSet_Pow2Groups && DivisorSet_Predictive
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
private bool TryExecutePlannedScan(
ByDivisorClassModel model,
ulong prime,
BigInteger allowedMax,
BigInteger minK,
out bool processedAll,
out BigInteger foundDivisor)
{
processedAll = false;
foundDivisor = BigInteger.Zero;
if (minK > ulong.MaxValue)
{
// Console.WriteLine($"[by-divisor-plan] p={prime} using BigInteger plan (minK={minK}).");
return TryExecutePlannedScanBig(model, prime, allowedMax, minK, out processedAll, out foundDivisor);
}
ulong allowedMax64;
if (allowedMax.IsZero)
{
allowedMax64 = ulong.MaxValue;
}
else if (allowedMax > ulong.MaxValue)
{
// Console.WriteLine($"[by-divisor-plan] p={prime} using BigInteger plan (allowedMax={allowedMax}).");
return TryExecutePlannedScanBig(model, prime, allowedMax, minK, out processedAll, out foundDivisor);
}
else
{
allowedMax64 = (ulong)allowedMax;
}
bool composite = TryExecutePlannedScan64(
model,
prime,
allowedMax64,
(ulong)minK,
out processedAll,
out ulong foundDivisor64);
if (composite)
{
foundDivisor = foundDivisor64;
return true;
}
if (processedAll)
{
foundDivisor = BigInteger.Zero;
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
private bool TryExecutePlannedScanBig(
ByDivisorClassModel model,
ulong prime,
in BigInteger allowedMax,
in BigInteger minK,
out bool processedAll,
out BigInteger foundDivisor)
{
processedAll = true;
foundDivisor = BigInteger.Zero;
if (allowedMax.IsZero)
{
processedAll = false;
return false;
}
BigInteger step = ((BigInteger)prime) << 1;
BigInteger maxKAllowed = (allowedMax - BigInteger.One) / step;
TryClampMaxKForMersenneTail(prime, step, ref maxKAllowed);
if (maxKAllowed <= BigInteger.Zero || minK > maxKAllowed)
{
processedAll = false;
return false;
}
ByDivisorScanTuning tuning = EnvironmentConfiguration.ByDivisorScanTuning;
ByDivisorScanPlan plan = model.BuildPlan(prime, allowedMax, tuning);
int suffix = (int)(prime & 1023UL);
// ByDivisorClassEntry entry = model.Entries[suffix];
// Console.WriteLine($"[by-divisor-plan] p={prime} suffix={suffix} k50={entry.K50:F2} k75={entry.K75:F2} gap={entry.GapProbability:F3} cheap<= {plan.CheapLimit:F0} start={plan.Start:F0} target={plan.Target:F0} rho1={plan.Rho1:F2} rho2={plan.Rho2:F2} maxK={maxKAllowed}");
BigInteger cheapLimit = BigInteger.Min((BigInteger)plan.CheapLimit, maxKAllowed);
if (cheapLimit >= minK)
{
BigInteger currentK = minK;
bool compositeCheap = CheckDivisorsBigPlanRange(
prime,
step,
allowedMax,
minK,
cheapLimit,
out bool processedRange,
out BigInteger foundRange,
ref currentK);
if (compositeCheap)
{
foundDivisor = foundRange;
return true;
}
if (!processedRange)
{
processedAll = false;
return false;
}
}
BigInteger startPlan = plan.Start <= 0d ? minK : new BigInteger(Math.Ceiling(plan.Start));
if (startPlan < minK)
{
startPlan = minK;
}
BigInteger target = plan.Target <= 0d ? startPlan + BigInteger.One : new BigInteger(Math.Ceiling(plan.Target));
if (target < startPlan)
{
target = startPlan + BigInteger.One;
}
BigInteger current = cheapLimit + BigInteger.One;
if (current < startPlan)
{
current = startPlan;
}
bool loggedProgressive = false;
while (current <= maxKAllowed)
{
if (!loggedProgressive)
{
// Console.WriteLine($"[by-divisor-progressive] p={prime} startK={current} target={target} rho={(current < target ? plan.Rho1 : plan.Rho2):F2} maxK={maxKAllowed}");
loggedProgressive = true;
}
BigInteger startRange = current;
BigInteger endRange = current;
bool composite = CheckDivisorsBigPlanRange(
prime,
step,
allowedMax,
startRange,
endRange,
out bool processedRange,
out BigInteger foundRange,
ref startRange);
if (composite)
{
foundDivisor = foundRange;
return true;
}
if (!processedRange)
{
processedAll = false;
return false;
}
double multiplier = current < target ? plan.Rho1 : plan.Rho2;
BigInteger advanced = new BigInteger(Math.Ceiling((double)current * multiplier));
if (advanced <= current)
{
advanced = current + BigInteger.One;
}
if (advanced > maxKAllowed)
{
break;
}
current = advanced;
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
private bool CheckDivisorsBigPlanRange(
ulong prime,
in BigInteger step,
in BigInteger allowedMax,
in BigInteger startK,
in BigInteger endK,
out bool processedAll,
out BigInteger foundDivisor,
ref BigInteger currentK)
{
processedAll = true;
foundDivisor = BigInteger.Zero;
if (startK < BigInteger.One || endK < startK)
{
return false;
}
BigInteger maxKAllowed = (allowedMax - BigInteger.One) / step;
TryClampMaxKForMersenneTail(prime, step, ref maxKAllowed);
if (startK > maxKAllowed)
{
return false;
}
BigInteger effectiveEnd = endK > maxKAllowed ? maxKAllowed : endK;
ushort primeDecimalMask = DivisorGenerator.GetDecimalMask((prime & 3UL) == 3UL);
BigInteger divisor = (step * startK) + BigInteger.One;
if (divisor > allowedMax)
{
return false;
}
BigInteger iterations = (effectiveEnd - startK) + BigInteger.One;
currentK = startK;
var rem10 = new BigIntegerCycleRemainderStepper(10);
var rem8 = new BigIntegerCycleRemainderStepper(8);
var rem3 = new BigIntegerCycleRemainderStepper(3);
var rem7 = new BigIntegerCycleRemainderStepper(7);
var rem11 = new BigIntegerCycleRemainderStepper(11);
var rem13 = new BigIntegerCycleRemainderStepper(13);
var rem17 = new BigIntegerCycleRemainderStepper(17);
var rem19 = new BigIntegerCycleRemainderStepper(19);
byte remainder10 = rem10.Initialize(divisor);
BigInteger primeBig = (BigInteger)prime;
while (iterations.Sign > 0)
{
if (remainder10 == 1 || remainder10 == 7)
{
if (rem3.NextIsNotDivisible(divisor) &&
rem7.NextIsNotDivisible(divisor) &&
rem11.NextIsNotDivisible(divisor) &&
rem13.NextIsNotDivisible(divisor) &&
rem17.NextIsNotDivisible(divisor) &&
rem19.NextIsNotDivisible(divisor))
{
byte remainder8 = rem8.ComputeNext(divisor);
if (remainder8 == 1 || remainder8 == 7)
{
if (((primeDecimalMask >> remainder10) & 1) != 0)
{
RecordState(currentK);
BigInteger powResult = BigInteger.ModPow(BigIntegerNumbers.Two, primeBig, divisor);
if (powResult.IsOne && !IsMersenneValue(prime, divisor))
{
foundDivisor = divisor;
return true;
}
}
}
}
}
iterations--;
if (iterations.IsZero)
{
break;
}
currentK++;
divisor += step;
remainder10 = rem10.ComputeNext(divisor);
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
private bool TryExecutePlannedScan64(
ByDivisorClassModel model,
ulong prime,
ulong allowedMax,
ulong minK,
out bool processedAll,
out ulong foundDivisor)
{
processedAll = true;
foundDivisor = 0UL;
ulong step64 = prime << 1;
if (step64 == 0UL)
{
processedAll = false;
return false;
}
ByDivisorScanTuning tuning = EnvironmentConfiguration.ByDivisorScanTuning;
ByDivisorScanPlan plan = model.BuildPlan(prime, allowedMax == ulong.MaxValue ? BigInteger.Zero : new BigInteger(allowedMax), tuning);
int suffix = (int)(prime & 1023UL);
// ByDivisorClassEntry entry = model.Entries[suffix];
// Console.WriteLine($"[by-divisor-plan] p={prime} suffix={suffix} k50={entry.K50:F2} k75={entry.K75:F2} gap={entry.GapProbability:F3} cheap<= {plan.CheapLimit:F0} start={plan.Start:F0} target={plan.Target:F0} rho1={plan.Rho1:F2} rho2={plan.Rho2:F2}");
ulong effectiveAllowed = allowedMax == 0UL ? ulong.MaxValue : allowedMax;
ulong maxKAllowed = effectiveAllowed == ulong.MaxValue ? ulong.MaxValue : ((effectiveAllowed - 1UL) / step64);
if (maxKAllowed == 0UL || maxKAllowed < minK)
{
processedAll = false;
return false;
}
ulong cheapLimit = (ulong)Math.Min(plan.CheapLimit, maxKAllowed);
if (cheapLimit >= minK)
{
ulong currentK = minK;
bool composite = CheckDivisors64Range(
prime,
step64,
effectiveAllowed,
minK,
cheapLimit,
ref currentK,
out bool processedRange,
out ulong foundRange);
if (composite)
{
foundDivisor = foundRange;
return true;
}
if (!processedRange)
{
processedAll = false;
return false;
}
}
double nextK = Math.Max(plan.Start, cheapLimit + 1d);
if (nextK < minK)
{
nextK = minK;
}
double target = plan.Target <= 0d ? nextK : plan.Target;
if (target < nextK)
{
target = nextK + 1d;
}
ulong current = (ulong)Math.Ceiling(nextK);
bool loggedProgressive = false;
while (current <= maxKAllowed)
{
if (!loggedProgressive)
{
Console.WriteLine($"[by-divisor-progressive] p={prime} startK={current} target={target:F0} rho={(current < target ? plan.Rho1 : plan.Rho2):F2} maxK={maxKAllowed}");
loggedProgressive = true;
}
ulong currentK = current;
bool compositeSingle = CheckDivisors64Range(
prime,
step64,
effectiveAllowed,
currentK,
currentK,
ref currentK,
out bool processedSingle,
out ulong foundSingle);
if (compositeSingle)
{
foundDivisor = foundSingle;
return true;
}
if (!processedSingle)
{
processedAll = false;
return false;
}
double multiplier = current < target ? plan.Rho1 : plan.Rho2;
double advanced = Math.Ceiling(current * multiplier);
if (advanced <= current)
{
advanced = current + 1d;
}
if (advanced > maxKAllowed)
{
break;
}
current = (ulong)advanced;
}
return false;
}
#endif
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
private bool CheckDivisors64Bit(
ulong prime,
@@ -11,6 +11,10 @@ using System.Runtime.CompilerServices;
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPow2GroupsDivisorSetForCpuOrder;
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPow2GroupsDivisorSetForHybridOrder;
using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPow2GroupsDivisorSetForGpuOrder;
#elif DivisorSet_Predictive
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForCpuOrder;
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForHybridOrder;
using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForGpuOrder;
#else
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForOneByOneDivisorSetForCpuOrder;
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForOneByOneDivisorSetForHybridOrder;
@@ -20,6 +20,9 @@ public static class EnvironmentConfiguration
public static bool UseCpuOrder;
public static bool UsePow2GroupDivisors;
public static int ByDivisorSpecialRange;
public static ByDivisor.ByDivisorClassModel? ByDivisorClassModel;
public static ByDivisor.ByDivisorScanTuning ByDivisorScanTuning = ByDivisor.ByDivisorScanTuning.Default;
public static double ByDivisorCheapKLimit = 100_000d;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Initialize()
@@ -9,6 +9,9 @@ using System.Runtime.CompilerServices;
using MersenneNumberDivisorByDivisorCpuTesterWithCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForOneByOneDivisorSetForCpuOrder;
using MersenneNumberDivisorByDivisorCpuTesterWithHybridOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForOneByOneDivisorSetForHybridOrder;
using MersenneNumberDivisorByDivisorCpuTesterWithGpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForOneByOneDivisorSetForGpuOrder;
using MersenneNumberDivisorByDivisorCpuTesterWithPredictiveCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForCpuOrder;
using MersenneNumberDivisorByDivisorCpuTesterWithPredictiveHybridOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForHybridOrder;
using MersenneNumberDivisorByDivisorCpuTesterWithPredictiveGpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForGpuOrder;
namespace PerfectNumbers.Core;
@@ -45,6 +48,37 @@ public static class MersenneNumberDivisorByDivisorTester
Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunCpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run(
List<ulong> candidates,
ref MersenneNumberDivisorByDivisorCpuTesterWithPredictiveCpuOrder tester,
Dictionary<ulong, (bool DetailedCheck, bool PassedAllTests)>? previousResults,
ulong startPrime,
Action markComposite,
Action clearComposite,
Action<ulong, bool, bool, bool, BigInteger> printResult,
int threadCount,
int primesPerTask)
=> RunStructTester(
candidates,
ref tester,
previousResults,
startPrime,
markComposite,
clearComposite,
printResult,
threadCount,
primesPerTask,
static (
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithPredictiveCpuOrder prototype,
int workerCount,
int partitionSize,
Action mc,
Action cc,
Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunPredictiveCpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run(
List<ulong> candidates,
@@ -76,6 +110,37 @@ public static class MersenneNumberDivisorByDivisorTester
Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunHybridOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run(
List<ulong> candidates,
ref MersenneNumberDivisorByDivisorCpuTesterWithPredictiveHybridOrder tester,
Dictionary<ulong, (bool DetailedCheck, bool PassedAllTests)>? previousResults,
ulong startPrime,
Action markComposite,
Action clearComposite,
Action<ulong, bool, bool, bool, BigInteger> printResult,
int threadCount,
int primesPerTask)
=> RunStructTester(
candidates,
ref tester,
previousResults,
startPrime,
markComposite,
clearComposite,
printResult,
threadCount,
primesPerTask,
static (
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithPredictiveHybridOrder prototype,
int workerCount,
int partitionSize,
Action mc,
Action cc,
Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunPredictiveHybridOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run(
List<ulong> candidates,
@@ -107,6 +172,37 @@ public static class MersenneNumberDivisorByDivisorTester
Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunGpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run(
List<ulong> candidates,
ref MersenneNumberDivisorByDivisorCpuTesterWithPredictiveGpuOrder tester,
Dictionary<ulong, (bool DetailedCheck, bool PassedAllTests)>? previousResults,
ulong startPrime,
Action markComposite,
Action clearComposite,
Action<ulong, bool, bool, bool, BigInteger> printResult,
int threadCount,
int primesPerTask)
=> RunStructTester(
candidates,
ref tester,
previousResults,
startPrime,
markComposite,
clearComposite,
printResult,
threadCount,
primesPerTask,
static (
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithPredictiveGpuOrder prototype,
int workerCount,
int partitionSize,
Action mc,
Action cc,
Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunPredictiveGpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run(
List<ulong> candidates,
@@ -1056,6 +1152,84 @@ public static class MersenneNumberDivisorByDivisorTester
startGate.Dispose();
}
private static void RunPredictiveCpuOrderFiltered(
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithPredictiveCpuOrder prototype,
int workerCount,
int partitionSize,
Action markComposite,
Action clearComposite,
Action<ulong, bool, bool, bool, BigInteger> printResult)
{
MersenneNumberDivisorByDivisorCpuTesterWithPredictiveCpuOrder prototypeCopy = prototype;
int totalCount = filteredPrimes.Count;
if (partitionSize > totalCount)
{
partitionSize = totalCount;
}
if (workerCount == 1)
{
DeterministicRandomCpu.Initialize();
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForPredictiveDivisorSetForCpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
foreach (ulong prime in filteredPrimes)
{
session.ProcessPrime(prime);
}
session.Dispose();
return;
}
TaskScheduler scheduler = UnboundedTaskScheduler.Instance;
int nextIndex = 0;
Task[] tasks = new Task[workerCount];
var startGate = new ManualResetEventSlim(initialState: false);
for (int taskIndex = 0; taskIndex < workerCount; taskIndex++)
{
int workerIndex = taskIndex;
tasks[taskIndex] = Task.Factory.StartNew(
() =>
{
DeterministicRandomCpu.Initialize();
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForPredictiveDivisorSetForCpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
startGate.Wait();
Console.WriteLine($"Task started for worker {workerIndex.ToString(CultureInfo.InvariantCulture)}");
while (true)
{
int start = Interlocked.Add(ref nextIndex, partitionSize) - partitionSize;
if (start >= totalCount)
{
break;
}
int end = start + partitionSize;
if (end > totalCount)
{
end = totalCount;
}
for (int index = start; index < end; index++)
{
session.ProcessPrime(filteredPrimes[index]);
}
}
session.Dispose();
Console.WriteLine($"Task finished for worker {workerIndex.ToString(CultureInfo.InvariantCulture)}");
},
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
scheduler);
}
startGate.Set();
Task.WaitAll(tasks);
startGate.Dispose();
}
private static void RunHybridOrderFiltered(
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithHybridOrder prototype,
@@ -1147,6 +1321,84 @@ public static class MersenneNumberDivisorByDivisorTester
startGate.Dispose();
}
private static void RunPredictiveHybridOrderFiltered(
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithPredictiveHybridOrder prototype,
int workerCount,
int partitionSize,
Action markComposite,
Action clearComposite,
Action<ulong, bool, bool, bool, BigInteger> printResult)
{
MersenneNumberDivisorByDivisorCpuTesterWithPredictiveHybridOrder prototypeCopy = prototype;
int totalCount = filteredPrimes.Count;
if (partitionSize > totalCount)
{
partitionSize = totalCount;
}
if (workerCount == 1)
{
DeterministicRandomCpu.Initialize();
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForPredictiveDivisorSetForHybridOrder(prototypeCopy, markComposite, clearComposite, printResult);
foreach (ulong prime in filteredPrimes)
{
session.ProcessPrime(prime);
}
session.Dispose();
return;
}
TaskScheduler scheduler = UnboundedTaskScheduler.Instance;
int nextIndex = 0;
Task[] tasks = new Task[workerCount];
var startGate = new ManualResetEventSlim(initialState: false);
for (int taskIndex = 0; taskIndex < workerCount; taskIndex++)
{
int workerIndex = taskIndex;
tasks[taskIndex] = Task.Factory.StartNew(
() =>
{
DeterministicRandomCpu.Initialize();
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForPredictiveDivisorSetForHybridOrder(prototypeCopy, markComposite, clearComposite, printResult);
startGate.Wait();
Console.WriteLine($"Task started for worker {workerIndex.ToString(CultureInfo.InvariantCulture)}");
while (true)
{
int start = Interlocked.Add(ref nextIndex, partitionSize) - partitionSize;
if (start >= totalCount)
{
break;
}
int end = start + partitionSize;
if (end > totalCount)
{
end = totalCount;
}
for (int index = start; index < end; index++)
{
session.ProcessPrime(filteredPrimes[index]);
}
}
session.Dispose();
Console.WriteLine($"Task finished for worker {workerIndex.ToString(CultureInfo.InvariantCulture)}");
},
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
scheduler);
}
startGate.Set();
Task.WaitAll(tasks);
startGate.Dispose();
}
private static void RunGpuOrderFiltered(
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithGpuOrder prototype,
@@ -1238,6 +1490,84 @@ public static class MersenneNumberDivisorByDivisorTester
startGate.Dispose();
}
private static void RunPredictiveGpuOrderFiltered(
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithPredictiveGpuOrder prototype,
int workerCount,
int partitionSize,
Action markComposite,
Action clearComposite,
Action<ulong, bool, bool, bool, BigInteger> printResult)
{
MersenneNumberDivisorByDivisorCpuTesterWithPredictiveGpuOrder prototypeCopy = prototype;
int totalCount = filteredPrimes.Count;
if (partitionSize > totalCount)
{
partitionSize = totalCount;
}
if (workerCount == 1)
{
DeterministicRandomCpu.Initialize();
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForPredictiveDivisorSetForGpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
foreach (ulong prime in filteredPrimes)
{
session.ProcessPrime(prime);
}
session.Dispose();
return;
}
TaskScheduler scheduler = UnboundedTaskScheduler.Instance;
int nextIndex = 0;
Task[] tasks = new Task[workerCount];
var startGate = new ManualResetEventSlim(initialState: false);
for (int taskIndex = 0; taskIndex < workerCount; taskIndex++)
{
int workerIndex = taskIndex;
tasks[taskIndex] = Task.Factory.StartNew(
() =>
{
DeterministicRandomCpu.Initialize();
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForPredictiveDivisorSetForGpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
startGate.Wait();
Console.WriteLine($"Task started for worker {workerIndex.ToString(CultureInfo.InvariantCulture)}");
while (true)
{
int start = Interlocked.Add(ref nextIndex, partitionSize) - partitionSize;
if (start >= totalCount)
{
break;
}
int end = start + partitionSize;
if (end > totalCount)
{
end = totalCount;
}
for (int index = start; index < end; index++)
{
session.ProcessPrime(filteredPrimes[index]);
}
}
session.Dispose();
Console.WriteLine($"Task finished for worker {workerIndex.ToString(CultureInfo.InvariantCulture)}");
},
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
scheduler);
}
startGate.Set();
Task.WaitAll(tasks);
startGate.Dispose();
}
private static void RunGpuTesterFiltered(
List<ulong> filteredPrimes,
MersenneNumberDivisorByDivisorGpuTester prototype,