DivisorSet.Percentile, Additive and TopDown modes implemented. State files ported to LMDB => FASTER to avoid millions of files in folders.
This commit is contained in:
1 parent
27f65baf3d
commit
6a924f4041
18 files changed
+2476
-224
No files matched your search
@@ -5,6 +5,16 @@ using PerfectNumbers.Core.Gpu;
|
|||||||
|
|
||||||
namespace EvenPerfectBitScanner;
|
namespace EvenPerfectBitScanner;
|
||||||
|
|
||||||
|
internal enum ByDivisorKIncrementMode
|
||||||
|
{
|
||||||
|
Sequential,
|
||||||
|
Pow2Groups,
|
||||||
|
Predictive,
|
||||||
|
Percentile,
|
||||||
|
Additive,
|
||||||
|
TopDown,
|
||||||
|
}
|
||||||
|
|
||||||
internal readonly struct CliArguments
|
internal readonly struct CliArguments
|
||||||
{
|
{
|
||||||
private const string DefaultCyclesPath = "divisor_cycles.bin";
|
private const string DefaultCyclesPath = "divisor_cycles.bin";
|
||||||
@@ -25,8 +35,7 @@ namespace EvenPerfectBitScanner;
|
|||||||
internal readonly bool UseResidue;
|
internal readonly bool UseResidue;
|
||||||
internal readonly bool UseDivisor;
|
internal readonly bool UseDivisor;
|
||||||
internal readonly bool UseByDivisor;
|
internal readonly bool UseByDivisor;
|
||||||
internal readonly bool UseDivisorPow2Increment;
|
internal readonly ByDivisorKIncrementMode ByDivisorKIncrementMode;
|
||||||
internal readonly bool UseDivisorPredictiveIncrement;
|
|
||||||
internal readonly int ByDivisorSpecialRange;
|
internal readonly int ByDivisorSpecialRange;
|
||||||
internal readonly bool UseGcdFilter;
|
internal readonly bool UseGcdFilter;
|
||||||
internal readonly bool TestMode;
|
internal readonly bool TestMode;
|
||||||
@@ -76,8 +85,7 @@ namespace EvenPerfectBitScanner;
|
|||||||
bool useDivisor,
|
bool useDivisor,
|
||||||
bool useByDivisor,
|
bool useByDivisor,
|
||||||
bool useGcdFilter,
|
bool useGcdFilter,
|
||||||
bool useDivisorPow2Increment,
|
ByDivisorKIncrementMode byDivisorKIncrementMode,
|
||||||
bool useDivisorPredictiveIncrement,
|
|
||||||
int byDivisorSpecialRange,
|
int byDivisorSpecialRange,
|
||||||
bool testMode,
|
bool testMode,
|
||||||
bool useGpuCycles,
|
bool useGpuCycles,
|
||||||
@@ -124,8 +132,7 @@ namespace EvenPerfectBitScanner;
|
|||||||
UseResidue = useResidue;
|
UseResidue = useResidue;
|
||||||
UseDivisor = useDivisor;
|
UseDivisor = useDivisor;
|
||||||
UseByDivisor = useByDivisor;
|
UseByDivisor = useByDivisor;
|
||||||
UseDivisorPow2Increment = useDivisorPow2Increment;
|
ByDivisorKIncrementMode = byDivisorKIncrementMode;
|
||||||
UseDivisorPredictiveIncrement = useDivisorPredictiveIncrement;
|
|
||||||
ByDivisorSpecialRange = byDivisorSpecialRange;
|
ByDivisorSpecialRange = byDivisorSpecialRange;
|
||||||
UseGcdFilter = useGcdFilter;
|
UseGcdFilter = useGcdFilter;
|
||||||
TestMode = testMode;
|
TestMode = testMode;
|
||||||
@@ -178,8 +185,7 @@ namespace EvenPerfectBitScanner;
|
|||||||
bool useResidue = false;
|
bool useResidue = false;
|
||||||
bool useDivisor = false;
|
bool useDivisor = false;
|
||||||
bool useByDivisor = false;
|
bool useByDivisor = false;
|
||||||
bool useDivisorPow2Increment = false;
|
ByDivisorKIncrementMode byDivisorKIncrementMode = ByDivisorKIncrementMode.Sequential;
|
||||||
bool useDivisorPredictiveIncrement = false;
|
|
||||||
int byDivisorSpecialRange = 0;
|
int byDivisorSpecialRange = 0;
|
||||||
bool useGcdFilter = false;
|
bool useGcdFilter = false;
|
||||||
bool testMode = false;
|
bool testMode = false;
|
||||||
@@ -314,22 +320,31 @@ namespace EvenPerfectBitScanner;
|
|||||||
string value = argument["--bydivisor-k-increment=".Length..].Trim();
|
string value = argument["--bydivisor-k-increment=".Length..].Trim();
|
||||||
if (value.Equals("pow2groups", StringComparison.OrdinalIgnoreCase))
|
if (value.Equals("pow2groups", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
useDivisorPow2Increment = true;
|
byDivisorKIncrementMode = ByDivisorKIncrementMode.Pow2Groups;
|
||||||
useDivisorPredictiveIncrement = false;
|
|
||||||
}
|
}
|
||||||
else if (value.Equals("sequential", StringComparison.OrdinalIgnoreCase))
|
else if (value.Equals("sequential", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
useDivisorPow2Increment = false;
|
byDivisorKIncrementMode = ByDivisorKIncrementMode.Sequential;
|
||||||
useDivisorPredictiveIncrement = false;
|
|
||||||
}
|
}
|
||||||
else if (value.Equals("predictive", StringComparison.OrdinalIgnoreCase))
|
else if (value.Equals("predictive", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
useDivisorPow2Increment = false;
|
byDivisorKIncrementMode = ByDivisorKIncrementMode.Predictive;
|
||||||
useDivisorPredictiveIncrement = true;
|
}
|
||||||
|
else if (value.Equals("percentile", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
byDivisorKIncrementMode = ByDivisorKIncrementMode.Percentile;
|
||||||
|
}
|
||||||
|
else if (value.Equals("additive", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
byDivisorKIncrementMode = ByDivisorKIncrementMode.Additive;
|
||||||
|
}
|
||||||
|
else if (value.Equals("topdown", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
byDivisorKIncrementMode = ByDivisorKIncrementMode.TopDown;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine("Invalid value for --bydivisor-k-increment. Use sequential, pow2groups, or predictive.");
|
Console.WriteLine("Invalid value for --bydivisor-k-increment. Use sequential, pow2groups, predictive, percentile, additive, or topdown.");
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -652,8 +667,7 @@ namespace EvenPerfectBitScanner;
|
|||||||
useDivisor,
|
useDivisor,
|
||||||
useByDivisor,
|
useByDivisor,
|
||||||
useGcdFilter,
|
useGcdFilter,
|
||||||
useDivisorPow2Increment,
|
byDivisorKIncrementMode,
|
||||||
useDivisorPredictiveIncrement,
|
|
||||||
byDivisorSpecialRange,
|
byDivisorSpecialRange,
|
||||||
testMode,
|
testMode,
|
||||||
useGpuCycles,
|
useGpuCycles,
|
||||||
@@ -696,7 +710,7 @@ namespace EvenPerfectBitScanner;
|
|||||||
Console.WriteLine(" --increment=bit|add exponent increment method (default add)");
|
Console.WriteLine(" --increment=bit|add exponent increment method (default add)");
|
||||||
Console.WriteLine(" --threads=<value> number of worker threads");
|
Console.WriteLine(" --threads=<value> number of worker threads");
|
||||||
Console.WriteLine(" --mersenne=pow2mod|incremental|lucas|residue|divisor|bydivisor Mersenne test method");
|
Console.WriteLine(" --mersenne=pow2mod|incremental|lucas|residue|divisor|bydivisor Mersenne test method");
|
||||||
Console.WriteLine(" --bydivisor-k-increment=sequential|pow2groups|predictive k iteration strategy for --mersenne=bydivisor (default sequential)");
|
Console.WriteLine(" --bydivisor-k-increment=sequential|pow2groups|predictive|percentile|additive|topdown k iteration strategy for --mersenne=bydivisor (default sequential)");
|
||||||
Console.WriteLine(" --bydivisor-special-range=<n> widen pow2groups special values to +/- n percent (default 0 = points)");
|
Console.WriteLine(" --bydivisor-special-range=<n> widen pow2groups special values to +/- n percent (default 0 = points)");
|
||||||
Console.WriteLine(" --max-k=<value> max k for Mersenne tests (q = 2*p*k + 1), including --mersenne=bydivisor");
|
Console.WriteLine(" --max-k=<value> max k for Mersenne tests (q = 2*p*k + 1), including --mersenne=bydivisor");
|
||||||
Console.WriteLine(" --mersenne-device=cpu|gpu|hybrid Device for Mersenne method (default gpu)");
|
Console.WriteLine(" --mersenne-device=cpu|gpu|hybrid Device for Mersenne method (default gpu)");
|
||||||
|
|||||||
@@ -10,15 +10,25 @@ using PerfectNumbers.Core.Cpu;
|
|||||||
using PerfectNumbers.Core.ByDivisor;
|
using PerfectNumbers.Core.ByDivisor;
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
using MersenneNumberDivisorByDivisorCpuTesterWithCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForOneByOneDivisorSetForCpuOrder;
|
using PerfectNumbers.Core.Persistence;
|
||||||
using MersenneNumberDivisorByDivisorCpuTesterWithHybridOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForOneByOneDivisorSetForHybridOrder;
|
using MersenneNumberDivisorByDivisorCpuTesterWithCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForCpuOrder;
|
||||||
using MersenneNumberDivisorByDivisorCpuTesterWithGpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForOneByOneDivisorSetForGpuOrder;
|
using MersenneNumberDivisorByDivisorCpuTesterWithHybridOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForHybridOrder;
|
||||||
|
using MersenneNumberDivisorByDivisorCpuTesterWithGpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForGpuOrder;
|
||||||
using ByDivisorCpuOrderPow2 = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPow2GroupsDivisorSetForCpuOrder;
|
using ByDivisorCpuOrderPow2 = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPow2GroupsDivisorSetForCpuOrder;
|
||||||
using ByDivisorHybridOrderPow2 = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPow2GroupsDivisorSetForHybridOrder;
|
using ByDivisorHybridOrderPow2 = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPow2GroupsDivisorSetForHybridOrder;
|
||||||
using ByDivisorGpuOrderPow2 = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPow2GroupsDivisorSetForGpuOrder;
|
using ByDivisorGpuOrderPow2 = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPow2GroupsDivisorSetForGpuOrder;
|
||||||
using ByDivisorCpuOrderPredictive = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForCpuOrder;
|
using ByDivisorCpuOrderPredictive = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForCpuOrder;
|
||||||
using ByDivisorHybridOrderPredictive = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForHybridOrder;
|
using ByDivisorHybridOrderPredictive = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForHybridOrder;
|
||||||
using ByDivisorGpuOrderPredictive = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForGpuOrder;
|
using ByDivisorGpuOrderPredictive = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForGpuOrder;
|
||||||
|
using ByDivisorCpuOrderPercentile = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPercentileDivisorSetForCpuOrder;
|
||||||
|
using ByDivisorHybridOrderPercentile = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPercentileDivisorSetForHybridOrder;
|
||||||
|
using ByDivisorGpuOrderPercentile = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPercentileDivisorSetForGpuOrder;
|
||||||
|
using ByDivisorCpuOrderAdditive = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForCpuOrder;
|
||||||
|
using ByDivisorHybridOrderAdditive = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForHybridOrder;
|
||||||
|
using ByDivisorGpuOrderAdditive = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForGpuOrder;
|
||||||
|
using ByDivisorCpuOrderTopDown = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForTopDownDivisorSetForCpuOrder;
|
||||||
|
using ByDivisorHybridOrderTopDown = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForTopDownDivisorSetForHybridOrder;
|
||||||
|
using ByDivisorGpuOrderTopDown = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForTopDownDivisorSetForGpuOrder;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
|
||||||
namespace EvenPerfectBitScanner;
|
namespace EvenPerfectBitScanner;
|
||||||
@@ -39,8 +49,18 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
private static ByDivisorCpuOrderPredictive _byDivisorCpuOrderTesterPredictive;
|
private static ByDivisorCpuOrderPredictive _byDivisorCpuOrderTesterPredictive;
|
||||||
private static ByDivisorHybridOrderPredictive _byDivisorHybridOrderTesterPredictive;
|
private static ByDivisorHybridOrderPredictive _byDivisorHybridOrderTesterPredictive;
|
||||||
private static ByDivisorGpuOrderPredictive _byDivisorGpuOrderTesterPredictive;
|
private static ByDivisorGpuOrderPredictive _byDivisorGpuOrderTesterPredictive;
|
||||||
|
private static ByDivisorCpuOrderPercentile _byDivisorCpuOrderTesterPercentile;
|
||||||
|
private static ByDivisorHybridOrderPercentile _byDivisorHybridOrderTesterPercentile;
|
||||||
|
private static ByDivisorGpuOrderPercentile _byDivisorGpuOrderTesterPercentile;
|
||||||
|
private static ByDivisorCpuOrderAdditive _byDivisorCpuOrderTesterAdditive;
|
||||||
|
private static ByDivisorHybridOrderAdditive _byDivisorHybridOrderTesterAdditive;
|
||||||
|
private static ByDivisorGpuOrderAdditive _byDivisorGpuOrderTesterAdditive;
|
||||||
|
private static ByDivisorCpuOrderTopDown _byDivisorCpuOrderTesterTopDown;
|
||||||
|
private static ByDivisorHybridOrderTopDown _byDivisorHybridOrderTesterTopDown;
|
||||||
|
private static ByDivisorGpuOrderTopDown _byDivisorGpuOrderTesterTopDown;
|
||||||
private static Dictionary<ulong, (bool DetailedCheck, bool PassedAllTests)>? _byDivisorPreviousResults;
|
private static Dictionary<ulong, (bool DetailedCheck, bool PassedAllTests)>? _byDivisorPreviousResults;
|
||||||
private static string? _byDivisorModelPath;
|
private static string? _byDivisorModelPath;
|
||||||
|
private static bool _byDivisorModeUsesModel;
|
||||||
private static bool _limitReached;
|
private static bool _limitReached;
|
||||||
private static ulong _byDivisorStartPrime;
|
private static ulong _byDivisorStartPrime;
|
||||||
private static CliArguments _cliArguments;
|
private static CliArguments _cliArguments;
|
||||||
@@ -87,7 +107,7 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
EnvironmentConfiguration.GpuBatchSize = Math.Max(1, _cliArguments.ScanBatchSize);
|
EnvironmentConfiguration.GpuBatchSize = Math.Max(1, _cliArguments.ScanBatchSize);
|
||||||
EnvironmentConfiguration.GpuRatio = _cliArguments.GpuRatio;
|
EnvironmentConfiguration.GpuRatio = _cliArguments.GpuRatio;
|
||||||
EnvironmentConfiguration.OrderDevice = _cliArguments.OrderDevice;
|
EnvironmentConfiguration.OrderDevice = _cliArguments.OrderDevice;
|
||||||
EnvironmentConfiguration.UsePow2GroupDivisors = _cliArguments.UseDivisorPow2Increment;
|
EnvironmentConfiguration.UsePow2GroupDivisors = _cliArguments.ByDivisorKIncrementMode == ByDivisorKIncrementMode.Pow2Groups;
|
||||||
EnvironmentConfiguration.ByDivisorSpecialRange = _cliArguments.ByDivisorSpecialRange;
|
EnvironmentConfiguration.ByDivisorSpecialRange = _cliArguments.ByDivisorSpecialRange;
|
||||||
EnvironmentConfiguration.RollingAccelerators = Math.Min(Math.Min(PerfectNumberConstants.RollingAccelerators, gpuPrimeThreads), threadCount);
|
EnvironmentConfiguration.RollingAccelerators = Math.Min(Math.Min(PerfectNumberConstants.RollingAccelerators, gpuPrimeThreads), threadCount);
|
||||||
EnvironmentConfiguration.Initialize();
|
EnvironmentConfiguration.Initialize();
|
||||||
@@ -289,51 +309,77 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
throw new NotSupportedException("--mersenne-device=gpu is not supported for --mersenne=bydivisor on this execution path.");
|
throw new NotSupportedException("--mersenne-device=gpu is not supported for --mersenne=bydivisor on this execution path.");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool usePow2Divisors = _cliArguments.UseDivisorPow2Increment;
|
ByDivisorKIncrementMode kIncrementMode = _cliArguments.ByDivisorKIncrementMode;
|
||||||
bool usePredictiveDivisors = _cliArguments.UseDivisorPredictiveIncrement;
|
|
||||||
if (EnvironmentConfiguration.UseCpuOrder)
|
if (EnvironmentConfiguration.UseCpuOrder)
|
||||||
{
|
{
|
||||||
if (usePow2Divisors)
|
switch (kIncrementMode)
|
||||||
{
|
{
|
||||||
|
case ByDivisorKIncrementMode.Pow2Groups:
|
||||||
_byDivisorCpuOrderTesterPow2 = new ByDivisorCpuOrderPow2();
|
_byDivisorCpuOrderTesterPow2 = new ByDivisorCpuOrderPow2();
|
||||||
}
|
break;
|
||||||
else if (usePredictiveDivisors)
|
case ByDivisorKIncrementMode.Predictive:
|
||||||
{
|
|
||||||
_byDivisorCpuOrderTesterPredictive = new ByDivisorCpuOrderPredictive();
|
_byDivisorCpuOrderTesterPredictive = new ByDivisorCpuOrderPredictive();
|
||||||
}
|
break;
|
||||||
else
|
case ByDivisorKIncrementMode.Percentile:
|
||||||
{
|
_byDivisorCpuOrderTesterPercentile = new ByDivisorCpuOrderPercentile();
|
||||||
|
break;
|
||||||
|
case ByDivisorKIncrementMode.Additive:
|
||||||
|
_byDivisorCpuOrderTesterAdditive = new ByDivisorCpuOrderAdditive();
|
||||||
|
break;
|
||||||
|
case ByDivisorKIncrementMode.TopDown:
|
||||||
|
_byDivisorCpuOrderTesterTopDown = new ByDivisorCpuOrderTopDown();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
_byDivisorCpuOrderTester = new MersenneNumberDivisorByDivisorCpuTesterWithCpuOrder();
|
_byDivisorCpuOrderTester = new MersenneNumberDivisorByDivisorCpuTesterWithCpuOrder();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (EnvironmentConfiguration.UseHybridOrder)
|
else if (EnvironmentConfiguration.UseHybridOrder)
|
||||||
{
|
{
|
||||||
if (usePow2Divisors)
|
switch (kIncrementMode)
|
||||||
{
|
{
|
||||||
|
case ByDivisorKIncrementMode.Pow2Groups:
|
||||||
_byDivisorHybridOrderTesterPow2 = new ByDivisorHybridOrderPow2();
|
_byDivisorHybridOrderTesterPow2 = new ByDivisorHybridOrderPow2();
|
||||||
}
|
break;
|
||||||
else if (usePredictiveDivisors)
|
case ByDivisorKIncrementMode.Predictive:
|
||||||
{
|
|
||||||
_byDivisorHybridOrderTesterPredictive = new ByDivisorHybridOrderPredictive();
|
_byDivisorHybridOrderTesterPredictive = new ByDivisorHybridOrderPredictive();
|
||||||
}
|
break;
|
||||||
else
|
case ByDivisorKIncrementMode.Percentile:
|
||||||
{
|
_byDivisorHybridOrderTesterPercentile = new ByDivisorHybridOrderPercentile();
|
||||||
|
break;
|
||||||
|
case ByDivisorKIncrementMode.Additive:
|
||||||
|
_byDivisorHybridOrderTesterAdditive = new ByDivisorHybridOrderAdditive();
|
||||||
|
break;
|
||||||
|
case ByDivisorKIncrementMode.TopDown:
|
||||||
|
_byDivisorHybridOrderTesterTopDown = new ByDivisorHybridOrderTopDown();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
_byDivisorHybridOrderTester = new MersenneNumberDivisorByDivisorCpuTesterWithHybridOrder();
|
_byDivisorHybridOrderTester = new MersenneNumberDivisorByDivisorCpuTesterWithHybridOrder();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (usePow2Divisors)
|
switch (kIncrementMode)
|
||||||
{
|
{
|
||||||
|
case ByDivisorKIncrementMode.Pow2Groups:
|
||||||
_byDivisorGpuOrderTesterPow2 = new ByDivisorGpuOrderPow2();
|
_byDivisorGpuOrderTesterPow2 = new ByDivisorGpuOrderPow2();
|
||||||
}
|
break;
|
||||||
else if (usePredictiveDivisors)
|
case ByDivisorKIncrementMode.Predictive:
|
||||||
{
|
|
||||||
_byDivisorGpuOrderTesterPredictive = new ByDivisorGpuOrderPredictive();
|
_byDivisorGpuOrderTesterPredictive = new ByDivisorGpuOrderPredictive();
|
||||||
}
|
break;
|
||||||
else
|
case ByDivisorKIncrementMode.Percentile:
|
||||||
{
|
_byDivisorGpuOrderTesterPercentile = new ByDivisorGpuOrderPercentile();
|
||||||
|
break;
|
||||||
|
case ByDivisorKIncrementMode.Additive:
|
||||||
|
_byDivisorGpuOrderTesterAdditive = new ByDivisorGpuOrderAdditive();
|
||||||
|
break;
|
||||||
|
case ByDivisorKIncrementMode.TopDown:
|
||||||
|
_byDivisorGpuOrderTesterTopDown = new ByDivisorGpuOrderTopDown();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
_byDivisorGpuOrderTester = new MersenneNumberDivisorByDivisorCpuTesterWithGpuOrder();
|
_byDivisorGpuOrderTester = new MersenneNumberDivisorByDivisorCpuTesterWithGpuOrder();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -437,26 +483,80 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
|
|
||||||
if (useByDivisor)
|
if (useByDivisor)
|
||||||
{
|
{
|
||||||
_byDivisorModelPath = Path.Combine(PerfectNumberConstants.ByDivisorStateDirectory, "bydivisor_k10_model.json");
|
ByDivisorKIncrementMode kIncrementMode = _cliArguments.ByDivisorKIncrementMode;
|
||||||
|
_byDivisorModeUsesModel = kIncrementMode is ByDivisorKIncrementMode.Predictive or ByDivisorKIncrementMode.Percentile or ByDivisorKIncrementMode.Additive;
|
||||||
var byDivisorTuning = new ByDivisorScanTuning
|
var byDivisorTuning = new ByDivisorScanTuning
|
||||||
{
|
{
|
||||||
CheapLimit = EnvironmentConfiguration.ByDivisorCheapKLimit,
|
CheapLimit = EnvironmentConfiguration.ByDivisorCheapKLimit,
|
||||||
};
|
};
|
||||||
EnvironmentConfiguration.ByDivisorScanTuning = byDivisorTuning;
|
EnvironmentConfiguration.ByDivisorScanTuning = byDivisorTuning;
|
||||||
bool modelExists = File.Exists(_byDivisorModelPath);
|
|
||||||
bool resultsExist = File.Exists(resultsFileName);
|
EnvironmentConfiguration.ByDivisorClassModel = null;
|
||||||
bool needsBuild = !modelExists || (resultsExist && File.GetLastWriteTimeUtc(resultsFileName) > File.GetLastWriteTimeUtc(_byDivisorModelPath));
|
EnvironmentConfiguration.ByDivisorClassModelRepository = null;
|
||||||
Console.WriteLine(needsBuild
|
if (_byDivisorModeUsesModel)
|
||||||
? $"Building by-divisor class model from {resultsFileName}..."
|
{
|
||||||
: $"Loading by-divisor class model from {_byDivisorModelPath}...");
|
string modelRepoFile = "bydivisor_model.faster";
|
||||||
EnvironmentConfiguration.ByDivisorClassModel = ByDivisorClassModelLoader.LoadOrBuild(
|
var modelRepo = ByDivisorClassModelRepository.Open(PerfectNumberConstants.ByDivisorStateDirectory, modelRepoFile);
|
||||||
|
EnvironmentConfiguration.ByDivisorClassModelRepository = modelRepo;
|
||||||
|
if (!modelRepo.TryLoad(out var loadedModel) || loadedModel is null)
|
||||||
|
{
|
||||||
|
_byDivisorModelPath = Path.Combine(PerfectNumberConstants.ByDivisorStateDirectory, "bydivisor_k10_model.json");
|
||||||
|
Console.WriteLine($"Building by-divisor class model from {resultsFileName}...");
|
||||||
|
ByDivisorClassModel model = ByDivisorClassModelLoader.LoadOrBuild(
|
||||||
_byDivisorModelPath,
|
_byDivisorModelPath,
|
||||||
resultsFileName,
|
resultsFileName,
|
||||||
byDivisorTuning,
|
byDivisorTuning,
|
||||||
byDivisorTuning.CheapLimit,
|
byDivisorTuning.CheapLimit,
|
||||||
rebuildWhenResultsNewer: true);
|
rebuildWhenResultsNewer: true);
|
||||||
|
modelRepo.Save(model);
|
||||||
|
modelRepo.Commit();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.Delete(_byDivisorModelPath);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// best-effort cleanup
|
||||||
|
}
|
||||||
|
EnvironmentConfiguration.ByDivisorClassModel = model;
|
||||||
Console.WriteLine($"By-divisor class model ready (cheap<={byDivisorTuning.CheapLimit:F0}).");
|
Console.WriteLine($"By-divisor class model ready (cheap<={byDivisorTuning.CheapLimit:F0}).");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
loadedModel.CheapLimit = byDivisorTuning.CheapLimit;
|
||||||
|
EnvironmentConfiguration.ByDivisorClassModel = loadedModel;
|
||||||
|
Console.WriteLine($"By-divisor class model loaded from LMDB (cheap<={byDivisorTuning.CheapLimit:F0}).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize LMDB repositories for by-divisor state
|
||||||
|
string checksDir = PerfectNumberConstants.ByDivisorStateDirectory;
|
||||||
|
string kRepoFile = kIncrementMode switch
|
||||||
|
{
|
||||||
|
ByDivisorKIncrementMode.Pow2Groups => "pow2groups.kstate.faster",
|
||||||
|
ByDivisorKIncrementMode.Predictive => "predictive.kstate.faster",
|
||||||
|
ByDivisorKIncrementMode.Percentile => "percentile.kstate.faster",
|
||||||
|
ByDivisorKIncrementMode.Additive => "additive.kstate.faster",
|
||||||
|
ByDivisorKIncrementMode.TopDown => "topdown.kstate.faster",
|
||||||
|
_ => "sequential.kstate.faster",
|
||||||
|
};
|
||||||
|
string lmdbPath = Path.Combine(checksDir, Path.ChangeExtension(kRepoFile, ".lmdb"));
|
||||||
|
EnvironmentConfiguration.ByDivisorKStateRepository = KStateRepository.Open(checksDir, kRepoFile, lmdbPath, checksDir);
|
||||||
|
EnvironmentConfiguration.ByDivisorCheapKStateRepository = KStateRepository.Open(checksDir, "cheaplimit.kstate.faster", Path.Combine(checksDir, "cheaplimit.kstate.lmdb"), checksDir);
|
||||||
|
string pow2MinusLmdb = Path.Combine(checksDir, "pow2minus1.lmdb");
|
||||||
|
EnvironmentConfiguration.ByDivisorPow2Minus1Repository ??= Pow2Minus1StateRepository.Open(Path.Combine(checksDir, "pow2minus1.faster"), pow2MinusLmdb, checksDir);
|
||||||
|
|
||||||
|
if (kIncrementMode == ByDivisorKIncrementMode.Pow2Groups)
|
||||||
|
{
|
||||||
|
EnvironmentConfiguration.ByDivisorSpecialStateRepository = KStateRepository.Open(checksDir, "pow2groups.special.kstate.faster", Path.Combine(checksDir, "pow2groups.special.kstate.lmdb"), checksDir);
|
||||||
|
EnvironmentConfiguration.ByDivisorGroupsStateRepository = KStateRepository.Open(checksDir, "pow2groups.groups.kstate.faster", Path.Combine(checksDir, "pow2groups.groups.kstate.lmdb"), checksDir);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EnvironmentConfiguration.ByDivisorSpecialStateRepository = null;
|
||||||
|
EnvironmentConfiguration.ByDivisorGroupsStateRepository = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool useFilter = !testMode && !string.IsNullOrEmpty(filterFile);
|
bool useFilter = !testMode && !string.IsNullOrEmpty(filterFile);
|
||||||
HashSet<ulong> filter = [];
|
HashSet<ulong> filter = [];
|
||||||
@@ -573,12 +673,12 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
if (useByDivisor)
|
if (useByDivisor)
|
||||||
{
|
{
|
||||||
int byDivisorPrimeRange = Math.Max(1, _cliArguments.BlockSize);
|
int byDivisorPrimeRange = Math.Max(1, _cliArguments.BlockSize);
|
||||||
bool usePow2Divisors = _cliArguments.UseDivisorPow2Increment;
|
ByDivisorKIncrementMode kIncrementMode = _cliArguments.ByDivisorKIncrementMode;
|
||||||
bool usePredictiveDivisors = _cliArguments.UseDivisorPredictiveIncrement;
|
|
||||||
if (EnvironmentConfiguration.UseCpuOrder)
|
if (EnvironmentConfiguration.UseCpuOrder)
|
||||||
{
|
{
|
||||||
if (usePow2Divisors)
|
switch (kIncrementMode)
|
||||||
{
|
{
|
||||||
|
case ByDivisorKIncrementMode.Pow2Groups:
|
||||||
Console.WriteLine("Using by-divisor pow2groups increment (CPU order).");
|
Console.WriteLine("Using by-divisor pow2groups increment (CPU order).");
|
||||||
MersenneNumberDivisorByDivisorTester.Run(
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
byDivisorCandidates,
|
byDivisorCandidates,
|
||||||
@@ -590,9 +690,8 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
HandleByDivisorResult,
|
HandleByDivisorResult,
|
||||||
threadCount,
|
threadCount,
|
||||||
byDivisorPrimeRange);
|
byDivisorPrimeRange);
|
||||||
}
|
break;
|
||||||
else if (usePredictiveDivisors)
|
case ByDivisorKIncrementMode.Predictive:
|
||||||
{
|
|
||||||
Console.WriteLine("Using by-divisor predictive increment (CPU order).");
|
Console.WriteLine("Using by-divisor predictive increment (CPU order).");
|
||||||
MersenneNumberDivisorByDivisorTester.Run(
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
byDivisorCandidates,
|
byDivisorCandidates,
|
||||||
@@ -604,9 +703,34 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
HandleByDivisorResult,
|
HandleByDivisorResult,
|
||||||
threadCount,
|
threadCount,
|
||||||
byDivisorPrimeRange);
|
byDivisorPrimeRange);
|
||||||
}
|
break;
|
||||||
else
|
case ByDivisorKIncrementMode.Percentile:
|
||||||
{
|
Console.WriteLine("Using by-divisor percentile increment (CPU order).");
|
||||||
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
|
byDivisorCandidates,
|
||||||
|
ref _byDivisorCpuOrderTesterPercentile,
|
||||||
|
_byDivisorPreviousResults,
|
||||||
|
_byDivisorStartPrime,
|
||||||
|
static () => _lastCompositeP = true,
|
||||||
|
static () => _lastCompositeP = false,
|
||||||
|
HandleByDivisorResult,
|
||||||
|
threadCount,
|
||||||
|
byDivisorPrimeRange);
|
||||||
|
break;
|
||||||
|
case ByDivisorKIncrementMode.Additive:
|
||||||
|
Console.WriteLine("Using by-divisor additive increment (CPU order).");
|
||||||
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
|
byDivisorCandidates,
|
||||||
|
ref _byDivisorCpuOrderTesterAdditive,
|
||||||
|
_byDivisorPreviousResults,
|
||||||
|
_byDivisorStartPrime,
|
||||||
|
static () => _lastCompositeP = true,
|
||||||
|
static () => _lastCompositeP = false,
|
||||||
|
HandleByDivisorResult,
|
||||||
|
threadCount,
|
||||||
|
byDivisorPrimeRange);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
MersenneNumberDivisorByDivisorTester.Run(
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
byDivisorCandidates,
|
byDivisorCandidates,
|
||||||
ref _byDivisorCpuOrderTester,
|
ref _byDivisorCpuOrderTester,
|
||||||
@@ -617,12 +741,14 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
HandleByDivisorResult,
|
HandleByDivisorResult,
|
||||||
threadCount,
|
threadCount,
|
||||||
byDivisorPrimeRange);
|
byDivisorPrimeRange);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (EnvironmentConfiguration.UseHybridOrder)
|
else if (EnvironmentConfiguration.UseHybridOrder)
|
||||||
{
|
{
|
||||||
if (usePow2Divisors)
|
switch (kIncrementMode)
|
||||||
{
|
{
|
||||||
|
case ByDivisorKIncrementMode.Pow2Groups:
|
||||||
Console.WriteLine("Using by-divisor pow2groups increment (Hybrid order).");
|
Console.WriteLine("Using by-divisor pow2groups increment (Hybrid order).");
|
||||||
MersenneNumberDivisorByDivisorTester.Run(
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
byDivisorCandidates,
|
byDivisorCandidates,
|
||||||
@@ -634,9 +760,8 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
HandleByDivisorResult,
|
HandleByDivisorResult,
|
||||||
threadCount,
|
threadCount,
|
||||||
byDivisorPrimeRange);
|
byDivisorPrimeRange);
|
||||||
}
|
break;
|
||||||
else if (usePredictiveDivisors)
|
case ByDivisorKIncrementMode.Predictive:
|
||||||
{
|
|
||||||
Console.WriteLine("Using by-divisor predictive increment (Hybrid order).");
|
Console.WriteLine("Using by-divisor predictive increment (Hybrid order).");
|
||||||
MersenneNumberDivisorByDivisorTester.Run(
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
byDivisorCandidates,
|
byDivisorCandidates,
|
||||||
@@ -648,9 +773,34 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
HandleByDivisorResult,
|
HandleByDivisorResult,
|
||||||
threadCount,
|
threadCount,
|
||||||
byDivisorPrimeRange);
|
byDivisorPrimeRange);
|
||||||
}
|
break;
|
||||||
else
|
case ByDivisorKIncrementMode.Percentile:
|
||||||
{
|
Console.WriteLine("Using by-divisor percentile increment (Hybrid order).");
|
||||||
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
|
byDivisorCandidates,
|
||||||
|
ref _byDivisorHybridOrderTesterPercentile,
|
||||||
|
_byDivisorPreviousResults,
|
||||||
|
_byDivisorStartPrime,
|
||||||
|
static () => _lastCompositeP = true,
|
||||||
|
static () => _lastCompositeP = false,
|
||||||
|
HandleByDivisorResult,
|
||||||
|
threadCount,
|
||||||
|
byDivisorPrimeRange);
|
||||||
|
break;
|
||||||
|
case ByDivisorKIncrementMode.Additive:
|
||||||
|
Console.WriteLine("Using by-divisor additive increment (Hybrid order).");
|
||||||
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
|
byDivisorCandidates,
|
||||||
|
ref _byDivisorHybridOrderTesterAdditive,
|
||||||
|
_byDivisorPreviousResults,
|
||||||
|
_byDivisorStartPrime,
|
||||||
|
static () => _lastCompositeP = true,
|
||||||
|
static () => _lastCompositeP = false,
|
||||||
|
HandleByDivisorResult,
|
||||||
|
threadCount,
|
||||||
|
byDivisorPrimeRange);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
MersenneNumberDivisorByDivisorTester.Run(
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
byDivisorCandidates,
|
byDivisorCandidates,
|
||||||
ref _byDivisorHybridOrderTester,
|
ref _byDivisorHybridOrderTester,
|
||||||
@@ -661,12 +811,14 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
HandleByDivisorResult,
|
HandleByDivisorResult,
|
||||||
threadCount,
|
threadCount,
|
||||||
byDivisorPrimeRange);
|
byDivisorPrimeRange);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (usePow2Divisors)
|
switch (kIncrementMode)
|
||||||
{
|
{
|
||||||
|
case ByDivisorKIncrementMode.Pow2Groups:
|
||||||
Console.WriteLine("Using by-divisor pow2groups increment (GPU order).");
|
Console.WriteLine("Using by-divisor pow2groups increment (GPU order).");
|
||||||
MersenneNumberDivisorByDivisorTester.Run(
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
byDivisorCandidates,
|
byDivisorCandidates,
|
||||||
@@ -678,9 +830,8 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
HandleByDivisorResult,
|
HandleByDivisorResult,
|
||||||
threadCount,
|
threadCount,
|
||||||
byDivisorPrimeRange);
|
byDivisorPrimeRange);
|
||||||
}
|
break;
|
||||||
else if (usePredictiveDivisors)
|
case ByDivisorKIncrementMode.Predictive:
|
||||||
{
|
|
||||||
Console.WriteLine("Using by-divisor predictive increment (GPU order).");
|
Console.WriteLine("Using by-divisor predictive increment (GPU order).");
|
||||||
MersenneNumberDivisorByDivisorTester.Run(
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
byDivisorCandidates,
|
byDivisorCandidates,
|
||||||
@@ -692,9 +843,34 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
HandleByDivisorResult,
|
HandleByDivisorResult,
|
||||||
threadCount,
|
threadCount,
|
||||||
byDivisorPrimeRange);
|
byDivisorPrimeRange);
|
||||||
}
|
break;
|
||||||
else
|
case ByDivisorKIncrementMode.Percentile:
|
||||||
{
|
Console.WriteLine("Using by-divisor percentile increment (GPU order).");
|
||||||
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
|
byDivisorCandidates,
|
||||||
|
ref _byDivisorGpuOrderTesterPercentile,
|
||||||
|
_byDivisorPreviousResults,
|
||||||
|
_byDivisorStartPrime,
|
||||||
|
static () => _lastCompositeP = true,
|
||||||
|
static () => _lastCompositeP = false,
|
||||||
|
HandleByDivisorResult,
|
||||||
|
threadCount,
|
||||||
|
byDivisorPrimeRange);
|
||||||
|
break;
|
||||||
|
case ByDivisorKIncrementMode.Additive:
|
||||||
|
Console.WriteLine("Using by-divisor additive increment (GPU order).");
|
||||||
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
|
byDivisorCandidates,
|
||||||
|
ref _byDivisorGpuOrderTesterAdditive,
|
||||||
|
_byDivisorPreviousResults,
|
||||||
|
_byDivisorStartPrime,
|
||||||
|
static () => _lastCompositeP = true,
|
||||||
|
static () => _lastCompositeP = false,
|
||||||
|
HandleByDivisorResult,
|
||||||
|
threadCount,
|
||||||
|
byDivisorPrimeRange);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
MersenneNumberDivisorByDivisorTester.Run(
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
byDivisorCandidates,
|
byDivisorCandidates,
|
||||||
ref _byDivisorGpuOrderTester,
|
ref _byDivisorGpuOrderTester,
|
||||||
@@ -705,6 +881,7 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
HandleByDivisorResult,
|
HandleByDivisorResult,
|
||||||
threadCount,
|
threadCount,
|
||||||
byDivisorPrimeRange);
|
byDivisorPrimeRange);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1092,24 +1269,42 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
if (_cliArguments.UseByDivisor)
|
if (_cliArguments.UseByDivisor)
|
||||||
{
|
{
|
||||||
// Windowed pow2mod kernel handles by-divisor scans.
|
// Windowed pow2mod kernel handles by-divisor scans.
|
||||||
bool usePow2Divisors = _cliArguments.UseDivisorPow2Increment;
|
ByDivisorKIncrementMode mode = _cliArguments.ByDivisorKIncrementMode;
|
||||||
if (EnvironmentConfiguration.UseCpuOrder)
|
if (EnvironmentConfiguration.UseCpuOrder)
|
||||||
{
|
{
|
||||||
return usePow2Divisors
|
return mode switch
|
||||||
? _byDivisorCpuOrderTesterPow2.IsPrime(p, out detailedCheck, out _)
|
{
|
||||||
: _byDivisorCpuOrderTester.IsPrime(p, out detailedCheck, out _);
|
ByDivisorKIncrementMode.Pow2Groups => _byDivisorCpuOrderTesterPow2.IsPrime(p, out detailedCheck, out _),
|
||||||
|
ByDivisorKIncrementMode.Predictive => _byDivisorCpuOrderTesterPredictive.IsPrime(p, out detailedCheck, out _),
|
||||||
|
ByDivisorKIncrementMode.Percentile => _byDivisorCpuOrderTesterPercentile.IsPrime(p, out detailedCheck, out _),
|
||||||
|
ByDivisorKIncrementMode.Additive => _byDivisorCpuOrderTesterAdditive.IsPrime(p, out detailedCheck, out _),
|
||||||
|
ByDivisorKIncrementMode.TopDown => _byDivisorCpuOrderTesterTopDown.IsPrime(p, out detailedCheck, out _),
|
||||||
|
_ => _byDivisorCpuOrderTester.IsPrime(p, out detailedCheck, out _),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (EnvironmentConfiguration.UseHybridOrder)
|
if (EnvironmentConfiguration.UseHybridOrder)
|
||||||
{
|
{
|
||||||
return usePow2Divisors
|
return mode switch
|
||||||
? _byDivisorHybridOrderTesterPow2.IsPrime(p, out detailedCheck, out _)
|
{
|
||||||
: _byDivisorHybridOrderTester.IsPrime(p, out detailedCheck, out _);
|
ByDivisorKIncrementMode.Pow2Groups => _byDivisorHybridOrderTesterPow2.IsPrime(p, out detailedCheck, out _),
|
||||||
|
ByDivisorKIncrementMode.Predictive => _byDivisorHybridOrderTesterPredictive.IsPrime(p, out detailedCheck, out _),
|
||||||
|
ByDivisorKIncrementMode.Percentile => _byDivisorHybridOrderTesterPercentile.IsPrime(p, out detailedCheck, out _),
|
||||||
|
ByDivisorKIncrementMode.Additive => _byDivisorHybridOrderTesterAdditive.IsPrime(p, out detailedCheck, out _),
|
||||||
|
ByDivisorKIncrementMode.TopDown => _byDivisorHybridOrderTesterTopDown.IsPrime(p, out detailedCheck, out _),
|
||||||
|
_ => _byDivisorHybridOrderTester.IsPrime(p, out detailedCheck, out _),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return usePow2Divisors
|
return mode switch
|
||||||
? _byDivisorGpuOrderTesterPow2.IsPrime(p, out detailedCheck, out _)
|
{
|
||||||
: _byDivisorGpuOrderTester.IsPrime(p, out detailedCheck, out _);
|
ByDivisorKIncrementMode.Pow2Groups => _byDivisorGpuOrderTesterPow2.IsPrime(p, out detailedCheck, out _),
|
||||||
|
ByDivisorKIncrementMode.Predictive => _byDivisorGpuOrderTesterPredictive.IsPrime(p, out detailedCheck, out _),
|
||||||
|
ByDivisorKIncrementMode.Percentile => _byDivisorGpuOrderTesterPercentile.IsPrime(p, out detailedCheck, out _),
|
||||||
|
ByDivisorKIncrementMode.Additive => _byDivisorGpuOrderTesterAdditive.IsPrime(p, out detailedCheck, out _),
|
||||||
|
ByDivisorKIncrementMode.TopDown => _byDivisorGpuOrderTesterTopDown.IsPrime(p, out detailedCheck, out _),
|
||||||
|
_ => _byDivisorGpuOrderTester.IsPrime(p, out detailedCheck, out _),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
detailedCheck = IsMersennePrime(gpu, p);
|
detailedCheck = IsMersennePrime(gpu, p);
|
||||||
@@ -1119,7 +1314,7 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
private static void HandleByDivisorResult(ulong candidate, bool searchedMersenne, bool detailedCheck, bool passedAllTests, BigInteger divisor)
|
private static void HandleByDivisorResult(ulong candidate, bool searchedMersenne, bool detailedCheck, bool passedAllTests, BigInteger divisor)
|
||||||
{
|
{
|
||||||
CalculationResultHandler.HandleResult(candidate, divisor, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP);
|
CalculationResultHandler.HandleResult(candidate, divisor, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP);
|
||||||
if (divisor.IsZero || _byDivisorModelPath is null || EnvironmentConfiguration.ByDivisorClassModel is null)
|
if (divisor.IsZero || !_byDivisorModeUsesModel || EnvironmentConfiguration.ByDivisorClassModel is null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1129,14 +1324,14 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
|
|
||||||
private static void TryRefreshByDivisorModel()
|
private static void TryRefreshByDivisorModel()
|
||||||
{
|
{
|
||||||
string? modelPath = _byDivisorModelPath;
|
if (!_byDivisorModeUsesModel)
|
||||||
if (modelPath is null)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var repository = EnvironmentConfiguration.ByDivisorClassModelRepository;
|
||||||
string resultsPath = CalculationResultHandler.ResultsFileName;
|
string resultsPath = CalculationResultHandler.ResultsFileName;
|
||||||
if (string.IsNullOrEmpty(resultsPath))
|
if (repository is null || string.IsNullOrEmpty(resultsPath))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1165,13 +1360,25 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
{
|
{
|
||||||
double cheapLimit = EnvironmentConfiguration.ByDivisorCheapKLimit;
|
double cheapLimit = EnvironmentConfiguration.ByDivisorCheapKLimit;
|
||||||
Console.WriteLine("Refreshing by-divisor class model from results...");
|
Console.WriteLine("Refreshing by-divisor class model from results...");
|
||||||
ByDivisorClassModelLoader.RebuildAndSave(modelPath, resultsPath, cheapLimit);
|
string tempModelPath = Path.Combine(PerfectNumberConstants.ByDivisorStateDirectory, "bydivisor_k10_model.json");
|
||||||
EnvironmentConfiguration.ByDivisorClassModel = ByDivisorClassModelLoader.LoadOrBuild(
|
ByDivisorClassModelLoader.RebuildAndSave(tempModelPath, resultsPath, cheapLimit);
|
||||||
modelPath,
|
ByDivisorClassModel refreshed = ByDivisorClassModelLoader.LoadOrBuild(
|
||||||
|
tempModelPath,
|
||||||
resultsPath,
|
resultsPath,
|
||||||
EnvironmentConfiguration.ByDivisorScanTuning,
|
EnvironmentConfiguration.ByDivisorScanTuning,
|
||||||
cheapLimit,
|
cheapLimit,
|
||||||
rebuildWhenResultsNewer: false);
|
rebuildWhenResultsNewer: false);
|
||||||
|
repository.Save(refreshed);
|
||||||
|
repository.Commit();
|
||||||
|
EnvironmentConfiguration.ByDivisorClassModel = refreshed;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.Delete(tempModelPath);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore cleanup errors
|
||||||
|
}
|
||||||
Console.WriteLine($"By-divisor class model refreshed (cheap<={cheapLimit:F0}).");
|
Console.WriteLine($"By-divisor class model refreshed (cheap<={cheapLimit:F0}).");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using PerfectNumbers.Core.ByDivisor;
|
using PerfectNumbers.Core.ByDivisor;
|
||||||
@@ -90,6 +91,70 @@ public sealed class ByDivisorClassModelTests
|
|||||||
plan.Rho2.Should().Be(1.8d);
|
plan.Rho2.Should().Be(1.8d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BuildPlan_exposes_percentiles_and_additive_delta()
|
||||||
|
{
|
||||||
|
var model = new ByDivisorClassModel
|
||||||
|
{
|
||||||
|
CheapLimit = 10_000,
|
||||||
|
};
|
||||||
|
var entries = new ByDivisorClassEntry[1024];
|
||||||
|
entries[0] = new ByDivisorClassEntry
|
||||||
|
{
|
||||||
|
K10 = 10d,
|
||||||
|
K25 = 20d,
|
||||||
|
K50 = 30d,
|
||||||
|
K75 = 40d,
|
||||||
|
GapProbability = 0.1d,
|
||||||
|
Stability = "stable",
|
||||||
|
};
|
||||||
|
model.Entries = entries;
|
||||||
|
|
||||||
|
ByDivisorScanTuning tuning = ByDivisorScanTuning.Default;
|
||||||
|
|
||||||
|
ByDivisorScanPlan plan = model.BuildPlan(0UL, BigInteger.Zero, tuning);
|
||||||
|
|
||||||
|
plan.K10.Should().Be(10d);
|
||||||
|
plan.K25.Should().Be(20d);
|
||||||
|
plan.K50.Should().Be(30d);
|
||||||
|
plan.K75.Should().Be(40d);
|
||||||
|
plan.Start.Should().Be(24d); // ceil(0.8 * 30)
|
||||||
|
plan.Target.Should().Be(48d); // ceil(1.2 * 40)
|
||||||
|
plan.DeltaK.Should().Be(4d); // ceil(24 * 0.15)
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BuildPlan_clamps_all_ranges_to_allowed_max()
|
||||||
|
{
|
||||||
|
var model = new ByDivisorClassModel
|
||||||
|
{
|
||||||
|
CheapLimit = 1000d,
|
||||||
|
};
|
||||||
|
var entries = new ByDivisorClassEntry[1024];
|
||||||
|
entries[0] = new ByDivisorClassEntry
|
||||||
|
{
|
||||||
|
K10 = 50d,
|
||||||
|
K25 = 60d,
|
||||||
|
K50 = 70d,
|
||||||
|
K75 = 80d,
|
||||||
|
GapProbability = 0.1d,
|
||||||
|
};
|
||||||
|
model.Entries = entries;
|
||||||
|
|
||||||
|
ByDivisorScanTuning tuning = ByDivisorScanTuning.Default;
|
||||||
|
// allowedMax = 41 => maxKAllowed = (41-1)/(2*5) = 4
|
||||||
|
ByDivisorScanPlan plan = model.BuildPlan(5UL, new BigInteger(41), tuning);
|
||||||
|
|
||||||
|
plan.CheapLimit.Should().Be(4d);
|
||||||
|
plan.K10.Should().Be(4d);
|
||||||
|
plan.K25.Should().Be(4d);
|
||||||
|
plan.K50.Should().Be(4d);
|
||||||
|
plan.K75.Should().Be(4d);
|
||||||
|
plan.Start.Should().Be(4d);
|
||||||
|
plan.Target.Should().Be(4d);
|
||||||
|
plan.DeltaK.Should().Be(4d);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void LoadOrBuild_rebuilds_when_results_newer_than_model()
|
public void LoadOrBuild_rebuilds_when_results_newer_than_model()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ using System.Numerics;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using PerfectNumbers.Core.Cpu;
|
using PerfectNumbers.Core.Cpu;
|
||||||
using MersenneNumberDivisorByDivisorCpuTesterWithCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForOneByOneDivisorSetForCpuOrder;
|
using MersenneNumberDivisorByDivisorCpuTesterWithCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForCpuOrder;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Tests;
|
namespace PerfectNumbers.Core.Tests;
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ namespace PerfectNumbers.Core.ByDivisor;
|
|||||||
|
|
||||||
public sealed class ByDivisorClassEntry
|
public sealed class ByDivisorClassEntry
|
||||||
{
|
{
|
||||||
|
public double K10 { get; set; }
|
||||||
|
public double K25 { get; set; }
|
||||||
public double K50 { get; set; }
|
public double K50 { get; set; }
|
||||||
public double K75 { get; set; }
|
public double K75 { get; set; }
|
||||||
public double GapProbability { get; set; }
|
public double GapProbability { get; set; }
|
||||||
@@ -26,6 +28,11 @@ public sealed class ByDivisorScanPlan
|
|||||||
public double Target { get; init; }
|
public double Target { get; init; }
|
||||||
public double Rho1 { get; init; }
|
public double Rho1 { get; init; }
|
||||||
public double Rho2 { get; init; }
|
public double Rho2 { get; init; }
|
||||||
|
public double K10 { get; init; }
|
||||||
|
public double K25 { get; init; }
|
||||||
|
public double K50 { get; init; }
|
||||||
|
public double K75 { get; init; }
|
||||||
|
public double DeltaK { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ByDivisorScanTuning
|
public sealed class ByDivisorScanTuning
|
||||||
@@ -39,6 +46,7 @@ public sealed class ByDivisorScanTuning
|
|||||||
public double Rho2 { get; init; } = 1.6d;
|
public double Rho2 { get; init; } = 1.6d;
|
||||||
public double AggressiveRho2 { get; init; } = 1.8d;
|
public double AggressiveRho2 { get; init; } = 1.8d;
|
||||||
public double HardGapThreshold { get; init; } = 0.75d;
|
public double HardGapThreshold { get; init; } = 0.75d;
|
||||||
|
public double DeltaFraction { get; init; } = 0.15d;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public static ByDivisorScanTuning Default => new();
|
public static ByDivisorScanTuning Default => new();
|
||||||
@@ -47,10 +55,10 @@ public sealed class ByDivisorScanTuning
|
|||||||
public sealed class ByDivisorClassModel
|
public sealed class ByDivisorClassModel
|
||||||
{
|
{
|
||||||
private const int ClassCount = 1024;
|
private const int ClassCount = 1024;
|
||||||
public int Version { get; set; } = 1;
|
public int Version { get; set; } = 2;
|
||||||
public double CheapLimit { get; set; } = 10_000d;
|
public double CheapLimit { get; set; } = 10_000d;
|
||||||
public ByDivisorClassEntry[] Entries { get; set; } = Enumerable.Range(0, ClassCount)
|
public ByDivisorClassEntry[] Entries { get; set; } = Enumerable.Range(0, ClassCount)
|
||||||
.Select(_ => new ByDivisorClassEntry { K50 = 1d, K75 = 1d })
|
.Select(_ => new ByDivisorClassEntry { K10 = 1d, K25 = 1d, K50 = 1d, K75 = 1d })
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
public ByDivisorScanPlan BuildPlan(ulong prime, BigInteger allowedMax, in ByDivisorScanTuning tuning)
|
public ByDivisorScanPlan BuildPlan(ulong prime, BigInteger allowedMax, in ByDivisorScanTuning tuning)
|
||||||
@@ -58,6 +66,8 @@ public sealed class ByDivisorClassModel
|
|||||||
int suffix = (int)(prime & 1023UL);
|
int suffix = (int)(prime & 1023UL);
|
||||||
ByDivisorClassEntry entry = Entries[suffix];
|
ByDivisorClassEntry entry = Entries[suffix];
|
||||||
|
|
||||||
|
double k10 = entry.K10 > 1d ? entry.K10 : 1d;
|
||||||
|
double k25 = entry.K25 > 1d ? entry.K25 : Math.Max(k10, 1d);
|
||||||
double k50 = entry.K50 > 1d ? entry.K50 : 1d;
|
double k50 = entry.K50 > 1d ? entry.K50 : 1d;
|
||||||
double k75 = entry.K75 > 1d ? entry.K75 : Math.Max(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);
|
bool isHard = entry.GapProbability >= tuning.HardGapThreshold || string.Equals(entry.Stability, "escaping", StringComparison.OrdinalIgnoreCase);
|
||||||
@@ -69,6 +79,7 @@ public sealed class ByDivisorClassModel
|
|||||||
|
|
||||||
double cheap = tuning.CheapLimit > 1d ? tuning.CheapLimit : CheapLimit;
|
double cheap = tuning.CheapLimit > 1d ? tuning.CheapLimit : CheapLimit;
|
||||||
double rho2 = isHard ? tuning.AggressiveRho2 : tuning.Rho2;
|
double rho2 = isHard ? tuning.AggressiveRho2 : tuning.Rho2;
|
||||||
|
double deltaK = Math.Max(1d, Math.Ceiling(start * Math.Max(0d, tuning.DeltaFraction)));
|
||||||
|
|
||||||
double maxKAllowed = ComputeMaxKAllowed(prime, allowedMax);
|
double maxKAllowed = ComputeMaxKAllowed(prime, allowedMax);
|
||||||
if (maxKAllowed > 0d)
|
if (maxKAllowed > 0d)
|
||||||
@@ -76,6 +87,11 @@ public sealed class ByDivisorClassModel
|
|||||||
cheap = Math.Min(cheap, maxKAllowed);
|
cheap = Math.Min(cheap, maxKAllowed);
|
||||||
start = Math.Min(start, maxKAllowed);
|
start = Math.Min(start, maxKAllowed);
|
||||||
target = Math.Min(target, maxKAllowed);
|
target = Math.Min(target, maxKAllowed);
|
||||||
|
k10 = Math.Min(k10, maxKAllowed);
|
||||||
|
k25 = Math.Min(k25, maxKAllowed);
|
||||||
|
k50 = Math.Min(k50, maxKAllowed);
|
||||||
|
k75 = Math.Min(k75, maxKAllowed);
|
||||||
|
deltaK = Math.Min(deltaK, maxKAllowed);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ByDivisorScanPlan
|
return new ByDivisorScanPlan
|
||||||
@@ -85,6 +101,11 @@ public sealed class ByDivisorClassModel
|
|||||||
Target = target,
|
Target = target,
|
||||||
Rho1 = tuning.Rho1,
|
Rho1 = tuning.Rho1,
|
||||||
Rho2 = rho2,
|
Rho2 = rho2,
|
||||||
|
K10 = k10,
|
||||||
|
K25 = k25,
|
||||||
|
K50 = k50,
|
||||||
|
K75 = k75,
|
||||||
|
DeltaK = deltaK,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,6 +170,7 @@ public static class ByDivisorClassModelLoader
|
|||||||
|
|
||||||
private static void Save(string modelPath, ByDivisorClassModel model)
|
private static void Save(string modelPath, ByDivisorClassModel model)
|
||||||
{
|
{
|
||||||
|
string tempPath = modelPath + ".tmp";
|
||||||
string? directory = Path.GetDirectoryName(modelPath);
|
string? directory = Path.GetDirectoryName(modelPath);
|
||||||
if (!string.IsNullOrEmpty(directory))
|
if (!string.IsNullOrEmpty(directory))
|
||||||
{
|
{
|
||||||
@@ -160,11 +182,13 @@ public static class ByDivisorClassModelLoader
|
|||||||
WriteIndented = true,
|
WriteIndented = true,
|
||||||
};
|
};
|
||||||
string json = JsonSerializer.Serialize(model, options);
|
string json = JsonSerializer.Serialize(model, options);
|
||||||
string tempPath = modelPath + ".tmp";
|
lock (IoLock)
|
||||||
|
{
|
||||||
File.WriteAllText(tempPath, json);
|
File.WriteAllText(tempPath, json);
|
||||||
File.Copy(tempPath, modelPath, overwrite: true);
|
File.Copy(tempPath, modelPath, overwrite: true);
|
||||||
File.Delete(tempPath);
|
File.Delete(tempPath);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static ByDivisorClassModel BuildFromResults(string resultsPath, double cheapLimit)
|
private static ByDivisorClassModel BuildFromResults(string resultsPath, double cheapLimit)
|
||||||
{
|
{
|
||||||
@@ -251,11 +275,15 @@ public static class ByDivisorClassModelLoader
|
|||||||
for (int i = 0; i < 1024; i++)
|
for (int i = 0; i < 1024; i++)
|
||||||
{
|
{
|
||||||
List<double>? classSamples = samples[i];
|
List<double>? classSamples = samples[i];
|
||||||
|
double k10 = 1d;
|
||||||
|
double k25 = 1d;
|
||||||
double k50 = 1d;
|
double k50 = 1d;
|
||||||
double k75 = 1d;
|
double k75 = 1d;
|
||||||
if (classSamples is not null && classSamples.Count > 0)
|
if (classSamples is not null && classSamples.Count > 0)
|
||||||
{
|
{
|
||||||
classSamples.Sort();
|
classSamples.Sort();
|
||||||
|
k10 = ComputePercentile(classSamples, 0.10d);
|
||||||
|
k25 = ComputePercentile(classSamples, 0.25d);
|
||||||
k50 = ComputePercentile(classSamples, 0.5d);
|
k50 = ComputePercentile(classSamples, 0.5d);
|
||||||
k75 = ComputePercentile(classSamples, 0.75d);
|
k75 = ComputePercentile(classSamples, 0.75d);
|
||||||
}
|
}
|
||||||
@@ -266,6 +294,8 @@ public static class ByDivisorClassModelLoader
|
|||||||
|
|
||||||
model.Entries[i] = new ByDivisorClassEntry
|
model.Entries[i] = new ByDivisorClassEntry
|
||||||
{
|
{
|
||||||
|
K10 = k10,
|
||||||
|
K25 = k25,
|
||||||
K50 = k50,
|
K50 = k50,
|
||||||
K75 = k75,
|
K75 = k75,
|
||||||
GapProbability = gapProb,
|
GapProbability = gapProb,
|
||||||
@@ -341,4 +371,6 @@ public static class ByDivisorClassModelLoader
|
|||||||
double fraction = index - lower;
|
double fraction = index - lower;
|
||||||
return values[lower] + ((values[upper] - values[lower]) * fraction);
|
return values[lower] + ((values[upper] - values[lower]) * fraction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly object IoLock = new();
|
||||||
}
|
}
|
||||||
@@ -7,14 +7,18 @@ using System.Collections.Concurrent;
|
|||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
using PerfectNumbers.Core.ByDivisor;
|
using PerfectNumbers.Core.ByDivisor;
|
||||||
|
using PerfectNumbers.Core.Persistence;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Cpu;
|
namespace PerfectNumbers.Core.Cpu;
|
||||||
|
|
||||||
public enum DivisorSet
|
public enum DivisorSet
|
||||||
{
|
{
|
||||||
OneByOne,
|
Sequential,
|
||||||
Pow2Groups,
|
Pow2Groups,
|
||||||
Predictive,
|
Predictive,
|
||||||
|
Percentile,
|
||||||
|
Additive,
|
||||||
|
TopDown,
|
||||||
}
|
}
|
||||||
|
|
||||||
[EnumDependentTemplate(typeof(DivisorSet))]
|
[EnumDependentTemplate(typeof(DivisorSet))]
|
||||||
@@ -36,10 +40,18 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
private int _stateCounter;
|
private int _stateCounter;
|
||||||
private BigInteger _lastSavedK;
|
private BigInteger _lastSavedK;
|
||||||
private bool _pow2MinusOneChecked;
|
private bool _pow2MinusOneChecked;
|
||||||
private string _pow2MinusOneStateFilePath = string.Empty;
|
private Pow2Minus1StateRepository? _pow2MinusOneRepository;
|
||||||
|
private KStateRepository _kRepository = null!;
|
||||||
|
private KStateRepository? _cheapRepository;
|
||||||
|
private BigInteger _cheapLastSavedK;
|
||||||
|
private BigInteger _cheapResumeK;
|
||||||
|
|
||||||
private GpuUInt128WorkSet _divisorScanGpuWorkSet;
|
private GpuUInt128WorkSet _divisorScanGpuWorkSet;
|
||||||
private static readonly ConcurrentDictionary<ulong, byte> _checkedPow2MinusOne = new();
|
private static readonly ConcurrentDictionary<ulong, byte> _checkedPow2MinusOne = new();
|
||||||
|
private ulong _currentPrime;
|
||||||
|
#if DivisorSet_TopDown
|
||||||
|
private BigInteger _topDownCursor;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if DivisorSet_Pow2Groups
|
#if DivisorSet_Pow2Groups
|
||||||
private const int PercentScale = DivisorByDivisorConfig.PercentScale;
|
private const int PercentScale = DivisorByDivisorConfig.PercentScale;
|
||||||
@@ -68,6 +80,8 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
private int _preparedSpecialRangePromille;
|
private int _preparedSpecialRangePromille;
|
||||||
private int[] _preparedSpecialSingles = Array.Empty<int>();
|
private int[] _preparedSpecialSingles = Array.Empty<int>();
|
||||||
private (int Start, int End)[] _preparedSpecialRanges = Array.Empty<(int Start, int End)>();
|
private (int Start, int End)[] _preparedSpecialRanges = Array.Empty<(int Start, int End)>();
|
||||||
|
private KStateRepository? _specialRepository;
|
||||||
|
private KStateRepository? _groupsRepository;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -197,11 +211,59 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private bool TryBuildCheapRange(in BigInteger minK, in BigInteger chunkSize, in BigInteger maxKAllowed, out BigInteger start, out BigInteger end)
|
||||||
|
{
|
||||||
|
start = minK > BigInteger.One ? minK : BigInteger.One;
|
||||||
|
if (_cheapResumeK > start)
|
||||||
|
{
|
||||||
|
start = _cheapResumeK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start > maxKAllowed)
|
||||||
|
{
|
||||||
|
end = BigInteger.Zero;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger span = chunkSize > BigInteger.One ? chunkSize - BigInteger.One : BigInteger.Zero;
|
||||||
|
end = start + span;
|
||||||
|
if (end > maxKAllowed)
|
||||||
|
{
|
||||||
|
end = maxKAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return end >= start;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private void RecordCheapState(in BigInteger k)
|
||||||
|
{
|
||||||
|
if (_currentPrime == 0UL || _cheapRepository is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (k <= _cheapLastSavedK)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cheapRepository.Upsert(_currentPrime, k);
|
||||||
|
_cheapLastSavedK = k;
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public void ResetStateTracking()
|
public void ResetStateTracking()
|
||||||
{
|
{
|
||||||
_stateCounter = 0;
|
_stateCounter = 0;
|
||||||
_pow2MinusOneChecked = false;
|
_pow2MinusOneChecked = false;
|
||||||
|
_currentPrime = 0UL;
|
||||||
|
_cheapLastSavedK = BigInteger.Zero;
|
||||||
|
_cheapResumeK = BigInteger.One;
|
||||||
|
#if DivisorSet_TopDown
|
||||||
|
_topDownCursor = BigInteger.Zero;
|
||||||
|
#endif
|
||||||
#if DivisorSet_Pow2Groups
|
#if DivisorSet_Pow2Groups
|
||||||
_specialStateCounter = 0;
|
_specialStateCounter = 0;
|
||||||
_groupsStateCounter = 0;
|
_groupsStateCounter = 0;
|
||||||
@@ -217,21 +279,49 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
_lastSavedK = lastSavedK;
|
_lastSavedK = lastSavedK;
|
||||||
MinK = minK;
|
MinK = minK;
|
||||||
_stateCounter = 0;
|
_stateCounter = 0;
|
||||||
_pow2MinusOneStateFilePath = stateFile + ".pow2minus1";
|
_kRepository = EnvironmentConfiguration.ByDivisorKStateRepository ?? throw new InvalidOperationException("By-divisor k-state repository not initialized.");
|
||||||
_pow2MinusOneChecked = File.Exists(_pow2MinusOneStateFilePath);
|
_pow2MinusOneRepository = EnvironmentConfiguration.ByDivisorPow2Minus1Repository;
|
||||||
#if DivisorSet_Pow2Groups
|
_cheapRepository = EnvironmentConfiguration.ByDivisorCheapKStateRepository;
|
||||||
_specialStateFilePath = stateFile + ".special";
|
_cheapLastSavedK = BigInteger.Zero;
|
||||||
_groupsStateFilePath = stateFile + ".groups";
|
_cheapResumeK = MinK;
|
||||||
_specialStateCounter = 0;
|
|
||||||
_groupsStateCounter = 0;
|
string stateName = Path.GetFileNameWithoutExtension(stateFile);
|
||||||
if (!MersenneNumberDivisorByDivisorTester.TryReadLastSavedK(_specialStateFilePath, out _specialLastSavedK))
|
if (ulong.TryParse(stateName, NumberStyles.None, CultureInfo.InvariantCulture, out ulong parsedPrime))
|
||||||
{
|
{
|
||||||
_specialLastSavedK = lastSavedK;
|
_currentPrime = parsedPrime;
|
||||||
|
if (_kRepository.TryGet(parsedPrime, out BigInteger storedK) && storedK > BigInteger.Zero)
|
||||||
|
{
|
||||||
|
_lastSavedK = storedK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!MersenneNumberDivisorByDivisorTester.TryReadLastSavedK(_groupsStateFilePath, out _groupsLastSavedK))
|
_pow2MinusOneChecked = _pow2MinusOneRepository?.IsChecked(parsedPrime) ?? false;
|
||||||
|
if (_cheapRepository is not null && _cheapRepository.TryGet(parsedPrime, out BigInteger cheapStored) && cheapStored > BigInteger.Zero)
|
||||||
{
|
{
|
||||||
|
_cheapLastSavedK = cheapStored;
|
||||||
|
_cheapResumeK = cheapStored + BigInteger.One;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if DivisorSet_TopDown
|
||||||
|
_topDownCursor = _lastSavedK;
|
||||||
|
#endif
|
||||||
|
#if DivisorSet_Pow2Groups
|
||||||
|
_specialRepository = EnvironmentConfiguration.ByDivisorSpecialStateRepository;
|
||||||
|
_groupsRepository = EnvironmentConfiguration.ByDivisorGroupsStateRepository;
|
||||||
|
_specialStateCounter = 0;
|
||||||
|
_groupsStateCounter = 0;
|
||||||
|
_specialLastSavedK = lastSavedK;
|
||||||
_groupsLastSavedK = lastSavedK;
|
_groupsLastSavedK = lastSavedK;
|
||||||
|
if (_currentPrime != 0UL)
|
||||||
|
{
|
||||||
|
if (_specialRepository is not null && _specialRepository.TryGet(_currentPrime, out BigInteger storedSpecial) && storedSpecial > BigInteger.Zero)
|
||||||
|
{
|
||||||
|
_specialLastSavedK = storedSpecial;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_groupsRepository is not null && _groupsRepository.TryGet(_currentPrime, out BigInteger storedGroups) && storedGroups > BigInteger.Zero)
|
||||||
|
{
|
||||||
|
_groupsLastSavedK = storedGroups;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_specialResumeK = _specialLastSavedK + BigInteger.One;
|
_specialResumeK = _specialLastSavedK + BigInteger.One;
|
||||||
@@ -244,6 +334,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||||
public bool IsPrime(ulong prime, out bool divisorsExhausted, out BigInteger divisor)
|
public bool IsPrime(ulong prime, out bool divisorsExhausted, out BigInteger divisor)
|
||||||
{
|
{
|
||||||
|
_currentPrime = prime;
|
||||||
BigInteger allowedMax = MersenneNumberDivisorByDivisorTester.ComputeAllowedMaxDivisorBig(prime, DivisorLimit);
|
BigInteger allowedMax = MersenneNumberDivisorByDivisorTester.ComputeAllowedMaxDivisorBig(prime, DivisorLimit);
|
||||||
|
|
||||||
if (TryFindPowerOfTwoMinusOneDivisor(prime, allowedMax, out divisor))
|
if (TryFindPowerOfTwoMinusOneDivisor(prime, allowedMax, out divisor))
|
||||||
@@ -341,7 +432,16 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
// Console.WriteLine($"[by-divisor-plan] p={prime} suffix={(prime & 1023UL)} using pow2-groups plan; model skipped.");
|
// Console.WriteLine($"[by-divisor-plan] p={prime} suffix={(prime & 1023UL)} using pow2-groups plan; model skipped.");
|
||||||
return compositePow2;
|
return compositePow2;
|
||||||
#else
|
#else
|
||||||
#if DivisorSet_Predictive
|
#if DivisorSet_TopDown
|
||||||
|
bool compositeTopDown = CheckDivisorsTopDown(
|
||||||
|
prime,
|
||||||
|
allowedMax,
|
||||||
|
normalizedMinK,
|
||||||
|
out processedAll,
|
||||||
|
out foundDivisor);
|
||||||
|
return compositeTopDown;
|
||||||
|
#endif
|
||||||
|
#if DivisorSet_Predictive || DivisorSet_Percentile || DivisorSet_Additive
|
||||||
ByDivisorClassModel? classifier = EnvironmentConfiguration.ByDivisorClassModel;
|
ByDivisorClassModel? classifier = EnvironmentConfiguration.ByDivisorClassModel;
|
||||||
if (classifier is not null)
|
if (classifier is not null)
|
||||||
{
|
{
|
||||||
@@ -591,7 +691,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !DivisorSet_Pow2Groups && DivisorSet_Predictive
|
#if !DivisorSet_Pow2Groups && (DivisorSet_Predictive || DivisorSet_Percentile || DivisorSet_Additive)
|
||||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||||
private bool TryExecutePlannedScan(
|
private bool TryExecutePlannedScan(
|
||||||
ByDivisorClassModel model,
|
ByDivisorClassModel model,
|
||||||
@@ -675,24 +775,28 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
ByDivisorScanTuning tuning = EnvironmentConfiguration.ByDivisorScanTuning;
|
ByDivisorScanTuning tuning = EnvironmentConfiguration.ByDivisorScanTuning;
|
||||||
ByDivisorScanPlan plan = model.BuildPlan(prime, allowedMax, tuning);
|
ByDivisorScanPlan plan = model.BuildPlan(prime, allowedMax, tuning);
|
||||||
int suffix = (int)(prime & 1023UL);
|
int suffix = (int)(prime & 1023UL);
|
||||||
|
_ = suffix;
|
||||||
// ByDivisorClassEntry entry = model.Entries[suffix];
|
// 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}");
|
// 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);
|
BigInteger cheapChunk = BigInteger.Min((BigInteger)plan.CheapLimit, maxKAllowed);
|
||||||
if (cheapLimit >= minK)
|
BigInteger cheapStart = BigInteger.Zero;
|
||||||
|
BigInteger cheapEnd = BigInteger.Zero;
|
||||||
|
if (TryBuildCheapRange(minK, cheapChunk, maxKAllowed, out cheapStart, out cheapEnd))
|
||||||
{
|
{
|
||||||
BigInteger currentK = minK;
|
BigInteger currentK = cheapStart;
|
||||||
bool compositeCheap = CheckDivisorsBigPlanRange(
|
bool compositeCheap = CheckDivisorsBigPlanRange(
|
||||||
prime,
|
prime,
|
||||||
step,
|
step,
|
||||||
allowedMax,
|
allowedMax,
|
||||||
minK,
|
cheapStart,
|
||||||
cheapLimit,
|
cheapEnd,
|
||||||
out bool processedRange,
|
out bool processedRange,
|
||||||
out BigInteger foundRange,
|
out BigInteger foundRange,
|
||||||
ref currentK);
|
ref currentK);
|
||||||
if (compositeCheap)
|
if (compositeCheap)
|
||||||
{
|
{
|
||||||
|
RecordCheapState(currentK);
|
||||||
foundDivisor = foundRange;
|
foundDivisor = foundRange;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -702,6 +806,8 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
processedAll = false;
|
processedAll = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecordCheapState(cheapEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
BigInteger startPlan = plan.Start <= 0d ? minK : new BigInteger(Math.Ceiling(plan.Start));
|
BigInteger startPlan = plan.Start <= 0d ? minK : new BigInteger(Math.Ceiling(plan.Start));
|
||||||
@@ -716,12 +822,13 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
target = startPlan + BigInteger.One;
|
target = startPlan + BigInteger.One;
|
||||||
}
|
}
|
||||||
|
|
||||||
BigInteger current = cheapLimit + BigInteger.One;
|
BigInteger current = (cheapEnd > BigInteger.Zero ? cheapEnd + BigInteger.One : minK);
|
||||||
if (current < startPlan)
|
if (current < startPlan)
|
||||||
{
|
{
|
||||||
current = startPlan;
|
current = startPlan;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DivisorSet_Predictive
|
||||||
bool loggedProgressive = false;
|
bool loggedProgressive = false;
|
||||||
while (current <= maxKAllowed)
|
while (current <= maxKAllowed)
|
||||||
{
|
{
|
||||||
@@ -768,6 +875,163 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
|
|
||||||
current = advanced;
|
current = advanced;
|
||||||
}
|
}
|
||||||
|
#elif DivisorSet_Percentile
|
||||||
|
BigInteger k10 = plan.K10 > 0d ? new BigInteger(Math.Ceiling(plan.K10)) : current;
|
||||||
|
BigInteger k25 = plan.K25 > 0d ? new BigInteger(Math.Ceiling(plan.K25)) : k10 + BigInteger.One;
|
||||||
|
BigInteger k50 = plan.K50 > 0d ? new BigInteger(Math.Ceiling(plan.K50)) : k25 + BigInteger.One;
|
||||||
|
BigInteger k75 = plan.K75 > 0d ? new BigInteger(Math.Ceiling(plan.K75)) : k50 + BigInteger.One;
|
||||||
|
|
||||||
|
BigInteger[] stops = new BigInteger[]
|
||||||
|
{
|
||||||
|
k25 - BigInteger.One,
|
||||||
|
k50 - BigInteger.One,
|
||||||
|
k75 - BigInteger.One,
|
||||||
|
};
|
||||||
|
|
||||||
|
BigInteger startRangeK = current > k10 ? current : k10;
|
||||||
|
for (int i = 0; i < stops.Length; i++)
|
||||||
|
{
|
||||||
|
BigInteger endRange = stops[i];
|
||||||
|
if (endRange < startRangeK)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool composite = CheckDivisorsBigPlanRange(
|
||||||
|
prime,
|
||||||
|
step,
|
||||||
|
allowedMax,
|
||||||
|
startRangeK,
|
||||||
|
endRange,
|
||||||
|
out bool processedRange,
|
||||||
|
out BigInteger foundRange,
|
||||||
|
ref startRangeK);
|
||||||
|
if (composite)
|
||||||
|
{
|
||||||
|
foundDivisor = foundRange;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!processedRange)
|
||||||
|
{
|
||||||
|
processedAll = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
startRangeK = endRange + BigInteger.One;
|
||||||
|
if (startRangeK > maxKAllowed)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// After percentile sweeps, escalate geometrically.
|
||||||
|
BigInteger startGeo = startRangeK;
|
||||||
|
if (startGeo < k75)
|
||||||
|
{
|
||||||
|
startGeo = k75;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool loggedPercentile = false;
|
||||||
|
while (startGeo <= maxKAllowed)
|
||||||
|
{
|
||||||
|
if (!loggedPercentile)
|
||||||
|
{
|
||||||
|
loggedPercentile = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger scanStart = startGeo;
|
||||||
|
BigInteger scanEnd = scanStart;
|
||||||
|
bool composite = CheckDivisorsBigPlanRange(
|
||||||
|
prime,
|
||||||
|
step,
|
||||||
|
allowedMax,
|
||||||
|
scanStart,
|
||||||
|
scanEnd,
|
||||||
|
out bool processedRange,
|
||||||
|
out BigInteger foundRange,
|
||||||
|
ref scanStart);
|
||||||
|
if (composite)
|
||||||
|
{
|
||||||
|
foundDivisor = foundRange;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!processedRange)
|
||||||
|
{
|
||||||
|
processedAll = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger advanced = new BigInteger(Math.Ceiling((double)startGeo * plan.Rho2));
|
||||||
|
if (advanced <= startGeo)
|
||||||
|
{
|
||||||
|
advanced = startGeo + BigInteger.One;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (advanced > maxKAllowed)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
startGeo = advanced;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
BigInteger delta = plan.DeltaK > 0d ? new BigInteger(Math.Ceiling(plan.DeltaK)) : BigInteger.One;
|
||||||
|
if (delta < BigInteger.One)
|
||||||
|
{
|
||||||
|
delta = BigInteger.One;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delta > maxKAllowed)
|
||||||
|
{
|
||||||
|
delta = maxKAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool loggedAdditive = false;
|
||||||
|
while (current <= maxKAllowed)
|
||||||
|
{
|
||||||
|
if (!loggedAdditive)
|
||||||
|
{
|
||||||
|
loggedAdditive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger startRange = current;
|
||||||
|
bool composite = CheckDivisorsBigPlanRange(
|
||||||
|
prime,
|
||||||
|
step,
|
||||||
|
allowedMax,
|
||||||
|
startRange,
|
||||||
|
startRange,
|
||||||
|
out bool processedRange,
|
||||||
|
out BigInteger foundRange,
|
||||||
|
ref startRange);
|
||||||
|
if (composite)
|
||||||
|
{
|
||||||
|
foundDivisor = foundRange;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!processedRange)
|
||||||
|
{
|
||||||
|
processedAll = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger advanced = current + delta;
|
||||||
|
if (advanced <= current)
|
||||||
|
{
|
||||||
|
advanced = current + BigInteger.One;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (advanced > maxKAllowed)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = advanced;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -885,6 +1149,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
ByDivisorScanTuning tuning = EnvironmentConfiguration.ByDivisorScanTuning;
|
ByDivisorScanTuning tuning = EnvironmentConfiguration.ByDivisorScanTuning;
|
||||||
ByDivisorScanPlan plan = model.BuildPlan(prime, allowedMax == ulong.MaxValue ? BigInteger.Zero : new BigInteger(allowedMax), tuning);
|
ByDivisorScanPlan plan = model.BuildPlan(prime, allowedMax == ulong.MaxValue ? BigInteger.Zero : new BigInteger(allowedMax), tuning);
|
||||||
int suffix = (int)(prime & 1023UL);
|
int suffix = (int)(prime & 1023UL);
|
||||||
|
_ = suffix;
|
||||||
// ByDivisorClassEntry entry = model.Entries[suffix];
|
// 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}");
|
// 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}");
|
||||||
|
|
||||||
@@ -896,21 +1161,28 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong cheapLimit = (ulong)Math.Min(plan.CheapLimit, maxKAllowed);
|
BigInteger cheapChunk = new BigInteger(Math.Min(plan.CheapLimit, (double)maxKAllowed));
|
||||||
if (cheapLimit >= minK)
|
BigInteger cheapStart = BigInteger.Zero;
|
||||||
|
BigInteger cheapEnd = BigInteger.Zero;
|
||||||
|
if (TryBuildCheapRange(new BigInteger(minK), cheapChunk, new BigInteger(maxKAllowed), out cheapStart, out cheapEnd))
|
||||||
{
|
{
|
||||||
ulong currentK = minK;
|
if (cheapStart <= ulong.MaxValue && cheapEnd <= ulong.MaxValue)
|
||||||
|
{
|
||||||
|
ulong cheapStart64 = (ulong)cheapStart;
|
||||||
|
ulong cheapEnd64 = (ulong)cheapEnd;
|
||||||
|
ulong currentK = cheapStart64;
|
||||||
bool composite = CheckDivisors64Range(
|
bool composite = CheckDivisors64Range(
|
||||||
prime,
|
prime,
|
||||||
step64,
|
step64,
|
||||||
effectiveAllowed,
|
effectiveAllowed,
|
||||||
minK,
|
cheapStart64,
|
||||||
cheapLimit,
|
cheapEnd64,
|
||||||
ref currentK,
|
ref currentK,
|
||||||
out bool processedRange,
|
out bool processedRange,
|
||||||
out ulong foundRange);
|
out ulong foundRange);
|
||||||
if (composite)
|
if (composite)
|
||||||
{
|
{
|
||||||
|
RecordCheapState(currentK);
|
||||||
foundDivisor = foundRange;
|
foundDivisor = foundRange;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -920,9 +1192,12 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
processedAll = false;
|
processedAll = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecordCheapState(cheapEnd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double nextK = Math.Max(plan.Start, cheapLimit + 1d);
|
double nextK = Math.Max(plan.Start, (cheapEnd > BigInteger.Zero ? (double)(cheapEnd + BigInteger.One) : minK));
|
||||||
if (nextK < minK)
|
if (nextK < minK)
|
||||||
{
|
{
|
||||||
nextK = minK;
|
nextK = minK;
|
||||||
@@ -935,6 +1210,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
}
|
}
|
||||||
|
|
||||||
ulong current = (ulong)Math.Ceiling(nextK);
|
ulong current = (ulong)Math.Ceiling(nextK);
|
||||||
|
#if DivisorSet_Predictive
|
||||||
bool loggedProgressive = false;
|
bool loggedProgressive = false;
|
||||||
while (current <= maxKAllowed)
|
while (current <= maxKAllowed)
|
||||||
{
|
{
|
||||||
@@ -980,6 +1256,145 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
|
|
||||||
current = (ulong)advanced;
|
current = (ulong)advanced;
|
||||||
}
|
}
|
||||||
|
#elif DivisorSet_Percentile
|
||||||
|
ulong k10 = plan.K10 > 0d ? (ulong)Math.Ceiling(plan.K10) : current;
|
||||||
|
ulong k25 = plan.K25 > 0d ? (ulong)Math.Ceiling(plan.K25) : k10 + 1UL;
|
||||||
|
ulong k50 = plan.K50 > 0d ? (ulong)Math.Ceiling(plan.K50) : k25 + 1UL;
|
||||||
|
ulong k75 = plan.K75 > 0d ? (ulong)Math.Ceiling(plan.K75) : k50 + 1UL;
|
||||||
|
|
||||||
|
ReadOnlySpan<ulong> stops = stackalloc ulong[3]
|
||||||
|
{
|
||||||
|
k25 > 0UL ? k25 - 1UL : 0UL,
|
||||||
|
k50 > 0UL ? k50 - 1UL : 0UL,
|
||||||
|
k75 > 0UL ? k75 - 1UL : 0UL,
|
||||||
|
};
|
||||||
|
|
||||||
|
ulong rangeStart = current > k10 ? current : k10;
|
||||||
|
for (int i = 0; i < stops.Length; i++)
|
||||||
|
{
|
||||||
|
ulong stop = stops[i];
|
||||||
|
if (stop < rangeStart)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong currentK = rangeStart;
|
||||||
|
bool composite = CheckDivisors64Range(
|
||||||
|
prime,
|
||||||
|
step64,
|
||||||
|
effectiveAllowed,
|
||||||
|
rangeStart,
|
||||||
|
stop,
|
||||||
|
ref currentK,
|
||||||
|
out bool processedRange,
|
||||||
|
out ulong foundRange);
|
||||||
|
if (composite)
|
||||||
|
{
|
||||||
|
foundDivisor = foundRange;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!processedRange)
|
||||||
|
{
|
||||||
|
processedAll = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
rangeStart = stop + 1UL;
|
||||||
|
if (rangeStart > maxKAllowed)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong geoStart = rangeStart < k75 ? k75 : rangeStart;
|
||||||
|
while (geoStart <= maxKAllowed)
|
||||||
|
{
|
||||||
|
ulong currentK = geoStart;
|
||||||
|
bool composite = CheckDivisors64Range(
|
||||||
|
prime,
|
||||||
|
step64,
|
||||||
|
effectiveAllowed,
|
||||||
|
currentK,
|
||||||
|
currentK,
|
||||||
|
ref currentK,
|
||||||
|
out bool processedSingle,
|
||||||
|
out ulong foundSingle);
|
||||||
|
if (composite)
|
||||||
|
{
|
||||||
|
foundDivisor = foundSingle;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!processedSingle)
|
||||||
|
{
|
||||||
|
processedAll = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
double advanced = Math.Ceiling(geoStart * plan.Rho2);
|
||||||
|
if (advanced <= geoStart)
|
||||||
|
{
|
||||||
|
advanced = geoStart + 1d;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (advanced > maxKAllowed)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
geoStart = (ulong)advanced;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ulong delta = plan.DeltaK > 0d ? (ulong)Math.Ceiling(plan.DeltaK) : 1UL;
|
||||||
|
if (delta == 0UL)
|
||||||
|
{
|
||||||
|
delta = 1UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delta > maxKAllowed)
|
||||||
|
{
|
||||||
|
delta = maxKAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (current <= maxKAllowed)
|
||||||
|
{
|
||||||
|
ulong currentK = current;
|
||||||
|
bool composite = CheckDivisors64Range(
|
||||||
|
prime,
|
||||||
|
step64,
|
||||||
|
effectiveAllowed,
|
||||||
|
currentK,
|
||||||
|
currentK,
|
||||||
|
ref currentK,
|
||||||
|
out bool processedSingle,
|
||||||
|
out ulong foundSingle);
|
||||||
|
if (composite)
|
||||||
|
{
|
||||||
|
foundDivisor = foundSingle;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!processedSingle)
|
||||||
|
{
|
||||||
|
processedAll = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong advanced = current + delta;
|
||||||
|
if (advanced <= current || advanced == 0UL)
|
||||||
|
{
|
||||||
|
advanced = current + 1UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (advanced > maxKAllowed)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = advanced;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1809,6 +2224,8 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(targetPath))
|
||||||
|
{
|
||||||
string? directory = Path.GetDirectoryName(targetPath);
|
string? directory = Path.GetDirectoryName(targetPath);
|
||||||
if (!string.IsNullOrEmpty(directory))
|
if (!string.IsNullOrEmpty(directory))
|
||||||
{
|
{
|
||||||
@@ -1816,6 +2233,8 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
}
|
}
|
||||||
|
|
||||||
File.AppendAllText(targetPath, k.ToString(CultureInfo.InvariantCulture) + Environment.NewLine);
|
File.AppendAllText(targetPath, k.ToString(CultureInfo.InvariantCulture) + Environment.NewLine);
|
||||||
|
}
|
||||||
|
|
||||||
counter = 0;
|
counter = 0;
|
||||||
last = k;
|
last = k;
|
||||||
}
|
}
|
||||||
@@ -2103,6 +2522,162 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
out foundDivisor);
|
out foundDivisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DivisorSet_TopDown
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private bool CheckDivisorsTopDown(
|
||||||
|
ulong prime,
|
||||||
|
BigInteger allowedMax,
|
||||||
|
BigInteger minK,
|
||||||
|
out bool processedAll,
|
||||||
|
out BigInteger foundDivisor)
|
||||||
|
{
|
||||||
|
foundDivisor = BigInteger.Zero;
|
||||||
|
processedAll = false;
|
||||||
|
|
||||||
|
BigInteger normalizedMinK = minK >= BigInteger.One ? minK : BigInteger.One;
|
||||||
|
ulong step = prime << 1;
|
||||||
|
ulong allowedMax64 = allowedMax.IsZero ? ulong.MaxValue : (allowedMax > ulong.MaxValue ? ulong.MaxValue : (ulong)allowedMax);
|
||||||
|
if (allowedMax64 <= 1UL)
|
||||||
|
{
|
||||||
|
processedAll = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong maxKAllowed = (allowedMax64 - 1UL) / step;
|
||||||
|
{
|
||||||
|
BigInteger maxKBig = maxKAllowed;
|
||||||
|
if (TryClampMaxKForMersenneTail(prime, (BigInteger)step, ref maxKBig))
|
||||||
|
{
|
||||||
|
maxKAllowed = maxKBig > ulong.MaxValue ? ulong.MaxValue : (ulong)maxKBig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxKAllowed < 1UL)
|
||||||
|
{
|
||||||
|
processedAll = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedMinK > maxKAllowed)
|
||||||
|
{
|
||||||
|
processedAll = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong minK64 = normalizedMinK > ulong.MaxValue ? ulong.MaxValue : (ulong)normalizedMinK;
|
||||||
|
if (minK64 < 1UL)
|
||||||
|
{
|
||||||
|
minK64 = 1UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong cursor = _topDownCursor > ulong.MaxValue ? ulong.MaxValue : (ulong)_topDownCursor;
|
||||||
|
ulong currentK = maxKAllowed;
|
||||||
|
if (cursor > 0 && cursor <= maxKAllowed)
|
||||||
|
{
|
||||||
|
currentK = maxKAllowed - cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentK < minK64)
|
||||||
|
{
|
||||||
|
processedAll = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt128 step128 = step;
|
||||||
|
UInt128 divisor = (step128 * currentK) + UInt128.One;
|
||||||
|
var residueStepper = new MersenneDivisorResidueStepperDescending(prime, new GpuUInt128(step), new GpuUInt128(divisor));
|
||||||
|
while (currentK >= minK64)
|
||||||
|
{
|
||||||
|
if (divisor > allowedMax64)
|
||||||
|
{
|
||||||
|
processedAll = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (residueStepper.IsAdmissible())
|
||||||
|
{
|
||||||
|
ulong divisor64 = (ulong)divisor;
|
||||||
|
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor64);
|
||||||
|
#if DEVICE_GPU
|
||||||
|
bool computed = MersenneNumberDivisorByDivisorTester.TryCalculateCycleLengthForExponentGpu(
|
||||||
|
divisor64,
|
||||||
|
Accelerator,
|
||||||
|
prime,
|
||||||
|
divisorData,
|
||||||
|
out ulong computedCycle,
|
||||||
|
out bool primeOrderFailed);
|
||||||
|
#elif DEVICE_HYBRID
|
||||||
|
bool computed = MersenneNumberDivisorByDivisorTester.TryCalculateCycleLengthForExponentHybrid(
|
||||||
|
divisor64,
|
||||||
|
Accelerator,
|
||||||
|
prime,
|
||||||
|
divisorData,
|
||||||
|
out ulong computedCycle,
|
||||||
|
out bool primeOrderFailed);
|
||||||
|
#else
|
||||||
|
bool computed = MersenneNumberDivisorByDivisorTester.TryCalculateCycleLengthForExponentCpu(
|
||||||
|
divisor64,
|
||||||
|
prime,
|
||||||
|
divisorData,
|
||||||
|
out ulong computedCycle,
|
||||||
|
out bool primeOrderFailed);
|
||||||
|
#endif
|
||||||
|
ulong divisorCycle;
|
||||||
|
if (!computed || computedCycle == 0UL)
|
||||||
|
{
|
||||||
|
#if DEVICE_GPU
|
||||||
|
divisorCycle = MersenneNumberDivisorByDivisorTester.CalculateCycleLengthGpu(
|
||||||
|
divisor64,
|
||||||
|
Accelerator,
|
||||||
|
divisorData,
|
||||||
|
skipPrimeOrderHeuristic: primeOrderFailed);
|
||||||
|
#elif DEVICE_HYBRID
|
||||||
|
divisorCycle = MersenneNumberDivisorByDivisorTester.CalculateCycleLengthHybrid(
|
||||||
|
divisor64,
|
||||||
|
Accelerator,
|
||||||
|
divisorData,
|
||||||
|
skipPrimeOrderHeuristic: primeOrderFailed);
|
||||||
|
#else
|
||||||
|
divisorCycle = MersenneNumberDivisorByDivisorTester.CalculateCycleLengthCpu(
|
||||||
|
divisor64,
|
||||||
|
divisorData,
|
||||||
|
skipPrimeOrderHeuristic: primeOrderFailed);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
divisorCycle = computedCycle;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordState(cursor);
|
||||||
|
if (divisorCycle == prime && !IsMersenneValue(prime, divisor64))
|
||||||
|
{
|
||||||
|
foundDivisor = divisor64;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor++;
|
||||||
|
if (cursor > maxKAllowed)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentK == minK64)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentK--;
|
||||||
|
divisor -= step128;
|
||||||
|
residueStepper.Retreat();
|
||||||
|
}
|
||||||
|
|
||||||
|
processedAll = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||||
private bool CheckDivisors64(
|
private bool CheckDivisors64(
|
||||||
ulong prime,
|
ulong prime,
|
||||||
@@ -2234,28 +2809,76 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||||
private void RecordState(BigInteger k)
|
private void RecordState(BigInteger k)
|
||||||
{
|
{
|
||||||
|
#if DivisorSet_TopDown
|
||||||
|
if (_currentPrime != 0UL)
|
||||||
|
{
|
||||||
|
if (k > _lastSavedK)
|
||||||
|
{
|
||||||
|
int next = _stateCounter + 1;
|
||||||
|
if (next >= PerfectNumberConstants.ByDivisorStateSaveInterval)
|
||||||
|
{
|
||||||
|
_kRepository.Upsert(_currentPrime, k);
|
||||||
|
_stateCounter = 0;
|
||||||
|
_lastSavedK = k;
|
||||||
|
_topDownCursor = k;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_stateCounter = next;
|
||||||
|
}
|
||||||
|
_topDownCursor = k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
#if DivisorSet_Pow2Groups
|
#if DivisorSet_Pow2Groups
|
||||||
if (_currentPow2Phase == Pow2Phase.Special)
|
if (_currentPow2Phase == Pow2Phase.Special)
|
||||||
{
|
{
|
||||||
RecordStateInternal(k, _specialStateFilePath, ref _specialStateCounter, ref _specialLastSavedK, _currentPow2Phase);
|
RecordStatePow2(k, _specialRepository, ref _specialStateCounter, ref _specialLastSavedK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_currentPow2Phase == Pow2Phase.Groups)
|
if (_currentPow2Phase == Pow2Phase.Groups)
|
||||||
{
|
{
|
||||||
RecordStateInternal(k, _groupsStateFilePath, ref _groupsStateCounter, ref _groupsLastSavedK, _currentPow2Phase);
|
RecordStatePow2(k, _groupsRepository, ref _groupsStateCounter, ref _groupsLastSavedK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
RecordStateInternal(k, StateFilePath, ref _stateCounter, ref _lastSavedK, default);
|
if (_currentPrime != 0UL)
|
||||||
|
{
|
||||||
|
if (k > _lastSavedK)
|
||||||
|
{
|
||||||
|
_kRepository.Upsert(_currentPrime, k);
|
||||||
|
_lastSavedK = k;
|
||||||
|
}
|
||||||
|
_stateCounter = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DivisorSet_Pow2Groups
|
#if DivisorSet_Pow2Groups
|
||||||
private void RecordStateInternal(BigInteger k, string path, ref int counter, ref BigInteger lastSavedK, Pow2Phase phase)
|
private void RecordState(BigInteger k, Pow2Phase phase)
|
||||||
#else
|
|
||||||
private void RecordStateInternal(BigInteger k, string path, ref int counter, ref BigInteger lastSavedK, object _)
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
|
switch (phase)
|
||||||
|
{
|
||||||
|
case Pow2Phase.Special:
|
||||||
|
RecordStatePow2(k, _specialRepository, ref _specialStateCounter, ref _specialLastSavedK);
|
||||||
|
break;
|
||||||
|
case Pow2Phase.Groups:
|
||||||
|
RecordStatePow2(k, _groupsRepository, ref _groupsStateCounter, ref _groupsLastSavedK);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
RecordState(k);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RecordStatePow2(BigInteger k, KStateRepository? repo, ref int counter, ref BigInteger lastSavedK)
|
||||||
|
{
|
||||||
|
if (_currentPrime == 0UL || repo is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (k <= lastSavedK)
|
if (k <= lastSavedK)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -2264,13 +2887,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
int next = counter + 1;
|
int next = counter + 1;
|
||||||
if (next >= PerfectNumberConstants.ByDivisorStateSaveInterval)
|
if (next >= PerfectNumberConstants.ByDivisorStateSaveInterval)
|
||||||
{
|
{
|
||||||
string? directory = Path.GetDirectoryName(path);
|
repo.Upsert(_currentPrime, k);
|
||||||
if (!string.IsNullOrEmpty(directory))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(directory);
|
|
||||||
}
|
|
||||||
|
|
||||||
File.AppendAllText(path, k.ToString(CultureInfo.InvariantCulture) + Environment.NewLine);
|
|
||||||
counter = 0;
|
counter = 0;
|
||||||
lastSavedK = k;
|
lastSavedK = k;
|
||||||
}
|
}
|
||||||
@@ -2280,24 +2897,6 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DivisorSet_Pow2Groups
|
|
||||||
// Overload used from CheckDivisors64Range through RecordState with current phase routed automatically.
|
|
||||||
private void RecordState(BigInteger k, Pow2Phase phase)
|
|
||||||
{
|
|
||||||
switch (phase)
|
|
||||||
{
|
|
||||||
case Pow2Phase.Special:
|
|
||||||
RecordStateInternal(k, _specialStateFilePath, ref _specialStateCounter, ref _specialLastSavedK, phase);
|
|
||||||
break;
|
|
||||||
case Pow2Phase.Groups:
|
|
||||||
RecordStateInternal(k, _groupsStateFilePath, ref _groupsStateCounter, ref _groupsLastSavedK, phase);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
RecordStateInternal(k, StateFilePath, ref _stateCounter, ref _lastSavedK, phase);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
private static void RecordPow2Hit(Pow2Phase phase)
|
private static void RecordPow2Hit(Pow2Phase phase)
|
||||||
{
|
{
|
||||||
@@ -2714,22 +3313,9 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
private void MarkPow2MinusOneChecked()
|
private void MarkPow2MinusOneChecked()
|
||||||
{
|
{
|
||||||
_pow2MinusOneChecked = true;
|
_pow2MinusOneChecked = true;
|
||||||
if (!string.IsNullOrEmpty(_pow2MinusOneStateFilePath))
|
if (_currentPrime != 0UL)
|
||||||
{
|
{
|
||||||
try
|
_pow2MinusOneRepository?.MarkChecked(_currentPrime);
|
||||||
{
|
|
||||||
string? directory = Path.GetDirectoryName(_pow2MinusOneStateFilePath);
|
|
||||||
if (!string.IsNullOrEmpty(directory))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(directory);
|
|
||||||
}
|
|
||||||
|
|
||||||
File.WriteAllText(_pow2MinusOneStateFilePath, "checked");
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// Best-effort persistence; ignore IO errors.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2797,6 +3383,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
}
|
}
|
||||||
|
|
||||||
_checkedPow2MinusOne[prime] = 1;
|
_checkedPow2MinusOne[prime] = 1;
|
||||||
|
_currentPrime = prime;
|
||||||
MarkPow2MinusOneChecked();
|
MarkPow2MinusOneChecked();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
+44
-23
@@ -15,10 +15,18 @@ using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTeste
|
|||||||
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForCpuOrder;
|
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForCpuOrder;
|
||||||
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForHybridOrder;
|
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForHybridOrder;
|
||||||
using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForGpuOrder;
|
using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForGpuOrder;
|
||||||
|
#elif DivisorSet_Percentile
|
||||||
|
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPercentileDivisorSetForCpuOrder;
|
||||||
|
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPercentileDivisorSetForHybridOrder;
|
||||||
|
using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPercentileDivisorSetForGpuOrder;
|
||||||
|
#elif DivisorSet_Additive
|
||||||
|
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForCpuOrder;
|
||||||
|
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForHybridOrder;
|
||||||
|
using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForGpuOrder;
|
||||||
#else
|
#else
|
||||||
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForOneByOneDivisorSetForCpuOrder;
|
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForCpuOrder;
|
||||||
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForOneByOneDivisorSetForHybridOrder;
|
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForHybridOrder;
|
||||||
using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForOneByOneDivisorSetForGpuOrder;
|
using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForGpuOrder;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Cpu;
|
namespace PerfectNumbers.Core.Cpu;
|
||||||
@@ -71,10 +79,14 @@ internal struct MersenneNumberDivisorByDivisorPrimeScanSessionWithTemplate
|
|||||||
string extension = ".bin";
|
string extension = ".bin";
|
||||||
#if DivisorSet_Predictive
|
#if DivisorSet_Predictive
|
||||||
extension = ".predictive";
|
extension = ".predictive";
|
||||||
|
#elif DivisorSet_Percentile
|
||||||
|
extension = ".percentile";
|
||||||
|
#elif DivisorSet_Additive
|
||||||
|
extension = ".additive";
|
||||||
|
#elif DivisorSet_TopDown
|
||||||
|
extension = ".topdown";
|
||||||
#endif
|
#endif
|
||||||
string stateFile = Path.Combine(
|
string stateFile = prime.ToString(CultureInfo.InvariantCulture) + extension;
|
||||||
PerfectNumberConstants.ByDivisorStateDirectory,
|
|
||||||
prime.ToString(CultureInfo.InvariantCulture) + extension);
|
|
||||||
|
|
||||||
ConfigureForPrime(stateFile);
|
ConfigureForPrime(stateFile);
|
||||||
|
|
||||||
@@ -82,11 +94,6 @@ internal struct MersenneNumberDivisorByDivisorPrimeScanSessionWithTemplate
|
|||||||
|
|
||||||
if (!isPrime)
|
if (!isPrime)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(stateFile) && File.Exists(stateFile))
|
|
||||||
{
|
|
||||||
File.Delete(stateFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
_markComposite();
|
_markComposite();
|
||||||
_printResult(prime, true, true, false, divisor);
|
_printResult(prime, true, true, false, divisor);
|
||||||
Console.WriteLine($"Finished processing {prime}");
|
Console.WriteLine($"Finished processing {prime}");
|
||||||
@@ -106,25 +113,39 @@ internal struct MersenneNumberDivisorByDivisorPrimeScanSessionWithTemplate
|
|||||||
|
|
||||||
private void ConfigureForPrime(string stateFile)
|
private void ConfigureForPrime(string stateFile)
|
||||||
{
|
{
|
||||||
|
#if DivisorSet_TopDown
|
||||||
BigInteger resumeK = EnvironmentConfiguration.MinK;
|
BigInteger resumeK = EnvironmentConfiguration.MinK;
|
||||||
if (File.Exists(stateFile))
|
BigInteger lastK = BigInteger.Zero;
|
||||||
|
if (EnvironmentConfiguration.ByDivisorKStateRepository is not null)
|
||||||
{
|
{
|
||||||
if (MersenneNumberDivisorByDivisorTester.TryReadLastSavedK(stateFile, out BigInteger lastK))
|
if (ulong.TryParse(Path.GetFileNameWithoutExtension(stateFile), NumberStyles.None, CultureInfo.InvariantCulture, out ulong parsedPrime) &&
|
||||||
|
EnvironmentConfiguration.ByDivisorKStateRepository.TryGet(parsedPrime, out BigInteger storedK) &&
|
||||||
|
storedK > BigInteger.Zero)
|
||||||
{
|
{
|
||||||
resumeK = lastK + BigInteger.One;
|
lastK = storedK;
|
||||||
_tester.ResumeFromState(stateFile, lastK, resumeK);
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
_tester.ResumeFromState(stateFile, BigInteger.Zero, resumeK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_tester.ResumeFromState(stateFile, BigInteger.Zero, resumeK);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_tester.ResumeFromState(stateFile, lastK, resumeK);
|
||||||
_tester.ResetStateTracking();
|
_tester.ResetStateTracking();
|
||||||
|
return;
|
||||||
|
#else
|
||||||
|
BigInteger resumeK = EnvironmentConfiguration.MinK;
|
||||||
|
BigInteger lastK = BigInteger.Zero;
|
||||||
|
if (EnvironmentConfiguration.ByDivisorKStateRepository is not null)
|
||||||
|
{
|
||||||
|
if (ulong.TryParse(Path.GetFileNameWithoutExtension(stateFile), NumberStyles.None, CultureInfo.InvariantCulture, out ulong parsedPrime) &&
|
||||||
|
EnvironmentConfiguration.ByDivisorKStateRepository.TryGet(parsedPrime, out BigInteger storedK) &&
|
||||||
|
storedK > BigInteger.Zero)
|
||||||
|
{
|
||||||
|
lastK = storedK;
|
||||||
|
resumeK = storedK + BigInteger.One;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_tester.ResumeFromState(stateFile, lastK, resumeK);
|
||||||
|
_tester.ResetStateTracking();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
|||||||
@@ -22,7 +22,13 @@ public static class EnvironmentConfiguration
|
|||||||
public static int ByDivisorSpecialRange;
|
public static int ByDivisorSpecialRange;
|
||||||
public static ByDivisor.ByDivisorClassModel? ByDivisorClassModel;
|
public static ByDivisor.ByDivisorClassModel? ByDivisorClassModel;
|
||||||
public static ByDivisor.ByDivisorScanTuning ByDivisorScanTuning = ByDivisor.ByDivisorScanTuning.Default;
|
public static ByDivisor.ByDivisorScanTuning ByDivisorScanTuning = ByDivisor.ByDivisorScanTuning.Default;
|
||||||
public static double ByDivisorCheapKLimit = 100_000d;
|
public static double ByDivisorCheapKLimit = 10_000d;
|
||||||
|
public static Persistence.ByDivisorClassModelRepository? ByDivisorClassModelRepository;
|
||||||
|
public static Persistence.KStateRepository? ByDivisorKStateRepository;
|
||||||
|
public static Persistence.KStateRepository? ByDivisorCheapKStateRepository;
|
||||||
|
public static Persistence.KStateRepository? ByDivisorSpecialStateRepository;
|
||||||
|
public static Persistence.KStateRepository? ByDivisorGroupsStateRepository;
|
||||||
|
public static Persistence.Pow2Minus1StateRepository? ByDivisorPow2Minus1Repository;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public static void Initialize()
|
public static void Initialize()
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Gpu;
|
namespace PerfectNumbers.Core.Gpu;
|
||||||
@@ -34,9 +35,7 @@ internal struct MersenneNumberDivisorByDivisorGpuPrimeScanSession
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Processing {prime}");
|
Console.WriteLine($"Processing {prime}");
|
||||||
string stateFile = Path.Combine(
|
string stateFile = prime.ToString(CultureInfo.InvariantCulture) + ".bin";
|
||||||
PerfectNumberConstants.ByDivisorStateDirectory,
|
|
||||||
prime.ToString(CultureInfo.InvariantCulture) + ".bin");
|
|
||||||
|
|
||||||
ConfigureForPrime(stateFile);
|
ConfigureForPrime(stateFile);
|
||||||
|
|
||||||
@@ -44,11 +43,6 @@ internal struct MersenneNumberDivisorByDivisorGpuPrimeScanSession
|
|||||||
|
|
||||||
if (!isPrime)
|
if (!isPrime)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(stateFile) && File.Exists(stateFile))
|
|
||||||
{
|
|
||||||
File.Delete(stateFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
_markComposite();
|
_markComposite();
|
||||||
_printResult(prime, true, true, false, divisor);
|
_printResult(prime, true, true, false, divisor);
|
||||||
Console.WriteLine($"Finished processing {prime}");
|
Console.WriteLine($"Finished processing {prime}");
|
||||||
@@ -68,25 +62,39 @@ internal struct MersenneNumberDivisorByDivisorGpuPrimeScanSession
|
|||||||
|
|
||||||
private void ConfigureForPrime(string stateFile)
|
private void ConfigureForPrime(string stateFile)
|
||||||
{
|
{
|
||||||
|
#if DivisorSet_TopDown
|
||||||
BigInteger resumeK = EnvironmentConfiguration.MinK;
|
BigInteger resumeK = EnvironmentConfiguration.MinK;
|
||||||
if (File.Exists(stateFile))
|
BigInteger lastK = BigInteger.Zero;
|
||||||
|
if (EnvironmentConfiguration.ByDivisorKStateRepository is not null)
|
||||||
{
|
{
|
||||||
if (MersenneNumberDivisorByDivisorTester.TryReadLastSavedK(stateFile, out BigInteger lastK))
|
if (ulong.TryParse(Path.GetFileNameWithoutExtension(stateFile), NumberStyles.None, CultureInfo.InvariantCulture, out ulong parsedPrime) &&
|
||||||
|
EnvironmentConfiguration.ByDivisorKStateRepository.TryGet(parsedPrime, out BigInteger storedK) &&
|
||||||
|
storedK > BigInteger.Zero)
|
||||||
{
|
{
|
||||||
resumeK = lastK + BigInteger.One;
|
lastK = storedK;
|
||||||
_tester.ResumeFromState(stateFile, lastK, resumeK);
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
_tester.ResumeFromState(stateFile, BigInteger.Zero, resumeK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_tester.ResumeFromState(stateFile, BigInteger.Zero, resumeK);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_tester.ResumeFromState(stateFile, lastK, resumeK);
|
||||||
_tester.ResetStateTracking();
|
_tester.ResetStateTracking();
|
||||||
|
return;
|
||||||
|
#else
|
||||||
|
BigInteger resumeK = EnvironmentConfiguration.MinK;
|
||||||
|
BigInteger lastK = BigInteger.Zero;
|
||||||
|
if (EnvironmentConfiguration.ByDivisorKStateRepository is not null)
|
||||||
|
{
|
||||||
|
if (ulong.TryParse(Path.GetFileNameWithoutExtension(stateFile), NumberStyles.None, CultureInfo.InvariantCulture, out ulong parsedPrime) &&
|
||||||
|
EnvironmentConfiguration.ByDivisorKStateRepository.TryGet(parsedPrime, out BigInteger storedK) &&
|
||||||
|
storedK > BigInteger.Zero)
|
||||||
|
{
|
||||||
|
lastK = storedK;
|
||||||
|
resumeK = storedK + BigInteger.One;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_tester.ResumeFromState(stateFile, lastK, resumeK);
|
||||||
|
_tester.ResetStateTracking();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@@ -87,3 +87,88 @@ internal struct MersenneDivisorResidueStepper(ulong prime, in GpuUInt128 step, i
|
|||||||
return (byte)sum;
|
return (byte)sum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[method: MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal struct MersenneDivisorResidueStepperDescending(ulong prime, in GpuUInt128 step, in GpuUInt128 firstDivisor)
|
||||||
|
{
|
||||||
|
private const ushort DecimalMaskWhenLastIsSeven = (1 << 3) | (1 << 7) | (1 << 9);
|
||||||
|
private const ushort DecimalMaskOtherwise = (1 << 1) | (1 << 3) | (1 << 9);
|
||||||
|
|
||||||
|
private readonly ushort decimalMask = (prime & 3UL) == 3UL ? DecimalMaskWhenLastIsSeven : DecimalMaskOtherwise;
|
||||||
|
private readonly byte step10 = ComputeModulo(step, 10, multiplier: 6);
|
||||||
|
private readonly byte step8 = (byte)(step.Low % 8UL);
|
||||||
|
private readonly byte step5 = ComputeModulo(step, 5, multiplier: 1);
|
||||||
|
private readonly byte step3 = ComputeModulo(step, 3, multiplier: 1);
|
||||||
|
private readonly byte step7 = ComputeModulo(step, 7, multiplier: 2);
|
||||||
|
private readonly byte step11 = ComputeModulo(step, 11, multiplier: 5);
|
||||||
|
private readonly byte step13 = ComputeModulo(step, 13, multiplier: 3);
|
||||||
|
private readonly byte step17 = ComputeModulo(step, 17, multiplier: 1);
|
||||||
|
private readonly byte step19 = ComputeModulo(step, 19, multiplier: 17);
|
||||||
|
|
||||||
|
private byte Remainder10 = ComputeModulo(firstDivisor, 10, multiplier: 6);
|
||||||
|
private byte Remainder8 = (byte)(firstDivisor.Low % 8UL);
|
||||||
|
private byte Remainder5 = ComputeModulo(firstDivisor, 5, multiplier: 1);
|
||||||
|
private byte Remainder3 = ComputeModulo(firstDivisor, 3, multiplier: 1);
|
||||||
|
private byte Remainder7 = ComputeModulo(firstDivisor, 7, multiplier: 2);
|
||||||
|
private byte Remainder11 = ComputeModulo(firstDivisor, 11, multiplier: 5);
|
||||||
|
private byte Remainder13 = ComputeModulo(firstDivisor, 13, multiplier: 3);
|
||||||
|
private byte Remainder17 = ComputeModulo(firstDivisor, 17, multiplier: 1);
|
||||||
|
private byte Remainder19 = ComputeModulo(firstDivisor, 19, multiplier: 17);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public readonly bool IsAdmissible()
|
||||||
|
{
|
||||||
|
if (((decimalMask >> Remainder10) & 1) == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Remainder8 != 1 && Remainder8 != 7)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Remainder3 == 0 || Remainder5 == 0 || Remainder7 == 0 || Remainder11 == 0 || Remainder13 == 0 || Remainder17 == 0 || Remainder19 == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Retreat()
|
||||||
|
{
|
||||||
|
Remainder10 = SubMod(Remainder10, step10, 10);
|
||||||
|
Remainder8 = SubMod(Remainder8, step8, 8);
|
||||||
|
Remainder5 = SubMod(Remainder5, step5, 5);
|
||||||
|
Remainder3 = SubMod(Remainder3, step3, 3);
|
||||||
|
Remainder7 = SubMod(Remainder7, step7, 7);
|
||||||
|
Remainder11 = SubMod(Remainder11, step11, 11);
|
||||||
|
Remainder13 = SubMod(Remainder13, step13, 13);
|
||||||
|
Remainder17 = SubMod(Remainder17, step17, 17);
|
||||||
|
Remainder19 = SubMod(Remainder19, step19, 19);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static byte ComputeModulo(in GpuUInt128 value, byte modulus, byte multiplier)
|
||||||
|
{
|
||||||
|
ulong high = value.High % modulus;
|
||||||
|
ulong low = value.Low % modulus;
|
||||||
|
int result = (int)((high * multiplier) + low);
|
||||||
|
result %= modulus;
|
||||||
|
return (byte)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static byte SubMod(byte value, byte delta, byte modulus)
|
||||||
|
{
|
||||||
|
int diff = value - delta;
|
||||||
|
if (diff < 0)
|
||||||
|
{
|
||||||
|
diff += modulus;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (byte)diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,12 +6,18 @@ using PerfectNumbers.Core.Gpu;
|
|||||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||||
using PerfectNumbers.Core.Cpu;
|
using PerfectNumbers.Core.Cpu;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using MersenneNumberDivisorByDivisorCpuTesterWithCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForOneByOneDivisorSetForCpuOrder;
|
using MersenneNumberDivisorByDivisorCpuTesterWithCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForCpuOrder;
|
||||||
using MersenneNumberDivisorByDivisorCpuTesterWithHybridOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForOneByOneDivisorSetForHybridOrder;
|
using MersenneNumberDivisorByDivisorCpuTesterWithHybridOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForHybridOrder;
|
||||||
using MersenneNumberDivisorByDivisorCpuTesterWithGpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForOneByOneDivisorSetForGpuOrder;
|
using MersenneNumberDivisorByDivisorCpuTesterWithGpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForGpuOrder;
|
||||||
using MersenneNumberDivisorByDivisorCpuTesterWithPredictiveCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForCpuOrder;
|
using MersenneNumberDivisorByDivisorCpuTesterWithPredictiveCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForCpuOrder;
|
||||||
using MersenneNumberDivisorByDivisorCpuTesterWithPredictiveHybridOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForHybridOrder;
|
using MersenneNumberDivisorByDivisorCpuTesterWithPredictiveHybridOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForHybridOrder;
|
||||||
using MersenneNumberDivisorByDivisorCpuTesterWithPredictiveGpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForGpuOrder;
|
using MersenneNumberDivisorByDivisorCpuTesterWithPredictiveGpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForGpuOrder;
|
||||||
|
using MersenneNumberDivisorByDivisorCpuTesterWithPercentileCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPercentileDivisorSetForCpuOrder;
|
||||||
|
using MersenneNumberDivisorByDivisorCpuTesterWithPercentileHybridOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPercentileDivisorSetForHybridOrder;
|
||||||
|
using MersenneNumberDivisorByDivisorCpuTesterWithPercentileGpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPercentileDivisorSetForGpuOrder;
|
||||||
|
using MersenneNumberDivisorByDivisorCpuTesterWithAdditiveCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForCpuOrder;
|
||||||
|
using MersenneNumberDivisorByDivisorCpuTesterWithAdditiveHybridOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForHybridOrder;
|
||||||
|
using MersenneNumberDivisorByDivisorCpuTesterWithAdditiveGpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForGpuOrder;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core;
|
namespace PerfectNumbers.Core;
|
||||||
|
|
||||||
@@ -79,6 +85,68 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
||||||
RunPredictiveCpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
|
RunPredictiveCpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static void Run(
|
||||||
|
List<ulong> candidates,
|
||||||
|
ref MersenneNumberDivisorByDivisorCpuTesterWithPercentileCpuOrder 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 MersenneNumberDivisorByDivisorCpuTesterWithPercentileCpuOrder prototype,
|
||||||
|
int workerCount,
|
||||||
|
int partitionSize,
|
||||||
|
Action mc,
|
||||||
|
Action cc,
|
||||||
|
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
||||||
|
RunPercentileCpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static void Run(
|
||||||
|
List<ulong> candidates,
|
||||||
|
ref MersenneNumberDivisorByDivisorCpuTesterWithAdditiveCpuOrder 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 MersenneNumberDivisorByDivisorCpuTesterWithAdditiveCpuOrder prototype,
|
||||||
|
int workerCount,
|
||||||
|
int partitionSize,
|
||||||
|
Action mc,
|
||||||
|
Action cc,
|
||||||
|
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
||||||
|
RunAdditiveCpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public static void Run(
|
public static void Run(
|
||||||
List<ulong> candidates,
|
List<ulong> candidates,
|
||||||
@@ -141,6 +209,68 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
||||||
RunPredictiveHybridOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
|
RunPredictiveHybridOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static void Run(
|
||||||
|
List<ulong> candidates,
|
||||||
|
ref MersenneNumberDivisorByDivisorCpuTesterWithPercentileHybridOrder 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 MersenneNumberDivisorByDivisorCpuTesterWithPercentileHybridOrder prototype,
|
||||||
|
int workerCount,
|
||||||
|
int partitionSize,
|
||||||
|
Action mc,
|
||||||
|
Action cc,
|
||||||
|
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
||||||
|
RunPercentileHybridOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static void Run(
|
||||||
|
List<ulong> candidates,
|
||||||
|
ref MersenneNumberDivisorByDivisorCpuTesterWithAdditiveHybridOrder 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 MersenneNumberDivisorByDivisorCpuTesterWithAdditiveHybridOrder prototype,
|
||||||
|
int workerCount,
|
||||||
|
int partitionSize,
|
||||||
|
Action mc,
|
||||||
|
Action cc,
|
||||||
|
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
||||||
|
RunAdditiveHybridOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public static void Run(
|
public static void Run(
|
||||||
List<ulong> candidates,
|
List<ulong> candidates,
|
||||||
@@ -203,6 +333,68 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
||||||
RunPredictiveGpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
|
RunPredictiveGpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static void Run(
|
||||||
|
List<ulong> candidates,
|
||||||
|
ref MersenneNumberDivisorByDivisorCpuTesterWithPercentileGpuOrder 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 MersenneNumberDivisorByDivisorCpuTesterWithPercentileGpuOrder prototype,
|
||||||
|
int workerCount,
|
||||||
|
int partitionSize,
|
||||||
|
Action mc,
|
||||||
|
Action cc,
|
||||||
|
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
||||||
|
RunPercentileGpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static void Run(
|
||||||
|
List<ulong> candidates,
|
||||||
|
ref MersenneNumberDivisorByDivisorCpuTesterWithAdditiveGpuOrder 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 MersenneNumberDivisorByDivisorCpuTesterWithAdditiveGpuOrder prototype,
|
||||||
|
int workerCount,
|
||||||
|
int partitionSize,
|
||||||
|
Action mc,
|
||||||
|
Action cc,
|
||||||
|
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
||||||
|
RunAdditiveGpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public static void Run(
|
public static void Run(
|
||||||
List<ulong> candidates,
|
List<ulong> candidates,
|
||||||
@@ -1093,7 +1285,7 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
if (workerCount == 1)
|
if (workerCount == 1)
|
||||||
{
|
{
|
||||||
DeterministicRandomCpu.Initialize();
|
DeterministicRandomCpu.Initialize();
|
||||||
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForOneByOneDivisorSetForCpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForSequentialDivisorSetForCpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
||||||
foreach (ulong prime in filteredPrimes)
|
foreach (ulong prime in filteredPrimes)
|
||||||
{
|
{
|
||||||
session.ProcessPrime(prime);
|
session.ProcessPrime(prime);
|
||||||
@@ -1115,7 +1307,7 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
() =>
|
() =>
|
||||||
{
|
{
|
||||||
DeterministicRandomCpu.Initialize();
|
DeterministicRandomCpu.Initialize();
|
||||||
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForOneByOneDivisorSetForCpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForSequentialDivisorSetForCpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
||||||
startGate.Wait();
|
startGate.Wait();
|
||||||
Console.WriteLine($"Task started for worker {workerIndex.ToString(CultureInfo.InvariantCulture)}");
|
Console.WriteLine($"Task started for worker {workerIndex.ToString(CultureInfo.InvariantCulture)}");
|
||||||
|
|
||||||
@@ -1230,6 +1422,162 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
startGate.Dispose();
|
startGate.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void RunPercentileCpuOrderFiltered(
|
||||||
|
List<ulong> filteredPrimes,
|
||||||
|
in MersenneNumberDivisorByDivisorCpuTesterWithPercentileCpuOrder prototype,
|
||||||
|
int workerCount,
|
||||||
|
int partitionSize,
|
||||||
|
Action markComposite,
|
||||||
|
Action clearComposite,
|
||||||
|
Action<ulong, bool, bool, bool, BigInteger> printResult)
|
||||||
|
{
|
||||||
|
MersenneNumberDivisorByDivisorCpuTesterWithPercentileCpuOrder prototypeCopy = prototype;
|
||||||
|
int totalCount = filteredPrimes.Count;
|
||||||
|
if (partitionSize > totalCount)
|
||||||
|
{
|
||||||
|
partitionSize = totalCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workerCount == 1)
|
||||||
|
{
|
||||||
|
DeterministicRandomCpu.Initialize();
|
||||||
|
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForPercentileDivisorSetForCpuOrder(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 MersenneNumberDivisorByDivisorPrimeScanSessionWithForPercentileDivisorSetForCpuOrder(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 RunAdditiveCpuOrderFiltered(
|
||||||
|
List<ulong> filteredPrimes,
|
||||||
|
in MersenneNumberDivisorByDivisorCpuTesterWithAdditiveCpuOrder prototype,
|
||||||
|
int workerCount,
|
||||||
|
int partitionSize,
|
||||||
|
Action markComposite,
|
||||||
|
Action clearComposite,
|
||||||
|
Action<ulong, bool, bool, bool, BigInteger> printResult)
|
||||||
|
{
|
||||||
|
MersenneNumberDivisorByDivisorCpuTesterWithAdditiveCpuOrder prototypeCopy = prototype;
|
||||||
|
int totalCount = filteredPrimes.Count;
|
||||||
|
if (partitionSize > totalCount)
|
||||||
|
{
|
||||||
|
partitionSize = totalCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workerCount == 1)
|
||||||
|
{
|
||||||
|
DeterministicRandomCpu.Initialize();
|
||||||
|
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForAdditiveDivisorSetForCpuOrder(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 MersenneNumberDivisorByDivisorPrimeScanSessionWithForAdditiveDivisorSetForCpuOrder(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(
|
private static void RunHybridOrderFiltered(
|
||||||
List<ulong> filteredPrimes,
|
List<ulong> filteredPrimes,
|
||||||
in MersenneNumberDivisorByDivisorCpuTesterWithHybridOrder prototype,
|
in MersenneNumberDivisorByDivisorCpuTesterWithHybridOrder prototype,
|
||||||
@@ -1256,7 +1604,7 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
if (workerCount == 1)
|
if (workerCount == 1)
|
||||||
{
|
{
|
||||||
DeterministicRandomCpu.Initialize();
|
DeterministicRandomCpu.Initialize();
|
||||||
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForOneByOneDivisorSetForHybridOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForSequentialDivisorSetForHybridOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
||||||
|
|
||||||
GpuPrimeWorkLimiter.Acquire();
|
GpuPrimeWorkLimiter.Acquire();
|
||||||
foreach (ulong prime in filteredPrimes)
|
foreach (ulong prime in filteredPrimes)
|
||||||
@@ -1282,7 +1630,7 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
() =>
|
() =>
|
||||||
{
|
{
|
||||||
DeterministicRandomCpu.Initialize();
|
DeterministicRandomCpu.Initialize();
|
||||||
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForOneByOneDivisorSetForHybridOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForSequentialDivisorSetForHybridOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
||||||
startGate.Wait();
|
startGate.Wait();
|
||||||
Console.WriteLine($"Task started for worker {workerIndex.ToString(CultureInfo.InvariantCulture)}");
|
Console.WriteLine($"Task started for worker {workerIndex.ToString(CultureInfo.InvariantCulture)}");
|
||||||
|
|
||||||
@@ -1399,6 +1747,188 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
startGate.Dispose();
|
startGate.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void RunPercentileHybridOrderFiltered(
|
||||||
|
List<ulong> filteredPrimes,
|
||||||
|
in MersenneNumberDivisorByDivisorCpuTesterWithPercentileHybridOrder prototype,
|
||||||
|
int workerCount,
|
||||||
|
int partitionSize,
|
||||||
|
Action markComposite,
|
||||||
|
Action clearComposite,
|
||||||
|
Action<ulong, bool, bool, bool, BigInteger> printResult)
|
||||||
|
{
|
||||||
|
MersenneNumberDivisorByDivisorCpuTesterWithPercentileHybridOrder prototypeCopy = prototype;
|
||||||
|
int totalCount = filteredPrimes.Count;
|
||||||
|
if (partitionSize > totalCount)
|
||||||
|
{
|
||||||
|
partitionSize = totalCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
int effectiveWorkerCount = workerCount;
|
||||||
|
int gpuLimit = GpuPrimeWorkLimiter.CurrentLimit;
|
||||||
|
if (effectiveWorkerCount > gpuLimit)
|
||||||
|
{
|
||||||
|
effectiveWorkerCount = gpuLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workerCount == 1)
|
||||||
|
{
|
||||||
|
DeterministicRandomCpu.Initialize();
|
||||||
|
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForPercentileDivisorSetForHybridOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
||||||
|
|
||||||
|
GpuPrimeWorkLimiter.Acquire();
|
||||||
|
foreach (ulong prime in filteredPrimes)
|
||||||
|
{
|
||||||
|
session.ProcessPrime(prime);
|
||||||
|
}
|
||||||
|
|
||||||
|
GpuPrimeWorkLimiter.Release();
|
||||||
|
session.Dispose();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskScheduler scheduler = UnboundedTaskScheduler.Instance;
|
||||||
|
int nextIndex = 0;
|
||||||
|
Task[] tasks = new Task[effectiveWorkerCount];
|
||||||
|
var startGate = new ManualResetEventSlim(initialState: false);
|
||||||
|
|
||||||
|
for (int taskIndex = 0; taskIndex < effectiveWorkerCount; taskIndex++)
|
||||||
|
{
|
||||||
|
int workerIndex = taskIndex;
|
||||||
|
tasks[taskIndex] = Task.Factory.StartNew(
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
DeterministicRandomCpu.Initialize();
|
||||||
|
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForPercentileDivisorSetForHybridOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
||||||
|
startGate.Wait();
|
||||||
|
Console.WriteLine($"Task started for worker {workerIndex.ToString(CultureInfo.InvariantCulture)}");
|
||||||
|
|
||||||
|
GpuPrimeWorkLimiter.Acquire();
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GpuPrimeWorkLimiter.Release();
|
||||||
|
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 RunAdditiveHybridOrderFiltered(
|
||||||
|
List<ulong> filteredPrimes,
|
||||||
|
in MersenneNumberDivisorByDivisorCpuTesterWithAdditiveHybridOrder prototype,
|
||||||
|
int workerCount,
|
||||||
|
int partitionSize,
|
||||||
|
Action markComposite,
|
||||||
|
Action clearComposite,
|
||||||
|
Action<ulong, bool, bool, bool, BigInteger> printResult)
|
||||||
|
{
|
||||||
|
MersenneNumberDivisorByDivisorCpuTesterWithAdditiveHybridOrder prototypeCopy = prototype;
|
||||||
|
int totalCount = filteredPrimes.Count;
|
||||||
|
if (partitionSize > totalCount)
|
||||||
|
{
|
||||||
|
partitionSize = totalCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
int effectiveWorkerCount = workerCount;
|
||||||
|
int gpuLimit = GpuPrimeWorkLimiter.CurrentLimit;
|
||||||
|
if (effectiveWorkerCount > gpuLimit)
|
||||||
|
{
|
||||||
|
effectiveWorkerCount = gpuLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workerCount == 1)
|
||||||
|
{
|
||||||
|
DeterministicRandomCpu.Initialize();
|
||||||
|
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForAdditiveDivisorSetForHybridOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
||||||
|
|
||||||
|
GpuPrimeWorkLimiter.Acquire();
|
||||||
|
foreach (ulong prime in filteredPrimes)
|
||||||
|
{
|
||||||
|
session.ProcessPrime(prime);
|
||||||
|
}
|
||||||
|
|
||||||
|
GpuPrimeWorkLimiter.Release();
|
||||||
|
session.Dispose();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskScheduler scheduler = UnboundedTaskScheduler.Instance;
|
||||||
|
int nextIndex = 0;
|
||||||
|
Task[] tasks = new Task[effectiveWorkerCount];
|
||||||
|
var startGate = new ManualResetEventSlim(initialState: false);
|
||||||
|
|
||||||
|
for (int taskIndex = 0; taskIndex < effectiveWorkerCount; taskIndex++)
|
||||||
|
{
|
||||||
|
int workerIndex = taskIndex;
|
||||||
|
tasks[taskIndex] = Task.Factory.StartNew(
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
DeterministicRandomCpu.Initialize();
|
||||||
|
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForAdditiveDivisorSetForHybridOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
||||||
|
startGate.Wait();
|
||||||
|
Console.WriteLine($"Task started for worker {workerIndex.ToString(CultureInfo.InvariantCulture)}");
|
||||||
|
|
||||||
|
GpuPrimeWorkLimiter.Acquire();
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GpuPrimeWorkLimiter.Release();
|
||||||
|
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(
|
private static void RunGpuOrderFiltered(
|
||||||
List<ulong> filteredPrimes,
|
List<ulong> filteredPrimes,
|
||||||
in MersenneNumberDivisorByDivisorCpuTesterWithGpuOrder prototype,
|
in MersenneNumberDivisorByDivisorCpuTesterWithGpuOrder prototype,
|
||||||
@@ -1425,7 +1955,7 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
if (workerCount == 1)
|
if (workerCount == 1)
|
||||||
{
|
{
|
||||||
DeterministicRandomCpu.Initialize();
|
DeterministicRandomCpu.Initialize();
|
||||||
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForOneByOneDivisorSetForGpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForSequentialDivisorSetForGpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
||||||
|
|
||||||
GpuPrimeWorkLimiter.Acquire();
|
GpuPrimeWorkLimiter.Acquire();
|
||||||
foreach (ulong prime in filteredPrimes)
|
foreach (ulong prime in filteredPrimes)
|
||||||
@@ -1451,7 +1981,7 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
() =>
|
() =>
|
||||||
{
|
{
|
||||||
DeterministicRandomCpu.Initialize();
|
DeterministicRandomCpu.Initialize();
|
||||||
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForOneByOneDivisorSetForGpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForSequentialDivisorSetForGpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
|
||||||
startGate.Wait();
|
startGate.Wait();
|
||||||
Console.WriteLine($"Task started for worker {workerIndex.ToString(CultureInfo.InvariantCulture)}");
|
Console.WriteLine($"Task started for worker {workerIndex.ToString(CultureInfo.InvariantCulture)}");
|
||||||
|
|
||||||
@@ -1568,6 +2098,162 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
startGate.Dispose();
|
startGate.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void RunPercentileGpuOrderFiltered(
|
||||||
|
List<ulong> filteredPrimes,
|
||||||
|
in MersenneNumberDivisorByDivisorCpuTesterWithPercentileGpuOrder prototype,
|
||||||
|
int workerCount,
|
||||||
|
int partitionSize,
|
||||||
|
Action markComposite,
|
||||||
|
Action clearComposite,
|
||||||
|
Action<ulong, bool, bool, bool, BigInteger> printResult)
|
||||||
|
{
|
||||||
|
MersenneNumberDivisorByDivisorCpuTesterWithPercentileGpuOrder prototypeCopy = prototype;
|
||||||
|
int totalCount = filteredPrimes.Count;
|
||||||
|
if (partitionSize > totalCount)
|
||||||
|
{
|
||||||
|
partitionSize = totalCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workerCount == 1)
|
||||||
|
{
|
||||||
|
DeterministicRandomCpu.Initialize();
|
||||||
|
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForPercentileDivisorSetForGpuOrder(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 MersenneNumberDivisorByDivisorPrimeScanSessionWithForPercentileDivisorSetForGpuOrder(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 RunAdditiveGpuOrderFiltered(
|
||||||
|
List<ulong> filteredPrimes,
|
||||||
|
in MersenneNumberDivisorByDivisorCpuTesterWithAdditiveGpuOrder prototype,
|
||||||
|
int workerCount,
|
||||||
|
int partitionSize,
|
||||||
|
Action markComposite,
|
||||||
|
Action clearComposite,
|
||||||
|
Action<ulong, bool, bool, bool, BigInteger> printResult)
|
||||||
|
{
|
||||||
|
MersenneNumberDivisorByDivisorCpuTesterWithAdditiveGpuOrder prototypeCopy = prototype;
|
||||||
|
int totalCount = filteredPrimes.Count;
|
||||||
|
if (partitionSize > totalCount)
|
||||||
|
{
|
||||||
|
partitionSize = totalCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workerCount == 1)
|
||||||
|
{
|
||||||
|
DeterministicRandomCpu.Initialize();
|
||||||
|
var session = new MersenneNumberDivisorByDivisorPrimeScanSessionWithForAdditiveDivisorSetForGpuOrder(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 MersenneNumberDivisorByDivisorPrimeScanSessionWithForAdditiveDivisorSetForGpuOrder(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(
|
private static void RunGpuTesterFiltered(
|
||||||
List<ulong> filteredPrimes,
|
List<ulong> filteredPrimes,
|
||||||
MersenneNumberDivisorByDivisorGpuTester prototype,
|
MersenneNumberDivisorByDivisorGpuTester prototype,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public static class PerfectNumberConstants
|
|||||||
public const int PooledArrayThreshold = 64;
|
public const int PooledArrayThreshold = 64;
|
||||||
public const int Pow2WindowSize = 8;
|
public const int Pow2WindowSize = 8;
|
||||||
public const int RollingAccelerators = 298;
|
public const int RollingAccelerators = 298;
|
||||||
public const int ThreadsByAccelerator = 1;
|
public const int ThreadsByAccelerator = 4;
|
||||||
public const int ByDivisorStateSaveInterval = 200_000;
|
public const int ByDivisorStateSaveInterval = 200_000;
|
||||||
public const string ByDivisorStateDirectory = "Checks";
|
public const string ByDivisorStateDirectory = "Checks";
|
||||||
public const int PollardRhoMilliseconds = 100;
|
public const int PollardRhoMilliseconds = 100;
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
<PackageReference Include="Open.Numeric.Primes" Version="4.0.4" />
|
<PackageReference Include="Open.Numeric.Primes" Version="4.0.4" />
|
||||||
<PackageReference Include="ILGPU" Version="1.5.3" />
|
<PackageReference Include="ILGPU" Version="1.5.3" />
|
||||||
<PackageReference Include="ILGPU.Algorithms" Version="1.5.3" />
|
<PackageReference Include="ILGPU.Algorithms" Version="1.5.3" />
|
||||||
|
<PackageReference Include="LightningDB" Version="0.21.0" />
|
||||||
|
<PackageReference Include="Microsoft.FASTER.Core" Version="2.4.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -0,0 +1,181 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using FASTER.core;
|
||||||
|
using LightningDB;
|
||||||
|
using PerfectNumbers.Core.ByDivisor;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Persistence;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Persists the by-divisor class model using Microsoft.FASTER.Core with optional migration from LightningDB.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ByDivisorClassModelRepository : IDisposable
|
||||||
|
{
|
||||||
|
private const string ModelKey = "bydivisor_model";
|
||||||
|
private readonly FasterKV<string, string> _store;
|
||||||
|
private readonly IDevice _logDevice;
|
||||||
|
private readonly IDevice _objectLogDevice;
|
||||||
|
private static readonly Encoding Utf8 = Encoding.UTF8;
|
||||||
|
|
||||||
|
public string DatabasePath { get; }
|
||||||
|
|
||||||
|
public static ByDivisorClassModelRepository Open(
|
||||||
|
string directory,
|
||||||
|
string repositoryFileName = "bydivisor_model.faster",
|
||||||
|
string? lmdbPath = null,
|
||||||
|
string? lmdbDirectory = null,
|
||||||
|
bool deleteLmdbAfterMigration = true)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(directory);
|
||||||
|
string dbPath = Path.Combine(directory, repositoryFileName);
|
||||||
|
var repo = new ByDivisorClassModelRepository(dbPath);
|
||||||
|
string lmdbSource = lmdbPath ?? Path.ChangeExtension(dbPath, ".lmdb");
|
||||||
|
if (!File.Exists(lmdbSource) && !Directory.Exists(lmdbSource) && !string.IsNullOrEmpty(lmdbDirectory))
|
||||||
|
{
|
||||||
|
lmdbSource = Path.Combine(lmdbDirectory, Path.GetFileName(Path.ChangeExtension(repositoryFileName, ".lmdb")));
|
||||||
|
}
|
||||||
|
repo.MigrateFromLmdb(lmdbSource, deleteLmdbAfterMigration);
|
||||||
|
return repo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ByDivisorClassModelRepository(string path)
|
||||||
|
{
|
||||||
|
DatabasePath = path;
|
||||||
|
_logDevice = Devices.CreateLogDevice(path + ".log");
|
||||||
|
_objectLogDevice = Devices.CreateLogDevice(path + ".obj.log");
|
||||||
|
_store = new FasterKV<string, string>(
|
||||||
|
1L << 16,
|
||||||
|
new LogSettings
|
||||||
|
{
|
||||||
|
LogDevice = _logDevice,
|
||||||
|
ObjectLogDevice = _objectLogDevice,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save(ByDivisorClassModel model)
|
||||||
|
{
|
||||||
|
string json = JsonSerializer.Serialize(model, new JsonSerializerOptions { WriteIndented = true });
|
||||||
|
using var session = _store.For(new SimpleFunctions<string, string>()).NewSession<SimpleFunctions<string, string>>();
|
||||||
|
session.Upsert(ModelKey, json);
|
||||||
|
session.CompletePending(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryLoad(out ByDivisorClassModel? model)
|
||||||
|
{
|
||||||
|
using var session = _store.For(new SimpleFunctions<string, string>()).NewSession<SimpleFunctions<string, string>>();
|
||||||
|
if (session.Read(ModelKey, out string? json).Found && json is not null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
model = JsonSerializer.Deserialize<ByDivisorClassModel>(json);
|
||||||
|
return model is not null;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore deserialization errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
model = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Commit()
|
||||||
|
{
|
||||||
|
_store.Log.FlushAndEvict(wait: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_store.Dispose();
|
||||||
|
_logDevice.Dispose();
|
||||||
|
_objectLogDevice.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MigrateFromLmdb(string lmdbPath, bool deleteAfter)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!LmdbExists(lmdbPath))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"No LMDB by-divisor model found at '{lmdbPath}', skipping migration.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"Migrating by-divisor model from LMDB at '{lmdbPath}' to FASTER...");
|
||||||
|
using var env = new LightningEnvironment(lmdbPath) { MaxDatabases = 2 };
|
||||||
|
env.Open();
|
||||||
|
using var tx = env.BeginTransaction(TransactionBeginFlags.ReadOnly);
|
||||||
|
var db = tx.OpenDatabase("bydivisor_model", new DatabaseConfiguration { Flags = DatabaseOpenFlags.None });
|
||||||
|
if (tx.TryGet(db, Utf8.GetBytes(ModelKey), out byte[]? jsonBytes) && jsonBytes is not null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string json = Utf8.GetString(jsonBytes);
|
||||||
|
var session = _store.For(new SimpleFunctions<string, string>()).NewSession<SimpleFunctions<string, string>>();
|
||||||
|
session.Upsert(ModelKey, json);
|
||||||
|
session.CompletePending(true);
|
||||||
|
session.Dispose();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore migration issues
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_store.Log.FlushAndEvict(wait: true);
|
||||||
|
Console.WriteLine("Migration of by-divisor model to FASTER completed.");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore migration failures
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deleteAfter)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Starting cleanup of LMDB by-divisor model at '{lmdbPath}'...");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Directory.Exists(lmdbPath))
|
||||||
|
{
|
||||||
|
Directory.Delete(lmdbPath, recursive: true);
|
||||||
|
}
|
||||||
|
else if (File.Exists(lmdbPath))
|
||||||
|
{
|
||||||
|
File.Delete(lmdbPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore cleanup failures
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool LmdbExists(string path)
|
||||||
|
{
|
||||||
|
if (Directory.Exists(path) || File.Exists(path))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string candidate = Path.Combine(path, "data.mdb");
|
||||||
|
if (File.Exists(candidate))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string? directory = Path.GetDirectoryName(path);
|
||||||
|
if (!string.IsNullOrEmpty(directory) && Directory.Exists(directory))
|
||||||
|
{
|
||||||
|
string sibling = Path.Combine(directory, "data.mdb");
|
||||||
|
if (File.Exists(sibling))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text;
|
||||||
|
using FASTER.core;
|
||||||
|
using LightningDB;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Persistence;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores last-checked k values (BigInteger) per prime using Microsoft.FASTER.Core.
|
||||||
|
/// Supports one-time migration from an existing LightningDB (LMDB) store.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class KStateRepository : IDisposable
|
||||||
|
{
|
||||||
|
private readonly FasterKV<string, string> _store;
|
||||||
|
private readonly IDevice _logDevice;
|
||||||
|
private readonly IDevice _objectLogDevice;
|
||||||
|
private static readonly Encoding Utf8 = Encoding.UTF8;
|
||||||
|
|
||||||
|
public string DatabasePath { get; }
|
||||||
|
|
||||||
|
public static KStateRepository Open(
|
||||||
|
string directory,
|
||||||
|
string repositoryFileName = "kstate.faster",
|
||||||
|
string? lmdbPath = null,
|
||||||
|
string? lmdbDirectory = null,
|
||||||
|
string lmdbDatabaseName = "kstate",
|
||||||
|
bool deleteLmdbAfterMigration = true)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(directory);
|
||||||
|
string dbPath = Path.Combine(directory, repositoryFileName);
|
||||||
|
var repo = new KStateRepository(dbPath);
|
||||||
|
|
||||||
|
string lmdbSource = lmdbPath ?? Path.ChangeExtension(dbPath, ".lmdb");
|
||||||
|
if (!File.Exists(lmdbSource) && !Directory.Exists(lmdbSource) && !string.IsNullOrEmpty(lmdbDirectory))
|
||||||
|
{
|
||||||
|
lmdbSource = Path.Combine(lmdbDirectory, Path.GetFileName(Path.ChangeExtension(repositoryFileName, ".lmdb")));
|
||||||
|
}
|
||||||
|
|
||||||
|
repo.MigrateFromLmdb(lmdbSource, lmdbDatabaseName, deleteLmdbAfterMigration);
|
||||||
|
return repo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private KStateRepository(string path)
|
||||||
|
{
|
||||||
|
DatabasePath = path;
|
||||||
|
_logDevice = Devices.CreateLogDevice(path + ".log");
|
||||||
|
_objectLogDevice = Devices.CreateLogDevice(path + ".obj.log");
|
||||||
|
_store = new FasterKV<string, string>(
|
||||||
|
1L << 20,
|
||||||
|
new LogSettings
|
||||||
|
{
|
||||||
|
LogDevice = _logDevice,
|
||||||
|
ObjectLogDevice = _objectLogDevice,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Upsert(ulong prime, BigInteger k)
|
||||||
|
{
|
||||||
|
using var session = _store.For(new SimpleFunctions<string, string>()).NewSession<SimpleFunctions<string, string>>();
|
||||||
|
string keyString = prime.ToString(CultureInfo.InvariantCulture);
|
||||||
|
string valueString = k.ToString(CultureInfo.InvariantCulture);
|
||||||
|
session.Upsert(keyString, valueString);
|
||||||
|
session.CompletePending(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGet(ulong prime, out BigInteger value)
|
||||||
|
{
|
||||||
|
using var session = _store.For(new SimpleFunctions<string, string>()).NewSession<SimpleFunctions<string, string>>();
|
||||||
|
string keyString = prime.ToString(CultureInfo.InvariantCulture);
|
||||||
|
if (session.Read(keyString, out string? stored).Found &&
|
||||||
|
stored is not null &&
|
||||||
|
BigInteger.TryParse(stored, NumberStyles.Integer, CultureInfo.InvariantCulture, out value))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = BigInteger.Zero;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SeedFromStates(IEnumerable<(ulong Prime, BigInteger K)> states)
|
||||||
|
{
|
||||||
|
foreach (var (prime, k) in states)
|
||||||
|
{
|
||||||
|
Upsert(prime, k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Commit()
|
||||||
|
{
|
||||||
|
_store.Log.FlushAndEvict(wait: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_store.Dispose();
|
||||||
|
_logDevice.Dispose();
|
||||||
|
_objectLogDevice.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MigrateFromLmdb(string lmdbPath, string databaseName, bool deleteAfter)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!LmdbExists(lmdbPath))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"No LMDB k-state found at '{lmdbPath}', skipping migration.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"Migrating k-state from LMDB at '{lmdbPath}' to FASTER...");
|
||||||
|
using var env = new LightningEnvironment(lmdbPath) { MaxDatabases = 2 };
|
||||||
|
env.Open();
|
||||||
|
using var tx = env.BeginTransaction(TransactionBeginFlags.ReadOnly);
|
||||||
|
var db = tx.OpenDatabase(databaseName, new DatabaseConfiguration { Flags = DatabaseOpenFlags.None });
|
||||||
|
using var cursor = tx.CreateCursor(db);
|
||||||
|
foreach (var entry in cursor.AsEnumerable())
|
||||||
|
{
|
||||||
|
string keyString = Utf8.GetString(entry.Item1.CopyToNewArray());
|
||||||
|
string valueString = Utf8.GetString(entry.Item2.CopyToNewArray());
|
||||||
|
if (ulong.TryParse(keyString, NumberStyles.None, CultureInfo.InvariantCulture, out ulong prime) &&
|
||||||
|
BigInteger.TryParse(valueString, NumberStyles.Integer, CultureInfo.InvariantCulture, out BigInteger k) &&
|
||||||
|
k > BigInteger.Zero)
|
||||||
|
{
|
||||||
|
Upsert(prime, k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_store.Log.FlushAndEvict(wait: true);
|
||||||
|
Console.WriteLine("Migration of k-state to FASTER completed.");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore migration failures; repo will simply start empty.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deleteAfter)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Starting cleanup of LMDB k-state at '{lmdbPath}'...");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Directory.Exists(lmdbPath))
|
||||||
|
{
|
||||||
|
Directory.Delete(lmdbPath, recursive: true);
|
||||||
|
}
|
||||||
|
else if (File.Exists(lmdbPath))
|
||||||
|
{
|
||||||
|
File.Delete(lmdbPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore cleanup failures
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool LmdbExists(string path)
|
||||||
|
{
|
||||||
|
if (Directory.Exists(path) || File.Exists(path))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string candidate = Path.Combine(path, "data.mdb");
|
||||||
|
if (File.Exists(candidate))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string? directory = Path.GetDirectoryName(path);
|
||||||
|
if (!string.IsNullOrEmpty(directory) && Directory.Exists(directory))
|
||||||
|
{
|
||||||
|
string sibling = Path.Combine(directory, "data.mdb");
|
||||||
|
if (File.Exists(sibling))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using FASTER.core;
|
||||||
|
using LightningDB;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Persistence;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tracks primes for which the pow2-1 divisor check was completed, backed by Microsoft.FASTER.Core.
|
||||||
|
/// Supports one-time migration from LightningDB.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class Pow2Minus1StateRepository : IDisposable
|
||||||
|
{
|
||||||
|
private readonly FasterKV<string, string> _store;
|
||||||
|
private readonly IDevice _logDevice;
|
||||||
|
private readonly IDevice _objectLogDevice;
|
||||||
|
private static readonly Encoding Utf8 = Encoding.UTF8;
|
||||||
|
|
||||||
|
public string DatabasePath { get; }
|
||||||
|
|
||||||
|
public static Pow2Minus1StateRepository Open(
|
||||||
|
string databasePath,
|
||||||
|
string lmdbPath = "pow2minus1.lmdb",
|
||||||
|
string? lmdbDirectory = null,
|
||||||
|
string lmdbDatabaseName = "pow2minus1",
|
||||||
|
bool deleteLmdbAfterMigration = true)
|
||||||
|
{
|
||||||
|
string directory = Path.GetDirectoryName(databasePath) ?? ".";
|
||||||
|
Directory.CreateDirectory(directory);
|
||||||
|
|
||||||
|
var repo = new Pow2Minus1StateRepository(databasePath);
|
||||||
|
|
||||||
|
if (!File.Exists(lmdbPath) && !Directory.Exists(lmdbPath) && !string.IsNullOrEmpty(lmdbDirectory))
|
||||||
|
{
|
||||||
|
lmdbPath = Path.Combine(lmdbDirectory, Path.GetFileName(lmdbPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
repo.MigrateFromLmdb(lmdbPath, lmdbDatabaseName, deleteLmdbAfterMigration);
|
||||||
|
return repo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pow2Minus1StateRepository(string path)
|
||||||
|
{
|
||||||
|
DatabasePath = path;
|
||||||
|
_logDevice = Devices.CreateLogDevice(path + ".log");
|
||||||
|
_objectLogDevice = Devices.CreateLogDevice(path + ".obj.log");
|
||||||
|
_store = new FasterKV<string, string>(
|
||||||
|
1L << 18,
|
||||||
|
new LogSettings
|
||||||
|
{
|
||||||
|
LogDevice = _logDevice,
|
||||||
|
ObjectLogDevice = _objectLogDevice,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MarkChecked(ulong prime)
|
||||||
|
{
|
||||||
|
using var session = _store.For(new SimpleFunctions<string, string>()).NewSession<SimpleFunctions<string, string>>();
|
||||||
|
string key = prime.ToString(CultureInfo.InvariantCulture);
|
||||||
|
session.Upsert(key, "checked");
|
||||||
|
session.CompletePending(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsChecked(ulong prime)
|
||||||
|
{
|
||||||
|
using var session = _store.For(new SimpleFunctions<string, string>()).NewSession<SimpleFunctions<string, string>>();
|
||||||
|
string key = prime.ToString(CultureInfo.InvariantCulture);
|
||||||
|
var status = session.Read(key, out _);
|
||||||
|
session.CompletePending(true);
|
||||||
|
return status.Found;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Commit()
|
||||||
|
{
|
||||||
|
_store.Log.FlushAndEvict(wait: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_store.Dispose();
|
||||||
|
_logDevice.Dispose();
|
||||||
|
_objectLogDevice.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MigrateFromLmdb(string lmdbPath, string databaseName, bool deleteAfter)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!LmdbExists(lmdbPath))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"No LMDB pow2minus1 state found at '{lmdbPath}', skipping migration.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"Migrating pow2minus1 state from LMDB at '{lmdbPath}' to FASTER...");
|
||||||
|
using var env = new LightningEnvironment(lmdbPath) { MaxDatabases = 2 };
|
||||||
|
env.Open();
|
||||||
|
using var tx = env.BeginTransaction(TransactionBeginFlags.ReadOnly);
|
||||||
|
var db = tx.OpenDatabase(databaseName, new DatabaseConfiguration { Flags = DatabaseOpenFlags.None });
|
||||||
|
using var cursor = tx.CreateCursor(db);
|
||||||
|
foreach (var entry in cursor.AsEnumerable())
|
||||||
|
{
|
||||||
|
string key = Utf8.GetString(entry.Item1.CopyToNewArray());
|
||||||
|
if (ulong.TryParse(key, NumberStyles.None, CultureInfo.InvariantCulture, out ulong prime))
|
||||||
|
{
|
||||||
|
MarkChecked(prime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_store.Log.FlushAndEvict(wait: true);
|
||||||
|
Console.WriteLine("Migration of pow2minus1 state to FASTER completed.");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore migration issues
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deleteAfter)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Starting cleanup of LMDB pow2minus1 state at '{lmdbPath}'...");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Directory.Exists(lmdbPath))
|
||||||
|
{
|
||||||
|
Directory.Delete(lmdbPath, recursive: true);
|
||||||
|
}
|
||||||
|
else if (File.Exists(lmdbPath))
|
||||||
|
{
|
||||||
|
File.Delete(lmdbPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore cleanup failures
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool LmdbExists(string path)
|
||||||
|
{
|
||||||
|
if (Directory.Exists(path) || File.Exists(path))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string candidate = Path.Combine(path, "data.mdb");
|
||||||
|
if (File.Exists(candidate))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string? directory = Path.GetDirectoryName(path);
|
||||||
|
if (!string.IsNullOrEmpty(directory) && Directory.Exists(directory))
|
||||||
|
{
|
||||||
|
string sibling = Path.Combine(directory, "data.mdb");
|
||||||
|
if (File.Exists(sibling))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,6 +36,7 @@ The source code relays on selected functionality from the following packages:
|
|||||||
* [FluentAssertions](https://www.nuget.org/packages/FluentAssertions)
|
* [FluentAssertions](https://www.nuget.org/packages/FluentAssertions)
|
||||||
* [ILGPU](https://www.nuget.org/packages/ILGPU)
|
* [ILGPU](https://www.nuget.org/packages/ILGPU)
|
||||||
* [ILGPU.Algorithms](https://www.nuget.org/packages/ILGPU.Algorithms)
|
* [ILGPU.Algorithms](https://www.nuget.org/packages/ILGPU.Algorithms)
|
||||||
|
* [LightningDB](https://www.nuget.org/packages/LightningDB) (LMDB-backed repositories for by-divisor state)
|
||||||
* [Open.Numeric.Primes](https://www.nuget.org/packages/Open.Numeric.Primes)
|
* [Open.Numeric.Primes](https://www.nuget.org/packages/Open.Numeric.Primes)
|
||||||
* [PeterO.Numbers](https://www.nuget.org/packages/PeterO.Numbers)
|
* [PeterO.Numbers](https://www.nuget.org/packages/PeterO.Numbers)
|
||||||
* [xunit](https://www.nuget.org/packages/xunit)
|
* [xunit](https://www.nuget.org/packages/xunit)
|
||||||
|
|||||||
@@ -42,6 +42,12 @@
|
|||||||
- [x] Skip trivial divisors and previously recorded divisors for p when scanning/rebuilding stats.
|
- [x] Skip trivial divisors and previously recorded divisors for p when scanning/rebuilding stats.
|
||||||
- [x] Persist updated model after processing primes (additions from new divisors found during run).
|
- [x] Persist updated model after processing primes (additions from new divisors found during run).
|
||||||
|
|
||||||
|
5) Additional increment modes and state separation
|
||||||
|
- [x] Add percentile and additive increment modes alongside predictive/pow2/sequential.
|
||||||
|
- [x] Use mode-specific state file extensions (.predictive/.percentile/.additive) to avoid cross-mode interference.
|
||||||
|
- [x] Wire CLI flag `--bydivisor-k-increment=predictive|percentile|additive|pow2|sequential` (default predictive) and route Program.cs to the correct tester.
|
||||||
|
- [x] Extend model percentiles (k10/k25/k50/k75) and delta-k tuning for additive mode.
|
||||||
|
|
||||||
4) Validation
|
4) Validation
|
||||||
- [ ] Add targeted unit/functional tests (xUnit + FluentAssertions) for classifier defaults, model rebuild from synthetic results, and scan plan generation for easy vs hard classes.
|
- [ ] Add targeted unit/functional tests (xUnit + FluentAssertions) for classifier defaults, model rebuild from synthetic results, and scan plan generation for easy vs hard classes.
|
||||||
- [ ] Document how to run fast tests (Category=Fast) and keep runtime under 2 minutes.
|
- [ ] Document how to run fast tests (Category=Fast) and keep runtime under 2 minutes.
|
||||||
|
|||||||
Reference in new issue
Block a user