DivisorSet.BitContradiction mode added to --mersenne=bydivisor.

This commit is contained in:
mlemanczyk committed 2026-01-04 17:59:02 +01:00
1 parent 6a924f4041
commit 45e3455ff3
11 files changed
+1133 -28

No files matched your search

@@ -0,0 +1,375 @@
using System;
namespace PerfectNumbers.Core.ByDivisor;
/// <summary>
/// Helper utilities for the BitContradiction divisor search. These methods implement the
/// interval-based carry propagation described in the BitContradictions plan document.
/// The solver itself will build on top of these primitives.
/// </summary>
internal static class BitContradictionSolver
{
internal enum BitState
{
Zero = 0,
One = 1,
Unknown = 2,
}
internal readonly struct ColumnBounds
{
public readonly long ForcedOnes;
public readonly long PossibleOnes;
public ColumnBounds(long forcedOnes, long possibleOnes)
{
if (forcedOnes < 0)
{
throw new ArgumentOutOfRangeException(nameof(forcedOnes));
}
if (possibleOnes < forcedOnes)
{
throw new ArgumentOutOfRangeException(nameof(possibleOnes), "possibleOnes must be >= forcedOnes.");
}
ForcedOnes = forcedOnes;
PossibleOnes = possibleOnes;
}
}
internal readonly struct CarryRange
{
public readonly long Min;
public readonly long Max;
public CarryRange(long min, long max)
{
if (min < 0)
{
throw new ArgumentOutOfRangeException(nameof(min));
}
if (max < min)
{
throw new ArgumentOutOfRangeException(nameof(max), "max must be >= min.");
}
Min = min;
Max = max;
}
public static CarryRange Single(long value) => new(value, value);
}
internal enum ContradictionReason
{
None,
ParityUnreachable,
TruncatedLength,
TailNotZero,
}
internal static ColumnBounds ComputeColumnBounds(
ReadOnlySpan<BitState> multiplicand,
ReadOnlySpan<BitState> multiplier,
int columnIndex)
{
if (columnIndex < 0)
{
throw new ArgumentOutOfRangeException(nameof(columnIndex));
}
long forced = 0;
long possible = 0;
for (int i = 0; i <= columnIndex; i++)
{
BitState a = i < multiplicand.Length ? multiplicand[i] : BitState.Zero;
BitState q = columnIndex - i < multiplier.Length ? multiplier[columnIndex - i] : BitState.Zero;
if (a == BitState.Zero || q == BitState.Zero)
{
continue;
}
if (a == BitState.One && q == BitState.One)
{
forced++;
possible++;
continue;
}
possible++;
}
return new ColumnBounds(forced, possible);
}
internal static bool TryPropagateCarry(
CarryRange currentCarry,
long forcedOnes,
long possibleOnes,
int requiredResultBit,
out CarryRange nextCarry,
out ContradictionReason reason)
{
if (forcedOnes < 0 || possibleOnes < forcedOnes)
{
throw new ArgumentOutOfRangeException(nameof(possibleOnes), "possibleOnes must be >= forcedOnes and both non-negative.");
}
long minSum = currentCarry.Min + forcedOnes;
long maxSum = currentCarry.Max + possibleOnes;
int parity = requiredResultBit & 1;
long minAligned = AlignUpToParity(minSum, parity);
long maxAligned = AlignDownToParity(maxSum, parity);
if (minAligned > maxAligned)
{
nextCarry = default;
reason = ContradictionReason.ParityUnreachable;
return false;
}
nextCarry = new CarryRange((minAligned - requiredResultBit) >> 1, (maxAligned - requiredResultBit) >> 1);
reason = ContradictionReason.None;
return true;
}
/// <summary>
/// Full column-wise propagation for a given set of q one-bit offsets (LSB=0) and exponent p.
/// Returns true if the solver could decide locally (divides or contradiction).
/// </summary>
internal static bool TryCheckDivisibilityFromOneOffsets(
ReadOnlySpan<int> qOneOffsets,
ulong p,
out bool divides,
out ContradictionReason reason)
{
divides = false;
reason = ContradictionReason.None;
if (p == 0 || qOneOffsets.Length == 0)
{
reason = ContradictionReason.ParityUnreachable;
return true;
}
// Exact small-case evaluation to short-circuit both success and failure deterministically.
int highestBit = qOneOffsets[^1];
if (p <= 64 && highestBit < 63)
{
ulong q = 0;
foreach (int bit in qOneOffsets)
{
q |= 1UL << bit;
}
ulong mersenne = (1UL << (int)p) - 1UL;
divides = q != 0 && (mersenne % q == 0);
reason = divides ? ContradictionReason.None : ContradictionReason.ParityUnreachable;
return true;
}
int maxOffset = qOneOffsets[^1];
if ((ulong)(maxOffset + 1) >= p)
{
divides = false;
reason = ContradictionReason.TruncatedLength;
return true;
}
if (qOneOffsets[0] != 0)
{
divides = false;
reason = ContradictionReason.ParityUnreachable;
return true;
}
var aBits = new Dictionary<long, sbyte>(capacity: qOneOffsets.Length * 4);
long carryMin = 0;
long carryMax = 0;
long maxKnownA = -1;
long maxAllowedA = (long)p - (maxOffset + 1L);
if (maxAllowedA < 0)
{
divides = false;
reason = ContradictionReason.TruncatedLength;
return true;
}
for (long column = 0; column < (long)p; column++)
{
long forced = 0;
long unknown = 0;
long chosenIndex = -1;
foreach (int offset in qOneOffsets)
{
long aIndex = column - offset;
if (aIndex < 0)
{
break;
}
if (aIndex > maxAllowedA)
{
continue; // a must be zero here
}
if (aBits.TryGetValue(aIndex, out sbyte aVal))
{
if (aVal == 1)
{
forced++;
}
}
else
{
unknown++;
if (chosenIndex == -1)
{
chosenIndex = aIndex;
}
}
}
long minSum = carryMin + forced;
long maxSum = carryMax + forced + unknown;
const int requiredParity = 1;
if (minSum == maxSum)
{
if ((minSum & 1) != requiredParity)
{
divides = false;
reason = ContradictionReason.ParityUnreachable;
return true;
}
long nextCarry = (minSum - requiredParity) >> 1;
carryMin = nextCarry;
carryMax = nextCarry;
}
else
{
if ((minSum & 1) != requiredParity)
{
if (unknown == 0)
{
divides = false;
return true;
}
if (chosenIndex >= 0)
{
aBits[chosenIndex] = 1;
forced++;
unknown--;
if (chosenIndex > maxKnownA)
{
maxKnownA = chosenIndex;
}
}
}
if (!TryPropagateCarry(
new CarryRange(carryMin, carryMax),
forced,
forced + unknown,
requiredParity,
out var next,
out var propagateReason))
{
divides = false;
reason = propagateReason;
return true;
}
carryMin = next.Min;
carryMax = next.Max;
}
if (chosenIndex >= 0 && !aBits.ContainsKey(chosenIndex))
{
aBits[chosenIndex] = 1;
if (chosenIndex > maxKnownA)
{
maxKnownA = chosenIndex;
}
}
}
// Tail: keep propagating until carry collapses to zero. No fixed budget; carry halves every column when no ones remain.
for (long column = (long)p; carryMax > 0; column++)
{
long forced = 0;
long unknown = 0;
foreach (int offset in qOneOffsets)
{
long aIndex = column - offset;
if (aIndex < 0)
{
break;
}
if (aIndex > maxAllowedA)
{
continue; // forced zero
}
if (aBits.TryGetValue(aIndex, out sbyte aVal))
{
if (aVal == 1)
{
forced++;
}
}
else
{
unknown++;
}
}
if (!TryPropagateCarry(
new CarryRange(carryMin, carryMax),
forced,
forced + unknown,
requiredResultBit: 0,
out var next,
out var reasonTail))
{
divides = false;
reason = reasonTail;
return true;
}
carryMin = next.Min;
carryMax = next.Max;
}
divides = carryMin == 0 && carryMax == 0;
reason = divides ? ContradictionReason.None : ContradictionReason.TailNotZero;
return true;
}
internal static bool TryCheckDivisibilityFromOneOffsets(
ReadOnlySpan<int> qOneOffsets,
ulong p,
out bool divides)
{
return TryCheckDivisibilityFromOneOffsets(qOneOffsets, p, out divides, out _);
}
private static long AlignUpToParity(long value, int parity)
{
return (value & 1) == parity ? value : value + 1;
}
private static long AlignDownToParity(long value, int parity)
{
return (value & 1) == parity ? value : value - 1;
}
}
@@ -19,6 +19,7 @@ public enum DivisorSet
Percentile,
Additive,
TopDown,
BitContradiction,
}
[EnumDependentTemplate(typeof(DivisorSet))]
@@ -49,6 +50,9 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
private GpuUInt128WorkSet _divisorScanGpuWorkSet;
private static readonly ConcurrentDictionary<ulong, byte> _checkedPow2MinusOne = new();
private ulong _currentPrime;
#if DivisorSet_BitContradiction
private static Persistence.BitContradictionStateRepository? _bitContradictionStateRepository;
#endif
#if DivisorSet_TopDown
private BigInteger _topDownCursor;
#endif
@@ -328,6 +332,9 @@ private ulong _currentPrime;
_groupsResumeK = _groupsLastSavedK + BigInteger.One;
_currentPow2Phase = Pow2Phase.None;
_preparedSpecialsInitialized = false;
#endif
#if DivisorSet_BitContradiction
_bitContradictionStateRepository = EnvironmentConfiguration.BitContradictionStateRepository;
#endif
}
@@ -432,6 +439,17 @@ private ulong _currentPrime;
// Console.WriteLine($"[by-divisor-plan] p={prime} suffix={(prime & 1023UL)} using pow2-groups plan; model skipped.");
return compositePow2;
#else
#if DivisorSet_BitContradiction
bool compositeBitContradiction = CheckDivisorsBitContradiction(
prime,
primeDecimalMask,
allowedMax,
normalizedMinK,
step,
out processedAll,
out foundDivisor);
return compositeBitContradiction;
#endif
#if DivisorSet_TopDown
bool compositeTopDown = CheckDivisorsTopDown(
prime,
@@ -3309,6 +3327,176 @@ private ulong _currentPrime;
}
#endif
#if DivisorSet_BitContradiction
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
private bool CheckDivisorsBitContradiction(
ulong prime,
ushort primeDecimalMask,
BigInteger allowedMax,
BigInteger minK,
BigInteger step,
out bool processedAll,
out BigInteger foundDivisor)
{
processedAll = true;
foundDivisor = BigInteger.Zero;
BigInteger currentK = minK;
bool hasLimit = !allowedMax.IsZero;
BigInteger maxK = hasLimit ? (allowedMax - BigInteger.One) / step : BigInteger.Zero;
while (true)
{
BigInteger divisor = (step * currentK) + BigInteger.One;
bool decided = TryExactBitContradictionCheck(prime, divisor, out bool divides);
if (!decided)
{
throw new InvalidOperationException($"BitContradiction check did not decide for p={prime}, divisor={divisor} (k={currentK}).");
}
if (divides)
{
foundDivisor = divisor;
return true;
}
if (hasLimit && currentK >= maxK)
{
break;
}
currentK += BigInteger.One;
}
return false;
bool fits64 = minK <= ulong.MaxValue;
bool limitFits64 = allowedMax.IsZero || allowedMax <= ulong.MaxValue;
if (fits64 && limitFits64)
{
ulong allowedMax64 = allowedMax.IsZero ? ulong.MaxValue : (ulong)allowedMax;
return CheckDivisors64Bit(
prime,
primeDecimalMask,
allowedMax64,
(ulong)minK,
out processedAll,
out ulong found64);
}
if (fits64 && allowedMax > ulong.MaxValue)
{
ulong allowedMax64 = ulong.MaxValue;
bool composite64 = CheckDivisors64Bit(
prime,
primeDecimalMask,
allowedMax64,
(ulong)minK,
out processedAll,
out ulong found64);
if (composite64)
{
foundDivisor = found64;
return true;
}
BigInteger iterations = ((BigInteger)(allowedMax64 - (((ulong)prime << 1) * (ulong)minK) - 1UL) / (ulong)step) + BigInteger.One;
BigInteger nextK = minK + iterations;
return CheckDivisorsLarge(
prime,
primeDecimalMask,
allowedMax,
nextK,
step,
out processedAll,
out foundDivisor);
}
return CheckDivisorsLarge(
prime,
primeDecimalMask,
allowedMax,
minK,
step,
out processedAll,
out foundDivisor);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryExactBitContradictionCheck(ulong prime, BigInteger divisor, out bool divides)
{
divides = false;
if (prime == 0 || divisor <= BigInteger.One)
{
Console.WriteLine($"[bitcontradiction] invalid inputs {prime} / {divisor}");
return false;
}
int qBitLength = GetBitLengthPortable(divisor);
if (qBitLength > (int)prime)
{
divides = false;
return true;
}
// Fast structural contradictions only: q must be odd and the implied maximum length of a must be positive.
if ((divisor & BigInteger.One) == BigInteger.Zero)
{
divides = false;
return true;
}
if (prime <= (ulong)qBitLength)
{
divides = false;
return true;
}
// Iterative column-wise propagation without storing all columns; we only keep assigned bits of 'a'.
Span<int> oneOffsets = stackalloc int[qBitLength];
int oneCount = 0;
for (int i = 0; i < qBitLength; i++)
{
if (((divisor >> i) & BigInteger.One) == BigInteger.One)
{
oneOffsets[oneCount++] = i;
}
}
if (oneCount == 0)
{
divides = false;
return true;
}
ReadOnlySpan<int> oneOffsetsSlice = oneOffsets[..oneCount];
bool decided = BitContradictionSolver.TryCheckDivisibilityFromOneOffsets(oneOffsetsSlice, prime, out divides, out var reason);
if (decided && divides)
{
// Verify to avoid false positives on structurally admissible but non-dividing q.
bool structuralOk = ((divisor - BigInteger.One) % (((BigInteger)prime) << 1)) == BigInteger.Zero;
bool dividesExact = structuralOk && BigInteger.ModPow(2, prime, divisor) == BigInteger.One;
if (!dividesExact)
{
divides = false;
reason = BitContradictionSolver.ContradictionReason.ParityUnreachable;
}
}
if (decided && !divides)
{
string message = $"[bit-contradiction] p={prime} ruled out divisor={divisor} ({reason})";
Console.WriteLine(message);
_bitContradictionStateRepository?.Upsert(prime, message);
}
Console.WriteLine($"[bitcontradiction] Prime={prime}, Divisor={divisor} Decided={decided} Divides={divides}");
return decided;
}
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private void MarkPow2MinusOneChecked()
{
@@ -23,6 +23,10 @@ using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTeste
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForCpuOrder;
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForHybridOrder;
using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForGpuOrder;
#elif DivisorSet_BitContradiction
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForCpuOrder;
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForHybridOrder;
using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForGpuOrder;
#else
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForCpuOrder;
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForHybridOrder;
@@ -85,6 +89,8 @@ internal struct MersenneNumberDivisorByDivisorPrimeScanSessionWithTemplate
extension = ".additive";
#elif DivisorSet_TopDown
extension = ".topdown";
#elif DivisorSet_BitContradiction
extension = ".bitcontradiction";
#endif
string stateFile = prime.ToString(CultureInfo.InvariantCulture) + extension;
@@ -29,6 +29,7 @@ public static class EnvironmentConfiguration
public static Persistence.KStateRepository? ByDivisorSpecialStateRepository;
public static Persistence.KStateRepository? ByDivisorGroupsStateRepository;
public static Persistence.Pow2Minus1StateRepository? ByDivisorPow2Minus1Repository;
public static Persistence.BitContradictionStateRepository? BitContradictionStateRepository;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Initialize()
@@ -18,6 +18,9 @@ using MersenneNumberDivisorByDivisorCpuTesterWithPercentileGpuOrder = PerfectNum
using MersenneNumberDivisorByDivisorCpuTesterWithAdditiveCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForCpuOrder;
using MersenneNumberDivisorByDivisorCpuTesterWithAdditiveHybridOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForHybridOrder;
using MersenneNumberDivisorByDivisorCpuTesterWithAdditiveGpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForGpuOrder;
using MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForCpuOrder;
using MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionHybridOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForHybridOrder;
using MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionGpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForGpuOrder;
namespace PerfectNumbers.Core;
@@ -54,6 +57,37 @@ public static class MersenneNumberDivisorByDivisorTester
Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunCpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run(
List<ulong> candidates,
ref MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionCpuOrder 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 MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionCpuOrder prototype,
int workerCount,
int partitionSize,
Action mc,
Action cc,
Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunBitContradictionCpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run(
List<ulong> candidates,
@@ -178,6 +212,37 @@ public static class MersenneNumberDivisorByDivisorTester
Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunHybridOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run(
List<ulong> candidates,
ref MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionHybridOrder 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 MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionHybridOrder prototype,
int workerCount,
int partitionSize,
Action mc,
Action cc,
Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunBitContradictionHybridOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run(
List<ulong> candidates,
@@ -302,6 +367,37 @@ public static class MersenneNumberDivisorByDivisorTester
Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunGpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run(
List<ulong> candidates,
ref MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionGpuOrder 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 MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionGpuOrder prototype,
int workerCount,
int partitionSize,
Action mc,
Action cc,
Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunBitContradictionGpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run(
List<ulong> candidates,
@@ -2431,6 +2527,150 @@ public static class MersenneNumberDivisorByDivisorTester
}
}
private static void RunBitContradictionGpuOrderFiltered(
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionGpuOrder prototype,
int workerCount,
int partitionSize,
Action markComposite,
Action clearComposite,
Action<ulong, bool, bool, bool, BigInteger> printResult)
{
MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionGpuOrder prototypeCopy = prototype;
int totalCount = filteredPrimes.Count;
if (partitionSize > totalCount)
{
partitionSize = totalCount;
}
if (workerCount <= 1 || totalCount <= partitionSize)
{
var session = new Cpu.MersenneNumberDivisorByDivisorPrimeScanSessionWithForBitContradictionDivisorSetForGpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
for (int i = 0; i < totalCount; i++)
{
session.ProcessPrime(filteredPrimes[i]);
}
return;
}
var options = new ParallelOptions
{
MaxDegreeOfParallelism = workerCount,
};
Parallel.For(
0,
(int)Math.Ceiling((double)totalCount / partitionSize),
options,
new Action<int>(partitionIndex =>
{
int startIndex = partitionIndex * partitionSize;
int count = Math.Min(partitionSize, totalCount - startIndex);
var session = new Cpu.MersenneNumberDivisorByDivisorPrimeScanSessionWithForBitContradictionDivisorSetForGpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
for (int i = 0; i < count; i++)
{
session.ProcessPrime(filteredPrimes[startIndex + i]);
}
}));
}
private static void RunBitContradictionHybridOrderFiltered(
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionHybridOrder prototype,
int workerCount,
int partitionSize,
Action markComposite,
Action clearComposite,
Action<ulong, bool, bool, bool, BigInteger> printResult)
{
MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionHybridOrder prototypeCopy = prototype;
int totalCount = filteredPrimes.Count;
if (partitionSize > totalCount)
{
partitionSize = totalCount;
}
if (workerCount <= 1 || totalCount <= partitionSize)
{
var session = new Cpu.MersenneNumberDivisorByDivisorPrimeScanSessionWithForBitContradictionDivisorSetForHybridOrder(prototypeCopy, markComposite, clearComposite, printResult);
for (int i = 0; i < totalCount; i++)
{
session.ProcessPrime(filteredPrimes[i]);
}
return;
}
var options = new ParallelOptions
{
MaxDegreeOfParallelism = workerCount,
};
Parallel.For(
0,
(int)Math.Ceiling((double)totalCount / partitionSize),
options,
new Action<int>(partitionIndex =>
{
int startIndex = partitionIndex * partitionSize;
int count = Math.Min(partitionSize, totalCount - startIndex);
var session = new Cpu.MersenneNumberDivisorByDivisorPrimeScanSessionWithForBitContradictionDivisorSetForHybridOrder(prototypeCopy, markComposite, clearComposite, printResult);
for (int i = 0; i < count; i++)
{
session.ProcessPrime(filteredPrimes[startIndex + i]);
}
}));
}
private static void RunBitContradictionCpuOrderFiltered(
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionCpuOrder prototype,
int workerCount,
int partitionSize,
Action markComposite,
Action clearComposite,
Action<ulong, bool, bool, bool, BigInteger> printResult)
{
MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionCpuOrder prototypeCopy = prototype;
int totalCount = filteredPrimes.Count;
if (partitionSize > totalCount)
{
partitionSize = totalCount;
}
if (workerCount <= 1 || totalCount <= partitionSize)
{
var session = new Cpu.MersenneNumberDivisorByDivisorPrimeScanSessionWithForBitContradictionDivisorSetForCpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
for (int i = 0; i < totalCount; i++)
{
session.ProcessPrime(filteredPrimes[i]);
}
return;
}
var options = new ParallelOptions
{
MaxDegreeOfParallelism = workerCount,
};
Parallel.For(
0,
(int)Math.Ceiling((double)totalCount / partitionSize),
options,
new Action<int>(partitionIndex =>
{
int startIndex = partitionIndex * partitionSize;
int count = Math.Min(partitionSize, totalCount - startIndex);
var session = new Cpu.MersenneNumberDivisorByDivisorPrimeScanSessionWithForBitContradictionDivisorSetForCpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
for (int i = 0; i < count; i++)
{
session.ProcessPrime(filteredPrimes[startIndex + i]);
}
}));
}
private static void RunHybridOrderFilteredPow2(
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithForPow2GroupsDivisorSetForHybridOrder prototype,
@@ -0,0 +1,71 @@
using System;
using System.Globalization;
using System.IO;
using System.Text;
using FASTER.core;
namespace PerfectNumbers.Core.Persistence;
/// <summary>
/// Stores per-prime BitContradiction solver state (arbitrary string payload) using Microsoft.FASTER.Core.
/// No LMDB migration is performed for this repository.
/// </summary>
public sealed class BitContradictionStateRepository : 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 BitContradictionStateRepository Open(string directory, string repositoryFileName = "bitcontradiction.solver.faster")
{
Directory.CreateDirectory(directory);
string dbPath = Path.Combine(directory, repositoryFileName);
return new BitContradictionStateRepository(dbPath);
}
private BitContradictionStateRepository(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, string state)
{
using var session = _store.For(new SimpleFunctions<string, string>()).NewSession<SimpleFunctions<string, string>>();
string keyString = prime.ToString(CultureInfo.InvariantCulture);
session.Upsert(keyString, state);
session.CompletePending(true);
}
public bool TryGet(ulong prime, out string? state)
{
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)
{
state = stored;
return true;
}
state = null;
return false;
}
public void Dispose()
{
_store.Dispose();
_logDevice.Dispose();
_objectLogDevice.Dispose();
}
}