DivisorSet.BitContradiction mode added to --mersenne=bydivisor.
This commit is contained in:
1 parent
6a924f4041
commit
45e3455ff3
11 files changed
+1107
-2
No files matched your search
@@ -13,6 +13,7 @@ namespace EvenPerfectBitScanner;
|
|||||||
Percentile,
|
Percentile,
|
||||||
Additive,
|
Additive,
|
||||||
TopDown,
|
TopDown,
|
||||||
|
BitContradiction,
|
||||||
}
|
}
|
||||||
|
|
||||||
internal readonly struct CliArguments
|
internal readonly struct CliArguments
|
||||||
@@ -342,9 +343,13 @@ namespace EvenPerfectBitScanner;
|
|||||||
{
|
{
|
||||||
byDivisorKIncrementMode = ByDivisorKIncrementMode.TopDown;
|
byDivisorKIncrementMode = ByDivisorKIncrementMode.TopDown;
|
||||||
}
|
}
|
||||||
|
else if (value.Equals("bitcontradiction", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
byDivisorKIncrementMode = ByDivisorKIncrementMode.BitContradiction;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine("Invalid value for --bydivisor-k-increment. Use sequential, pow2groups, predictive, percentile, additive, or topdown.");
|
Console.WriteLine("Invalid value for --bydivisor-k-increment. Use sequential, pow2groups, predictive, percentile, additive, topdown, or bitcontradiction.");
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -710,7 +715,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|percentile|additive|topdown k iteration strategy for --mersenne=bydivisor (default sequential)");
|
Console.WriteLine(" --bydivisor-k-increment=sequential|pow2groups|predictive|percentile|additive|topdown|bitcontradiction 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)");
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ using ByDivisorGpuOrderAdditive = PerfectNumbers.Core.Cpu.MersenneNumberDivisorB
|
|||||||
using ByDivisorCpuOrderTopDown = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForTopDownDivisorSetForCpuOrder;
|
using ByDivisorCpuOrderTopDown = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForTopDownDivisorSetForCpuOrder;
|
||||||
using ByDivisorHybridOrderTopDown = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForTopDownDivisorSetForHybridOrder;
|
using ByDivisorHybridOrderTopDown = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForTopDownDivisorSetForHybridOrder;
|
||||||
using ByDivisorGpuOrderTopDown = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForTopDownDivisorSetForGpuOrder;
|
using ByDivisorGpuOrderTopDown = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForTopDownDivisorSetForGpuOrder;
|
||||||
|
using ByDivisorCpuOrderBitContradiction = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForCpuOrder;
|
||||||
|
using ByDivisorHybridOrderBitContradiction = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForHybridOrder;
|
||||||
|
using ByDivisorGpuOrderBitContradiction = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForGpuOrder;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
|
||||||
namespace EvenPerfectBitScanner;
|
namespace EvenPerfectBitScanner;
|
||||||
@@ -58,6 +61,9 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
private static ByDivisorCpuOrderTopDown _byDivisorCpuOrderTesterTopDown;
|
private static ByDivisorCpuOrderTopDown _byDivisorCpuOrderTesterTopDown;
|
||||||
private static ByDivisorHybridOrderTopDown _byDivisorHybridOrderTesterTopDown;
|
private static ByDivisorHybridOrderTopDown _byDivisorHybridOrderTesterTopDown;
|
||||||
private static ByDivisorGpuOrderTopDown _byDivisorGpuOrderTesterTopDown;
|
private static ByDivisorGpuOrderTopDown _byDivisorGpuOrderTesterTopDown;
|
||||||
|
private static ByDivisorCpuOrderBitContradiction _byDivisorCpuOrderTesterBitContradiction;
|
||||||
|
private static ByDivisorHybridOrderBitContradiction _byDivisorHybridOrderTesterBitContradiction;
|
||||||
|
private static ByDivisorGpuOrderBitContradiction _byDivisorGpuOrderTesterBitContradiction;
|
||||||
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 _byDivisorModeUsesModel;
|
||||||
@@ -538,6 +544,7 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
ByDivisorKIncrementMode.Percentile => "percentile.kstate.faster",
|
ByDivisorKIncrementMode.Percentile => "percentile.kstate.faster",
|
||||||
ByDivisorKIncrementMode.Additive => "additive.kstate.faster",
|
ByDivisorKIncrementMode.Additive => "additive.kstate.faster",
|
||||||
ByDivisorKIncrementMode.TopDown => "topdown.kstate.faster",
|
ByDivisorKIncrementMode.TopDown => "topdown.kstate.faster",
|
||||||
|
ByDivisorKIncrementMode.BitContradiction => "bitcontradiction.kstate.faster",
|
||||||
_ => "sequential.kstate.faster",
|
_ => "sequential.kstate.faster",
|
||||||
};
|
};
|
||||||
string lmdbPath = Path.Combine(checksDir, Path.ChangeExtension(kRepoFile, ".lmdb"));
|
string lmdbPath = Path.Combine(checksDir, Path.ChangeExtension(kRepoFile, ".lmdb"));
|
||||||
@@ -545,6 +552,7 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
EnvironmentConfiguration.ByDivisorCheapKStateRepository = KStateRepository.Open(checksDir, "cheaplimit.kstate.faster", Path.Combine(checksDir, "cheaplimit.kstate.lmdb"), checksDir);
|
EnvironmentConfiguration.ByDivisorCheapKStateRepository = KStateRepository.Open(checksDir, "cheaplimit.kstate.faster", Path.Combine(checksDir, "cheaplimit.kstate.lmdb"), checksDir);
|
||||||
string pow2MinusLmdb = Path.Combine(checksDir, "pow2minus1.lmdb");
|
string pow2MinusLmdb = Path.Combine(checksDir, "pow2minus1.lmdb");
|
||||||
EnvironmentConfiguration.ByDivisorPow2Minus1Repository ??= Pow2Minus1StateRepository.Open(Path.Combine(checksDir, "pow2minus1.faster"), pow2MinusLmdb, checksDir);
|
EnvironmentConfiguration.ByDivisorPow2Minus1Repository ??= Pow2Minus1StateRepository.Open(Path.Combine(checksDir, "pow2minus1.faster"), pow2MinusLmdb, checksDir);
|
||||||
|
EnvironmentConfiguration.BitContradictionStateRepository ??= BitContradictionStateRepository.Open(checksDir);
|
||||||
|
|
||||||
if (kIncrementMode == ByDivisorKIncrementMode.Pow2Groups)
|
if (kIncrementMode == ByDivisorKIncrementMode.Pow2Groups)
|
||||||
{
|
{
|
||||||
@@ -730,6 +738,19 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
threadCount,
|
threadCount,
|
||||||
byDivisorPrimeRange);
|
byDivisorPrimeRange);
|
||||||
break;
|
break;
|
||||||
|
case ByDivisorKIncrementMode.BitContradiction:
|
||||||
|
Console.WriteLine("Using by-divisor bit-contradiction increment (CPU order).");
|
||||||
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
|
byDivisorCandidates,
|
||||||
|
ref _byDivisorCpuOrderTesterBitContradiction,
|
||||||
|
_byDivisorPreviousResults,
|
||||||
|
_byDivisorStartPrime,
|
||||||
|
static () => _lastCompositeP = true,
|
||||||
|
static () => _lastCompositeP = false,
|
||||||
|
HandleByDivisorResult,
|
||||||
|
threadCount,
|
||||||
|
byDivisorPrimeRange);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
MersenneNumberDivisorByDivisorTester.Run(
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
byDivisorCandidates,
|
byDivisorCandidates,
|
||||||
@@ -800,6 +821,19 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
threadCount,
|
threadCount,
|
||||||
byDivisorPrimeRange);
|
byDivisorPrimeRange);
|
||||||
break;
|
break;
|
||||||
|
case ByDivisorKIncrementMode.BitContradiction:
|
||||||
|
Console.WriteLine("Using by-divisor bit-contradiction increment (Hybrid order).");
|
||||||
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
|
byDivisorCandidates,
|
||||||
|
ref _byDivisorHybridOrderTesterBitContradiction,
|
||||||
|
_byDivisorPreviousResults,
|
||||||
|
_byDivisorStartPrime,
|
||||||
|
static () => _lastCompositeP = true,
|
||||||
|
static () => _lastCompositeP = false,
|
||||||
|
HandleByDivisorResult,
|
||||||
|
threadCount,
|
||||||
|
byDivisorPrimeRange);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
MersenneNumberDivisorByDivisorTester.Run(
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
byDivisorCandidates,
|
byDivisorCandidates,
|
||||||
@@ -870,6 +904,19 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
threadCount,
|
threadCount,
|
||||||
byDivisorPrimeRange);
|
byDivisorPrimeRange);
|
||||||
break;
|
break;
|
||||||
|
case ByDivisorKIncrementMode.BitContradiction:
|
||||||
|
Console.WriteLine("Using by-divisor bit-contradiction increment (GPU order).");
|
||||||
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
|
byDivisorCandidates,
|
||||||
|
ref _byDivisorGpuOrderTesterBitContradiction,
|
||||||
|
_byDivisorPreviousResults,
|
||||||
|
_byDivisorStartPrime,
|
||||||
|
static () => _lastCompositeP = true,
|
||||||
|
static () => _lastCompositeP = false,
|
||||||
|
HandleByDivisorResult,
|
||||||
|
threadCount,
|
||||||
|
byDivisorPrimeRange);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
MersenneNumberDivisorByDivisorTester.Run(
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
byDivisorCandidates,
|
byDivisorCandidates,
|
||||||
@@ -1279,6 +1326,7 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
ByDivisorKIncrementMode.Percentile => _byDivisorCpuOrderTesterPercentile.IsPrime(p, out detailedCheck, out _),
|
ByDivisorKIncrementMode.Percentile => _byDivisorCpuOrderTesterPercentile.IsPrime(p, out detailedCheck, out _),
|
||||||
ByDivisorKIncrementMode.Additive => _byDivisorCpuOrderTesterAdditive.IsPrime(p, out detailedCheck, out _),
|
ByDivisorKIncrementMode.Additive => _byDivisorCpuOrderTesterAdditive.IsPrime(p, out detailedCheck, out _),
|
||||||
ByDivisorKIncrementMode.TopDown => _byDivisorCpuOrderTesterTopDown.IsPrime(p, out detailedCheck, out _),
|
ByDivisorKIncrementMode.TopDown => _byDivisorCpuOrderTesterTopDown.IsPrime(p, out detailedCheck, out _),
|
||||||
|
ByDivisorKIncrementMode.BitContradiction => _byDivisorCpuOrderTesterBitContradiction.IsPrime(p, out detailedCheck, out _),
|
||||||
_ => _byDivisorCpuOrderTester.IsPrime(p, out detailedCheck, out _),
|
_ => _byDivisorCpuOrderTester.IsPrime(p, out detailedCheck, out _),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1292,6 +1340,7 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
ByDivisorKIncrementMode.Percentile => _byDivisorHybridOrderTesterPercentile.IsPrime(p, out detailedCheck, out _),
|
ByDivisorKIncrementMode.Percentile => _byDivisorHybridOrderTesterPercentile.IsPrime(p, out detailedCheck, out _),
|
||||||
ByDivisorKIncrementMode.Additive => _byDivisorHybridOrderTesterAdditive.IsPrime(p, out detailedCheck, out _),
|
ByDivisorKIncrementMode.Additive => _byDivisorHybridOrderTesterAdditive.IsPrime(p, out detailedCheck, out _),
|
||||||
ByDivisorKIncrementMode.TopDown => _byDivisorHybridOrderTesterTopDown.IsPrime(p, out detailedCheck, out _),
|
ByDivisorKIncrementMode.TopDown => _byDivisorHybridOrderTesterTopDown.IsPrime(p, out detailedCheck, out _),
|
||||||
|
ByDivisorKIncrementMode.BitContradiction => _byDivisorHybridOrderTesterBitContradiction.IsPrime(p, out detailedCheck, out _),
|
||||||
_ => _byDivisorHybridOrderTester.IsPrime(p, out detailedCheck, out _),
|
_ => _byDivisorHybridOrderTester.IsPrime(p, out detailedCheck, out _),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1303,6 +1352,7 @@ private static ThreadLocal<object>? MersenneTesters;
|
|||||||
ByDivisorKIncrementMode.Percentile => _byDivisorGpuOrderTesterPercentile.IsPrime(p, out detailedCheck, out _),
|
ByDivisorKIncrementMode.Percentile => _byDivisorGpuOrderTesterPercentile.IsPrime(p, out detailedCheck, out _),
|
||||||
ByDivisorKIncrementMode.Additive => _byDivisorGpuOrderTesterAdditive.IsPrime(p, out detailedCheck, out _),
|
ByDivisorKIncrementMode.Additive => _byDivisorGpuOrderTesterAdditive.IsPrime(p, out detailedCheck, out _),
|
||||||
ByDivisorKIncrementMode.TopDown => _byDivisorGpuOrderTesterTopDown.IsPrime(p, out detailedCheck, out _),
|
ByDivisorKIncrementMode.TopDown => _byDivisorGpuOrderTesterTopDown.IsPrime(p, out detailedCheck, out _),
|
||||||
|
ByDivisorKIncrementMode.BitContradiction => _byDivisorGpuOrderTesterBitContradiction.IsPrime(p, out detailedCheck, out _),
|
||||||
_ => _byDivisorGpuOrderTester.IsPrime(p, out detailedCheck, out _),
|
_ => _byDivisorGpuOrderTester.IsPrime(p, out detailedCheck, out _),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using FluentAssertions;
|
||||||
|
using PerfectNumbers.Core.ByDivisor;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Tests;
|
||||||
|
|
||||||
|
public class BitContradictionIntegrationTests
|
||||||
|
{
|
||||||
|
// 2^11 - 1 = 2047 = 23 * 89
|
||||||
|
[Fact]
|
||||||
|
public void BitContradictionAcceptsKnownDivisorWithDecision()
|
||||||
|
{
|
||||||
|
BigInteger divisor = 23;
|
||||||
|
ulong p = 11;
|
||||||
|
bool decided = Invoke(divisor, p, out bool divides);
|
||||||
|
decided.Should().BeTrue("small p should be decided exactly");
|
||||||
|
divides.Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BitContradictionRejectsEvenDivisorQuickly()
|
||||||
|
{
|
||||||
|
BigInteger divisor = 24;
|
||||||
|
ulong p = 11;
|
||||||
|
bool decided = Invoke(divisor, p, out bool divides);
|
||||||
|
decided.Should().BeTrue();
|
||||||
|
divides.Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BitContradictionRejectsTooLongDivisor()
|
||||||
|
{
|
||||||
|
// q bit length >= p => impossible
|
||||||
|
BigInteger divisor = (BigInteger.One << 15) + 1;
|
||||||
|
ulong p = 11;
|
||||||
|
bool decided = Invoke(divisor, p, out bool divides);
|
||||||
|
decided.Should().BeTrue();
|
||||||
|
divides.Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BitContradictionRejectsNonDivisorWithDecision()
|
||||||
|
{
|
||||||
|
// 2^11 - 1 is not divisible by 25
|
||||||
|
BigInteger divisor = 25;
|
||||||
|
ulong p = 11;
|
||||||
|
bool decided = Invoke(divisor, p, out bool divides);
|
||||||
|
decided.Should().BeTrue("small p should be decided exactly");
|
||||||
|
divides.Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool Invoke(BigInteger divisor, ulong p, out bool divides)
|
||||||
|
{
|
||||||
|
int bitLen = GetBitLength(divisor);
|
||||||
|
Span<int> offsets = stackalloc int[bitLen];
|
||||||
|
int count = 0;
|
||||||
|
for (int i = 0; i < bitLen; i++)
|
||||||
|
{
|
||||||
|
if (((divisor >> i) & BigInteger.One) == BigInteger.One)
|
||||||
|
{
|
||||||
|
offsets[count++] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return BitContradictionSolver.TryCheckDivisibilityFromOneOffsets(offsets[..count], p, out divides);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetBitLength(BigInteger value)
|
||||||
|
{
|
||||||
|
if (value.IsZero)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)Math.Floor(BigInteger.Log(value, 2.0)) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
using FluentAssertions;
|
||||||
|
using PerfectNumbers.Core.ByDivisor;
|
||||||
|
using Xunit;
|
||||||
|
using static PerfectNumbers.Core.ByDivisor.BitContradictionSolver;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Tests;
|
||||||
|
|
||||||
|
public class BitContradictionSolverTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void ComputeColumnBounds_TracksForcedAndPossibleOnes()
|
||||||
|
{
|
||||||
|
BitState[] a = [BitState.One, BitState.Unknown];
|
||||||
|
BitState[] q = [BitState.One, BitState.One];
|
||||||
|
|
||||||
|
ColumnBounds bounds = ComputeColumnBounds(a, q, 1);
|
||||||
|
|
||||||
|
bounds.ForcedOnes.Should().Be(1);
|
||||||
|
bounds.PossibleOnes.Should().Be(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TryPropagateCarry_FailsWhenParityUnreachable()
|
||||||
|
{
|
||||||
|
CarryRange carry = CarryRange.Single(0);
|
||||||
|
|
||||||
|
bool success = TryPropagateCarry(
|
||||||
|
carry,
|
||||||
|
forcedOnes: 0,
|
||||||
|
possibleOnes: 0,
|
||||||
|
requiredResultBit: 1,
|
||||||
|
out CarryRange next,
|
||||||
|
out ContradictionReason reason);
|
||||||
|
|
||||||
|
success.Should().BeFalse();
|
||||||
|
reason.Should().Be(ContradictionReason.ParityUnreachable);
|
||||||
|
next.Min.Should().Be(0);
|
||||||
|
next.Max.Should().Be(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TryPropagateCarry_ComputesNextCarryRange()
|
||||||
|
{
|
||||||
|
CarryRange carry = new(0, 1);
|
||||||
|
|
||||||
|
bool success = TryPropagateCarry(
|
||||||
|
carry,
|
||||||
|
forcedOnes: 1,
|
||||||
|
possibleOnes: 3,
|
||||||
|
requiredResultBit: 1,
|
||||||
|
out CarryRange next,
|
||||||
|
out ContradictionReason reason);
|
||||||
|
|
||||||
|
success.Should().BeTrue();
|
||||||
|
reason.Should().Be(ContradictionReason.None);
|
||||||
|
next.Min.Should().Be(0);
|
||||||
|
next.Max.Should().Be(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,
|
Percentile,
|
||||||
Additive,
|
Additive,
|
||||||
TopDown,
|
TopDown,
|
||||||
|
BitContradiction,
|
||||||
}
|
}
|
||||||
|
|
||||||
[EnumDependentTemplate(typeof(DivisorSet))]
|
[EnumDependentTemplate(typeof(DivisorSet))]
|
||||||
@@ -49,6 +50,9 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
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;
|
private ulong _currentPrime;
|
||||||
|
#if DivisorSet_BitContradiction
|
||||||
|
private static Persistence.BitContradictionStateRepository? _bitContradictionStateRepository;
|
||||||
|
#endif
|
||||||
#if DivisorSet_TopDown
|
#if DivisorSet_TopDown
|
||||||
private BigInteger _topDownCursor;
|
private BigInteger _topDownCursor;
|
||||||
#endif
|
#endif
|
||||||
@@ -328,6 +332,9 @@ private ulong _currentPrime;
|
|||||||
_groupsResumeK = _groupsLastSavedK + BigInteger.One;
|
_groupsResumeK = _groupsLastSavedK + BigInteger.One;
|
||||||
_currentPow2Phase = Pow2Phase.None;
|
_currentPow2Phase = Pow2Phase.None;
|
||||||
_preparedSpecialsInitialized = false;
|
_preparedSpecialsInitialized = false;
|
||||||
|
#endif
|
||||||
|
#if DivisorSet_BitContradiction
|
||||||
|
_bitContradictionStateRepository = EnvironmentConfiguration.BitContradictionStateRepository;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,6 +439,17 @@ private ulong _currentPrime;
|
|||||||
// 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_BitContradiction
|
||||||
|
bool compositeBitContradiction = CheckDivisorsBitContradiction(
|
||||||
|
prime,
|
||||||
|
primeDecimalMask,
|
||||||
|
allowedMax,
|
||||||
|
normalizedMinK,
|
||||||
|
step,
|
||||||
|
out processedAll,
|
||||||
|
out foundDivisor);
|
||||||
|
return compositeBitContradiction;
|
||||||
|
#endif
|
||||||
#if DivisorSet_TopDown
|
#if DivisorSet_TopDown
|
||||||
bool compositeTopDown = CheckDivisorsTopDown(
|
bool compositeTopDown = CheckDivisorsTopDown(
|
||||||
prime,
|
prime,
|
||||||
@@ -3309,6 +3327,176 @@ private ulong _currentPrime;
|
|||||||
}
|
}
|
||||||
#endif
|
#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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
private void MarkPow2MinusOneChecked()
|
private void MarkPow2MinusOneChecked()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTeste
|
|||||||
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForCpuOrder;
|
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForCpuOrder;
|
||||||
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForHybridOrder;
|
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForHybridOrder;
|
||||||
using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForGpuOrder;
|
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
|
#else
|
||||||
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForCpuOrder;
|
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForCpuOrder;
|
||||||
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForHybridOrder;
|
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForHybridOrder;
|
||||||
@@ -85,6 +89,8 @@ internal struct MersenneNumberDivisorByDivisorPrimeScanSessionWithTemplate
|
|||||||
extension = ".additive";
|
extension = ".additive";
|
||||||
#elif DivisorSet_TopDown
|
#elif DivisorSet_TopDown
|
||||||
extension = ".topdown";
|
extension = ".topdown";
|
||||||
|
#elif DivisorSet_BitContradiction
|
||||||
|
extension = ".bitcontradiction";
|
||||||
#endif
|
#endif
|
||||||
string stateFile = prime.ToString(CultureInfo.InvariantCulture) + extension;
|
string stateFile = prime.ToString(CultureInfo.InvariantCulture) + extension;
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ public static class EnvironmentConfiguration
|
|||||||
public static Persistence.KStateRepository? ByDivisorSpecialStateRepository;
|
public static Persistence.KStateRepository? ByDivisorSpecialStateRepository;
|
||||||
public static Persistence.KStateRepository? ByDivisorGroupsStateRepository;
|
public static Persistence.KStateRepository? ByDivisorGroupsStateRepository;
|
||||||
public static Persistence.Pow2Minus1StateRepository? ByDivisorPow2Minus1Repository;
|
public static Persistence.Pow2Minus1StateRepository? ByDivisorPow2Minus1Repository;
|
||||||
|
public static Persistence.BitContradictionStateRepository? BitContradictionStateRepository;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public static void Initialize()
|
public static void Initialize()
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ using MersenneNumberDivisorByDivisorCpuTesterWithPercentileGpuOrder = PerfectNum
|
|||||||
using MersenneNumberDivisorByDivisorCpuTesterWithAdditiveCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForCpuOrder;
|
using MersenneNumberDivisorByDivisorCpuTesterWithAdditiveCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForCpuOrder;
|
||||||
using MersenneNumberDivisorByDivisorCpuTesterWithAdditiveHybridOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForHybridOrder;
|
using MersenneNumberDivisorByDivisorCpuTesterWithAdditiveHybridOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForHybridOrder;
|
||||||
using MersenneNumberDivisorByDivisorCpuTesterWithAdditiveGpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForAdditiveDivisorSetForGpuOrder;
|
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;
|
namespace PerfectNumbers.Core;
|
||||||
|
|
||||||
@@ -54,6 +57,37 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
||||||
RunCpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, 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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public static void Run(
|
public static void Run(
|
||||||
List<ulong> candidates,
|
List<ulong> candidates,
|
||||||
@@ -178,6 +212,37 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
||||||
RunHybridOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, 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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public static void Run(
|
public static void Run(
|
||||||
List<ulong> candidates,
|
List<ulong> candidates,
|
||||||
@@ -302,6 +367,37 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
Action<ulong, bool, bool, bool, BigInteger> pr) =>
|
||||||
RunGpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, 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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public static void Run(
|
public static void Run(
|
||||||
List<ulong> candidates,
|
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(
|
private static void RunHybridOrderFilteredPow2(
|
||||||
List<ulong> filteredPrimes,
|
List<ulong> filteredPrimes,
|
||||||
in MersenneNumberDivisorByDivisorCpuTesterWithForPow2GroupsDivisorSetForHybridOrder prototype,
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Bit-Contradiction Divisor Scan
|
||||||
|
|
||||||
|
Plan and notes for the `DivisorSet.BitContradiction` / `--bydivisor-k-increment=bitcontradiction` mode.
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
- Divisor search via joint reconstruction of bits in \(q = 1 + 2pk\) and column-wise carries in \(q \cdot a = 2^p - 1\).
|
||||||
|
- Early local contradictions (parity reachability, carry budgets, length limits for \(a\)) without recursion.
|
||||||
|
- Report primes \(p\) whose search space exhausts with no successful branch first.
|
||||||
|
|
||||||
|
## Operational assumptions
|
||||||
|
- \(p\) is odd; we target prime divisors \(q \equiv 1 \pmod{2p}\).
|
||||||
|
- Work in \(k\)-space (since \(q = 2pk + 1\)).
|
||||||
|
- Iterative DFS/BFS (explicit stack/queue), no recursion.
|
||||||
|
|
||||||
|
## Implementation steps
|
||||||
|
- [x] Add `BitContradiction` mode to CLI (`--bydivisor-k-increment`) and state repositories/files.
|
||||||
|
- [x] Wire mode through CPU/Hybrid/GPU paths with `.bitcontradiction` state files.
|
||||||
|
- [x] Add core interval/propagation primitives (carry ranges, column bounds) with unit tests.
|
||||||
|
- [x] Wire BitContradiction mode into the scan path with a bit-level divisibility pre-check for small \(p\) before falling back to the standard divisor scan.
|
||||||
|
- [x] Replace the pre-check with a full forward-only column solver (no fixed column cap), reusable in tests.
|
||||||
|
- [x] Emit detailed log when the solver prunes a divisor with a concrete contradiction reason.
|
||||||
|
- [x] Unit tests on small \(p\) covering both success and no-divisor paths.
|
||||||
|
- [ ] (Optional) Integrate class-model stats to influence \(k_t\) branching order if helpful.
|
||||||
|
- [ ] Persist solver-specific state (FASTER) in `Checks\bitcontradiction.solver.faster` if we decide to retain per-prime solver telemetry.
|
||||||
|
|
||||||
|
## Current minimal version
|
||||||
|
- Mode is selectable and persists its own state in FASTER/`.bitcontradiction` files.
|
||||||
|
- Uses the stable by-divisor scan pipeline as a placeholder until the dedicated solver is completed.
|
||||||
|
|
||||||
|
## Next steps
|
||||||
|
- Implement interval-based propagation and iterative DFS on \(k\) bits (no recursion).
|
||||||
|
- Add tests for carry/length contradictions and for detecting a real divisor.
|
||||||
Reference in new issue
Block a user