BitContradiction solvers improvements to increase early exclusions.
This commit is contained in:
1 parent
c6dea4afcc
commit
d0dbb8f92a
3 files changed
+466
-139
No files matched your search
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.ByDivisor;
|
namespace PerfectNumbers.Core.ByDivisor;
|
||||||
@@ -14,9 +15,85 @@ internal static class BitContradictionSolverWithAMultiplier
|
|||||||
private const int TailCarryBatchColumns = 1024;
|
private const int TailCarryBatchColumns = 1024;
|
||||||
private const int TailCarryBatchRepeats = 1;
|
private const int TailCarryBatchRepeats = 1;
|
||||||
|
|
||||||
|
private const int ModLocalPrefilterMaxABits = 20;
|
||||||
|
private static readonly int[] ModLocalPrefilterModuli = { 65537, 65521 };
|
||||||
|
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static TopDownPruneFailure? _lastTopDownFailure;
|
private static TopDownPruneFailure? _lastTopDownFailure;
|
||||||
private static bool _debugEnabled = true;
|
private static bool _debugEnabled = true;
|
||||||
|
|
||||||
|
|
||||||
|
private const int TopDownCacheCapacity = 16;
|
||||||
|
|
||||||
|
private readonly struct TopDownCacheKey(long hash, int length, long maxAllowedA, long pLong)
|
||||||
|
{
|
||||||
|
public readonly long Hash = hash;
|
||||||
|
public readonly int Length = length;
|
||||||
|
public readonly long MaxAllowedA = maxAllowedA;
|
||||||
|
public readonly long PLong = pLong;
|
||||||
|
|
||||||
|
public bool Equals(TopDownCacheKey other) => Hash == other.Hash && Length == other.Length && MaxAllowedA == other.MaxAllowedA && PLong == other.PLong;
|
||||||
|
public override bool Equals(object? obj) => obj is TopDownCacheKey other && Equals(other);
|
||||||
|
public override int GetHashCode() => HashCode.Combine(Hash, Length, MaxAllowedA, PLong);
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly struct TopDownCacheValue(bool success, TopDownPruneFailure? failure)
|
||||||
|
{
|
||||||
|
public readonly bool Success = success;
|
||||||
|
public readonly TopDownPruneFailure? Failure = failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Dictionary<TopDownCacheKey, TopDownCacheValue>? _topDownCache;
|
||||||
|
[ThreadStatic]
|
||||||
|
private static List<TopDownCacheKey>? _topDownCacheOrder;
|
||||||
|
|
||||||
|
private static long ComputeQOffsetsHash(ReadOnlySpan<int> qOneOffsets)
|
||||||
|
{
|
||||||
|
const long fnvOffset = unchecked((long)1469598103934665603);
|
||||||
|
const long fnvPrime = 1099511628211;
|
||||||
|
long hash = fnvOffset;
|
||||||
|
for (int i = 0; i < qOneOffsets.Length; i++)
|
||||||
|
{
|
||||||
|
hash ^= qOneOffsets[i];
|
||||||
|
hash *= fnvPrime;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryGetTopDownCache(in TopDownCacheKey key, out TopDownCacheValue value)
|
||||||
|
{
|
||||||
|
if (_topDownCache == null)
|
||||||
|
{
|
||||||
|
value = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _topDownCache.TryGetValue(key, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void StoreTopDownCache(in TopDownCacheKey key, in TopDownCacheValue value)
|
||||||
|
{
|
||||||
|
_topDownCache ??= new Dictionary<TopDownCacheKey, TopDownCacheValue>();
|
||||||
|
_topDownCacheOrder ??= new List<TopDownCacheKey>(TopDownCacheCapacity + 1);
|
||||||
|
|
||||||
|
if (_topDownCache.ContainsKey(key))
|
||||||
|
{
|
||||||
|
_topDownCache[key] = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_topDownCacheOrder.Count >= TopDownCacheCapacity)
|
||||||
|
{
|
||||||
|
var evict = _topDownCacheOrder[0];
|
||||||
|
_topDownCacheOrder.RemoveAt(0);
|
||||||
|
_topDownCache.Remove(evict);
|
||||||
|
}
|
||||||
|
|
||||||
|
_topDownCache[key] = value;
|
||||||
|
_topDownCacheOrder.Add(key);
|
||||||
|
}
|
||||||
private static int _parityZeroLogCount;
|
private static int _parityZeroLogCount;
|
||||||
|
|
||||||
internal static TopDownPruneFailure? LastTopDownFailure => _lastTopDownFailure;
|
internal static TopDownPruneFailure? LastTopDownFailure => _lastTopDownFailure;
|
||||||
@@ -80,6 +157,109 @@ internal static class BitContradictionSolverWithAMultiplier
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TryModularPrefilter(
|
||||||
|
ReadOnlySpan<int> qOneOffsets,
|
||||||
|
ulong p,
|
||||||
|
long maxAllowedA)
|
||||||
|
{
|
||||||
|
if (maxAllowedA < 0 || maxAllowedA > ModLocalPrefilterMaxABits)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bitCount = (int)(maxAllowedA + 1);
|
||||||
|
if (bitCount <= 0 || bitCount >= 62)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
long maxAValue = (1L << bitCount) - 1L;
|
||||||
|
for (int i = 0; i < ModLocalPrefilterModuli.Length; i++)
|
||||||
|
{
|
||||||
|
int mod = ModLocalPrefilterModuli[i];
|
||||||
|
int qMod = ComputeQMod(qOneOffsets, mod);
|
||||||
|
int target = ModPow2Minus1(p, mod);
|
||||||
|
if (qMod == 0)
|
||||||
|
{
|
||||||
|
if (target != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int inverse = ModPow(qMod, mod - 2, mod);
|
||||||
|
long x = (long)((long)target * inverse % mod);
|
||||||
|
if (x > maxAValue)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int ComputeQMod(ReadOnlySpan<int> qOneOffsets, int mod)
|
||||||
|
{
|
||||||
|
long sum = 0;
|
||||||
|
for (int i = 0; i < qOneOffsets.Length; i++)
|
||||||
|
{
|
||||||
|
sum += PowMod2((ulong)qOneOffsets[i], mod);
|
||||||
|
if (sum >= mod)
|
||||||
|
{
|
||||||
|
sum %= mod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)(sum % mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int ModPow2Minus1(ulong p, int mod)
|
||||||
|
{
|
||||||
|
int pow = PowMod2(p, mod);
|
||||||
|
int result = pow - 1;
|
||||||
|
return result < 0 ? result + mod : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int PowMod2(ulong exp, int mod)
|
||||||
|
{
|
||||||
|
long result = 1 % mod;
|
||||||
|
long baseVal = 2 % mod;
|
||||||
|
ulong value = exp;
|
||||||
|
while (value > 0)
|
||||||
|
{
|
||||||
|
if ((value & 1) != 0)
|
||||||
|
{
|
||||||
|
result = (result * baseVal) % mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
baseVal = (baseVal * baseVal) % mod;
|
||||||
|
value >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int ModPow(int value, int exp, int mod)
|
||||||
|
{
|
||||||
|
long result = 1 % mod;
|
||||||
|
long baseVal = value % mod;
|
||||||
|
int e = exp;
|
||||||
|
while (e > 0)
|
||||||
|
{
|
||||||
|
if ((e & 1) != 0)
|
||||||
|
{
|
||||||
|
result = (result * baseVal) % mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
baseVal = (baseVal * baseVal) % mod;
|
||||||
|
e >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)result;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Full column-wise propagation for a given set of q one-bit offsets (LSB=0) and exponent p.
|
/// 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).
|
/// Returns true if the solver could decide locally (divides or contradiction).
|
||||||
@@ -145,16 +325,44 @@ internal static class BitContradictionSolverWithAMultiplier
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryTopDownCarryPrune(qOneOffsets, qOneOffsetsLength, pLong, maxAllowedA, out _lastTopDownFailure))
|
if (!TryModularPrefilter(qOneOffsets, p, maxAllowedA))
|
||||||
{
|
{
|
||||||
reason = ContradictionReason.ParityUnreachable;
|
reason = ContradictionReason.ParityUnreachable;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long qHash = ComputeQOffsetsHash(qOneOffsets);
|
||||||
|
var cacheKey = new TopDownCacheKey(qHash, qOneOffsetsLength, maxAllowedA, pLong);
|
||||||
|
if (TryGetTopDownCache(in cacheKey, out var cached))
|
||||||
|
{
|
||||||
|
_lastTopDownFailure = cached.Failure;
|
||||||
|
if (!cached.Success)
|
||||||
|
{
|
||||||
|
reason = ContradictionReason.ParityUnreachable;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!TryTopDownCarryPrune(qOneOffsets, qOneOffsetsLength, pLong, maxAllowedA, out _lastTopDownFailure))
|
||||||
|
{
|
||||||
|
StoreTopDownCache(in cacheKey, new TopDownCacheValue(false, _lastTopDownFailure));
|
||||||
|
reason = ContradictionReason.ParityUnreachable;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
StoreTopDownCache(in cacheKey, new TopDownCacheValue(true, null));
|
||||||
|
}
|
||||||
|
|
||||||
long lowColumn = 0;
|
long lowColumn = 0;
|
||||||
long highColumn = pLong - 1;
|
long highColumn = pLong - 1;
|
||||||
CarryRange carryLow = CarryRange.Zero;
|
CarryRange carryLow = CarryRange.Zero;
|
||||||
CarryRange carryHigh = CarryRange.Zero;
|
CarryRange carryHigh = CarryRange.Zero;
|
||||||
|
int bottomWindowStart = 0;
|
||||||
|
int bottomWindowEnd = 0;
|
||||||
|
int topWindowStart = qOneOffsetsLength;
|
||||||
|
int topWindowEnd = qOneOffsetsLength;
|
||||||
|
|
||||||
bool processTop = true;
|
bool processTop = true;
|
||||||
|
|
||||||
while (lowColumn <= highColumn)
|
while (lowColumn <= highColumn)
|
||||||
@@ -164,7 +372,7 @@ internal static class BitContradictionSolverWithAMultiplier
|
|||||||
|
|
||||||
if (processTop)
|
if (processTop)
|
||||||
{
|
{
|
||||||
if (!TryProcessTopDownBlock(qOneOffsets, qOneOffsetsLength, maxAllowedA, highColumn, blockSize, carryHigh, out carryHigh, out _lastTopDownFailure))
|
if (!TryProcessTopDownBlock(qOneOffsets, qOneOffsetsLength, maxAllowedA, highColumn, blockSize, carryHigh, ref topWindowStart, ref topWindowEnd, out carryHigh, out _lastTopDownFailure))
|
||||||
{
|
{
|
||||||
reason = ContradictionReason.ParityUnreachable;
|
reason = ContradictionReason.ParityUnreachable;
|
||||||
return true;
|
return true;
|
||||||
@@ -174,7 +382,7 @@ internal static class BitContradictionSolverWithAMultiplier
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!TryProcessBottomUpBlock(qOneOffsets, maxAllowedA, lowColumn, blockSize, ref carryLow, ref maxKnownA, ref segmentBase, segmentState0, segmentState1, out reason))
|
if (!TryProcessBottomUpBlock(qOneOffsets, maxAllowedA, lowColumn, blockSize, ref carryLow, ref maxKnownA, ref segmentBase, ref bottomWindowStart, ref bottomWindowEnd, segmentState0, segmentState1, out reason))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -237,6 +445,8 @@ internal static class BitContradictionSolverWithAMultiplier
|
|||||||
ref CarryRange carry,
|
ref CarryRange carry,
|
||||||
ref long maxKnownA,
|
ref long maxKnownA,
|
||||||
ref long segmentBase,
|
ref long segmentBase,
|
||||||
|
ref int windowStartIdx,
|
||||||
|
ref int windowEndIdx,
|
||||||
sbyte[] segmentState0,
|
sbyte[] segmentState0,
|
||||||
sbyte[] segmentState1,
|
sbyte[] segmentState1,
|
||||||
out ContradictionReason reason)
|
out ContradictionReason reason)
|
||||||
@@ -244,8 +454,53 @@ internal static class BitContradictionSolverWithAMultiplier
|
|||||||
long endColumn = startColumn + columnCount;
|
long endColumn = startColumn + columnCount;
|
||||||
int qOneOffsetsLength = qOneOffsets.Length;
|
int qOneOffsetsLength = qOneOffsets.Length;
|
||||||
int windowSize = segmentState0.Length;
|
int windowSize = segmentState0.Length;
|
||||||
for (long column = startColumn; column < endColumn; column++)
|
int maxOffset = qOneOffsets[qOneOffsetsLength - 1];
|
||||||
|
long stableUnknown = qOneOffsetsLength;
|
||||||
|
long column = startColumn;
|
||||||
|
while (column < endColumn)
|
||||||
{
|
{
|
||||||
|
if (column >= maxOffset && column <= maxAllowedA && maxKnownA < column - maxOffset)
|
||||||
|
{
|
||||||
|
long batchEnd = endColumn - 1;
|
||||||
|
if (batchEnd > maxAllowedA)
|
||||||
|
{
|
||||||
|
batchEnd = maxAllowedA;
|
||||||
|
}
|
||||||
|
|
||||||
|
long remaining = batchEnd - column + 1;
|
||||||
|
if (remaining > 0)
|
||||||
|
{
|
||||||
|
windowStartIdx = 0;
|
||||||
|
windowEndIdx = qOneOffsetsLength;
|
||||||
|
while (remaining > 0)
|
||||||
|
{
|
||||||
|
int step = remaining > TailCarryBatchColumns ? TailCarryBatchColumns : (int)remaining;
|
||||||
|
for (int i = 0; i < step; i++)
|
||||||
|
{
|
||||||
|
if (!TryPropagateCarry(carry, 0, stableUnknown, 1, out var next, out var propagateReason))
|
||||||
|
{
|
||||||
|
reason = propagateReason;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
carry = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
column += step;
|
||||||
|
remaining -= step;
|
||||||
|
}
|
||||||
|
|
||||||
|
long batchBase = (column / windowSize) * windowSize;
|
||||||
|
if (batchBase != segmentBase)
|
||||||
|
{
|
||||||
|
Array.Clear(segmentState0);
|
||||||
|
Array.Clear(segmentState1);
|
||||||
|
segmentBase = batchBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
long forced = 0L;
|
long forced = 0L;
|
||||||
long unknown = 0L;
|
long unknown = 0L;
|
||||||
long chosenIndex = -1L;
|
long chosenIndex = -1L;
|
||||||
@@ -266,7 +521,18 @@ internal static class BitContradictionSolverWithAMultiplier
|
|||||||
segmentBase = newBase;
|
segmentBase = newBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int offsetIndex = 0; offsetIndex < qOneOffsetsLength; offsetIndex++)
|
while (windowEndIdx < qOneOffsetsLength && qOneOffsets[windowEndIdx] <= column)
|
||||||
|
{
|
||||||
|
windowEndIdx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
long lowerBound = column - maxAllowedA;
|
||||||
|
while (windowStartIdx < windowEndIdx && qOneOffsets[windowStartIdx] < lowerBound)
|
||||||
|
{
|
||||||
|
windowStartIdx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int offsetIndex = windowStartIdx; offsetIndex < windowEndIdx; offsetIndex++)
|
||||||
{
|
{
|
||||||
int offset = qOneOffsets[offsetIndex];
|
int offset = qOneOffsets[offsetIndex];
|
||||||
long aIndex = column - offset;
|
long aIndex = column - offset;
|
||||||
@@ -418,6 +684,7 @@ internal static class BitContradictionSolverWithAMultiplier
|
|||||||
carry = next;
|
carry = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
column++;
|
||||||
}
|
}
|
||||||
|
|
||||||
reason = ContradictionReason.None;
|
reason = ContradictionReason.None;
|
||||||
@@ -431,6 +698,8 @@ internal static class BitContradictionSolverWithAMultiplier
|
|||||||
long startHighColumn,
|
long startHighColumn,
|
||||||
int columnCount,
|
int columnCount,
|
||||||
CarryRange carryOut,
|
CarryRange carryOut,
|
||||||
|
ref int windowStart,
|
||||||
|
ref int windowEnd,
|
||||||
out CarryRange nextCarryOut,
|
out CarryRange nextCarryOut,
|
||||||
out TopDownPruneFailure? failure)
|
out TopDownPruneFailure? failure)
|
||||||
{
|
{
|
||||||
@@ -438,8 +707,6 @@ internal static class BitContradictionSolverWithAMultiplier
|
|||||||
long carryOutMin = carryOut.Min,
|
long carryOutMin = carryOut.Min,
|
||||||
carryOutMax = carryOut.Max,
|
carryOutMax = carryOut.Max,
|
||||||
value;
|
value;
|
||||||
int windowStart = qOneOffsetsLength;
|
|
||||||
int windowEnd = qOneOffsetsLength;
|
|
||||||
long endColumn = startHighColumn - columnCount + 1;
|
long endColumn = startHighColumn - columnCount + 1;
|
||||||
|
|
||||||
long column = startHighColumn;
|
long column = startHighColumn;
|
||||||
@@ -560,11 +827,11 @@ internal static class BitContradictionSolverWithAMultiplier
|
|||||||
out TopDownPruneFailure? failure)
|
out TopDownPruneFailure? failure)
|
||||||
{
|
{
|
||||||
failure = null;
|
failure = null;
|
||||||
|
int windowStart = qOneOffsetsLength;
|
||||||
|
int windowEnd = qOneOffsetsLength;
|
||||||
long carryOutMin = 0,
|
long carryOutMin = 0,
|
||||||
carryOutMax = 0,
|
carryOutMax = 0,
|
||||||
value;
|
value;
|
||||||
int windowStart = qOneOffsetsLength;
|
|
||||||
int windowEnd = qOneOffsetsLength;
|
|
||||||
|
|
||||||
long column = pLong - 1;
|
long column = pLong - 1;
|
||||||
while (column >= 0)
|
while (column >= 0)
|
||||||
|
|||||||
@@ -52,6 +52,79 @@ internal static class BitContradictionSolverWithQMultiplier
|
|||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static bool _debugEnabled;
|
private static bool _debugEnabled;
|
||||||
|
|
||||||
|
private const int TopDownCacheCapacity = 16;
|
||||||
|
|
||||||
|
private readonly struct TopDownCacheKey(long hash, int length, long maxAllowedA, long pLong)
|
||||||
|
{
|
||||||
|
public readonly long Hash = hash;
|
||||||
|
public readonly int Length = length;
|
||||||
|
public readonly long MaxAllowedA = maxAllowedA;
|
||||||
|
public readonly long PLong = pLong;
|
||||||
|
|
||||||
|
public bool Equals(TopDownCacheKey other) => Hash == other.Hash && Length == other.Length && MaxAllowedA == other.MaxAllowedA && PLong == other.PLong;
|
||||||
|
public override bool Equals(object? obj) => obj is TopDownCacheKey other && Equals(other);
|
||||||
|
public override int GetHashCode() => HashCode.Combine(Hash, Length, MaxAllowedA, PLong);
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly struct TopDownCacheValue(bool success, TopDownPruneFailure? failure)
|
||||||
|
{
|
||||||
|
public readonly bool Success = success;
|
||||||
|
public readonly TopDownPruneFailure? Failure = failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static Dictionary<TopDownCacheKey, TopDownCacheValue>? _topDownCache;
|
||||||
|
[ThreadStatic]
|
||||||
|
private static List<TopDownCacheKey>? _topDownCacheOrder;
|
||||||
|
|
||||||
|
private static long ComputeQOffsetsHash(ReadOnlySpan<int> qOneOffsets)
|
||||||
|
{
|
||||||
|
const long fnvOffset = unchecked((long)1469598103934665603);
|
||||||
|
const long fnvPrime = 1099511628211;
|
||||||
|
long hash = fnvOffset;
|
||||||
|
for (int i = 0; i < qOneOffsets.Length; i++)
|
||||||
|
{
|
||||||
|
hash ^= qOneOffsets[i];
|
||||||
|
hash *= fnvPrime;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryGetTopDownCache(in TopDownCacheKey key, out TopDownCacheValue value)
|
||||||
|
{
|
||||||
|
if (_topDownCache == null)
|
||||||
|
{
|
||||||
|
value = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _topDownCache.TryGetValue(key, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void StoreTopDownCache(in TopDownCacheKey key, in TopDownCacheValue value)
|
||||||
|
{
|
||||||
|
_topDownCache ??= new Dictionary<TopDownCacheKey, TopDownCacheValue>();
|
||||||
|
_topDownCacheOrder ??= new List<TopDownCacheKey>(TopDownCacheCapacity + 1);
|
||||||
|
|
||||||
|
if (_topDownCache.ContainsKey(key))
|
||||||
|
{
|
||||||
|
_topDownCache[key] = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_topDownCacheOrder.Count >= TopDownCacheCapacity)
|
||||||
|
{
|
||||||
|
var evict = _topDownCacheOrder[0];
|
||||||
|
_topDownCacheOrder.RemoveAt(0);
|
||||||
|
_topDownCache.Remove(evict);
|
||||||
|
}
|
||||||
|
|
||||||
|
_topDownCache[key] = value;
|
||||||
|
_topDownCacheOrder.Add(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
internal static TopDownPruneFailure? LastTopDownFailure => _lastTopDownFailure;
|
internal static TopDownPruneFailure? LastTopDownFailure => _lastTopDownFailure;
|
||||||
internal static BottomUpFailure? LastBottomUpFailure => _lastBottomUpFailure;
|
internal static BottomUpFailure? LastBottomUpFailure => _lastBottomUpFailure;
|
||||||
|
|
||||||
@@ -188,17 +261,40 @@ internal static class BitContradictionSolverWithQMultiplier
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryTopDownCarryPrune(qOneOffsets, qOneOffsetsLength, pLong, maxAllowedA, out _lastTopDownFailure))
|
long qHash = ComputeQOffsetsHash(qOneOffsets);
|
||||||
|
var cacheKey = new TopDownCacheKey(qHash, qOneOffsetsLength, maxAllowedA, pLong);
|
||||||
|
if (TryGetTopDownCache(in cacheKey, out var cached))
|
||||||
|
{
|
||||||
|
_lastTopDownFailure = cached.Failure;
|
||||||
|
if (!cached.Success)
|
||||||
{
|
{
|
||||||
reason = ContradictionReason.ParityUnreachable;
|
reason = ContradictionReason.ParityUnreachable;
|
||||||
Console.WriteLine($"[bit-contradiction] parity unreachable in top-down prune (failure={_lastTopDownFailure.HasValue})");
|
Console.WriteLine($"[bit-contradiction] parity unreachable in top-down prune (failure={_lastTopDownFailure.HasValue})");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!TryTopDownCarryPrune(qOneOffsets, qOneOffsetsLength, pLong, maxAllowedA, out _lastTopDownFailure))
|
||||||
|
{
|
||||||
|
StoreTopDownCache(in cacheKey, new TopDownCacheValue(false, _lastTopDownFailure));
|
||||||
|
reason = ContradictionReason.ParityUnreachable;
|
||||||
|
Console.WriteLine($"[bit-contradiction] parity unreachable in top-down prune (failure={_lastTopDownFailure.HasValue})");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
StoreTopDownCache(in cacheKey, new TopDownCacheValue(true, null));
|
||||||
|
}
|
||||||
|
|
||||||
long lowColumn = 0;
|
long lowColumn = 0;
|
||||||
long highColumn = pLong - 1;
|
long highColumn = pLong - 1;
|
||||||
CarryRange carryLow = CarryRange.Zero;
|
CarryRange carryLow = CarryRange.Zero;
|
||||||
CarryRange carryHigh = CarryRange.Zero;
|
CarryRange carryHigh = CarryRange.Zero;
|
||||||
|
int bottomWindowStart = 0;
|
||||||
|
int bottomWindowEnd = 0;
|
||||||
|
int topWindowStart = qOneOffsetsLength;
|
||||||
|
int topWindowEnd = qOneOffsetsLength;
|
||||||
|
|
||||||
bool processTop = true;
|
bool processTop = true;
|
||||||
|
|
||||||
while (lowColumn <= highColumn)
|
while (lowColumn <= highColumn)
|
||||||
@@ -208,7 +304,7 @@ internal static class BitContradictionSolverWithQMultiplier
|
|||||||
|
|
||||||
if (processTop)
|
if (processTop)
|
||||||
{
|
{
|
||||||
if (!TryProcessTopDownBlock(qOneOffsets, qOneOffsetsLength, maxAllowedA, highColumn, blockSize, carryHigh, out carryHigh, out _lastTopDownFailure))
|
if (!TryProcessTopDownBlock(qOneOffsets, qOneOffsetsLength, maxAllowedA, highColumn, blockSize, carryHigh, ref topWindowStart, ref topWindowEnd, out carryHigh, out _lastTopDownFailure))
|
||||||
{
|
{
|
||||||
reason = ContradictionReason.ParityUnreachable;
|
reason = ContradictionReason.ParityUnreachable;
|
||||||
return true;
|
return true;
|
||||||
@@ -218,7 +314,7 @@ internal static class BitContradictionSolverWithQMultiplier
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!TryProcessBottomUpBlock(qOneOffsets, maxAllowedA, lowColumn, blockSize, ref carryLow, ref segmentBase, segmentState0, segmentState1, out reason))
|
if (!TryProcessBottomUpBlock(qOneOffsets, maxAllowedA, lowColumn, blockSize, ref carryLow, ref segmentBase, ref bottomWindowStart, ref bottomWindowEnd, segmentState0, segmentState1, out reason))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -281,6 +377,8 @@ internal static class BitContradictionSolverWithQMultiplier
|
|||||||
int columnCount,
|
int columnCount,
|
||||||
ref CarryRange carry,
|
ref CarryRange carry,
|
||||||
ref long segmentBase,
|
ref long segmentBase,
|
||||||
|
ref int windowStartIdx,
|
||||||
|
ref int windowEndIdx,
|
||||||
sbyte[] segmentState0,
|
sbyte[] segmentState0,
|
||||||
sbyte[] segmentState1,
|
sbyte[] segmentState1,
|
||||||
out ContradictionReason reason)
|
out ContradictionReason reason)
|
||||||
@@ -310,7 +408,18 @@ internal static class BitContradictionSolverWithQMultiplier
|
|||||||
segmentBase = newBase;
|
segmentBase = newBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int offsetIndex = 0; offsetIndex < qOneOffsetsLength; offsetIndex++)
|
while (windowEndIdx < qOneOffsetsLength && qOneOffsets[windowEndIdx] <= column)
|
||||||
|
{
|
||||||
|
windowEndIdx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
long lowerBound = column - maxAllowedA;
|
||||||
|
while (windowStartIdx < windowEndIdx && qOneOffsets[windowStartIdx] < lowerBound)
|
||||||
|
{
|
||||||
|
windowStartIdx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int offsetIndex = windowStartIdx; offsetIndex < windowEndIdx; offsetIndex++)
|
||||||
{
|
{
|
||||||
int offset = qOneOffsets[offsetIndex];
|
int offset = qOneOffsets[offsetIndex];
|
||||||
long aIndex = column - offset;
|
long aIndex = column - offset;
|
||||||
@@ -480,6 +589,8 @@ internal static class BitContradictionSolverWithQMultiplier
|
|||||||
long startHighColumn,
|
long startHighColumn,
|
||||||
int columnCount,
|
int columnCount,
|
||||||
CarryRange carryOut,
|
CarryRange carryOut,
|
||||||
|
ref int windowStart,
|
||||||
|
ref int windowEnd,
|
||||||
out CarryRange nextCarryOut,
|
out CarryRange nextCarryOut,
|
||||||
out TopDownPruneFailure? failure)
|
out TopDownPruneFailure? failure)
|
||||||
{
|
{
|
||||||
@@ -487,8 +598,6 @@ internal static class BitContradictionSolverWithQMultiplier
|
|||||||
long carryOutMin = carryOut.Min,
|
long carryOutMin = carryOut.Min,
|
||||||
carryOutMax = carryOut.Max,
|
carryOutMax = carryOut.Max,
|
||||||
value;
|
value;
|
||||||
int windowStart = qOneOffsetsLength;
|
|
||||||
int windowEnd = qOneOffsetsLength;
|
|
||||||
long endColumn = startHighColumn - columnCount + 1;
|
long endColumn = startHighColumn - columnCount + 1;
|
||||||
|
|
||||||
long column = startHighColumn;
|
long column = startHighColumn;
|
||||||
@@ -612,6 +721,7 @@ internal static class BitContradictionSolverWithQMultiplier
|
|||||||
long carryOutMin = 0,
|
long carryOutMin = 0,
|
||||||
carryOutMax = 0,
|
carryOutMax = 0,
|
||||||
value;
|
value;
|
||||||
|
|
||||||
int windowStart = qOneOffsetsLength;
|
int windowStart = qOneOffsetsLength;
|
||||||
int windowEnd = qOneOffsetsLength;
|
int windowEnd = qOneOffsetsLength;
|
||||||
|
|
||||||
|
|||||||
@@ -1398,7 +1398,9 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private bool TryExactBitTreeCheck(ulong exponent, BigInteger divisor, out bool divides)
|
private bool TryExactBitTreeCheck(ulong exponent, BigInteger divisor, out bool divides)
|
||||||
{
|
{
|
||||||
|
#if DETAILED_LOG
|
||||||
Stopwatch sw = Stopwatch.StartNew();
|
Stopwatch sw = Stopwatch.StartNew();
|
||||||
|
#endif
|
||||||
int qBitLength = GetBitLengthPortable(divisor);
|
int qBitLength = GetBitLengthPortable(divisor);
|
||||||
int aBitLength = GetBitLengthPortable(exponent);
|
int aBitLength = GetBitLengthPortable(exponent);
|
||||||
|
|
||||||
@@ -1504,13 +1506,17 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if DETAILED_LOG
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
|
#endif
|
||||||
|
|
||||||
ArrayPool<byte>.Shared.Return(aWindowArray);
|
ArrayPool<byte>.Shared.Return(aWindowArray);
|
||||||
|
|
||||||
divides = carry == 0;
|
divides = carry == 0;
|
||||||
|
#if DETAILED_LOG
|
||||||
var elapsed = sw.Elapsed;
|
var elapsed = sw.Elapsed;
|
||||||
Console.WriteLine($"[bittree ({elapsed})] Prime={exponent}, Divisor={divisor} Decided=True Divides={divides}");
|
Console.WriteLine($"[bittree ({elapsed})] Prime={exponent}, Divisor={divisor} Decided=True Divides={divides}");
|
||||||
|
#endif
|
||||||
if (!divides)
|
if (!divides)
|
||||||
{
|
{
|
||||||
string message = $"[bittree] p={exponent} ruled out divisor={divisor} (parity/carry exhaustion)";
|
string message = $"[bittree] p={exponent} ruled out divisor={divisor} (parity/carry exhaustion)";
|
||||||
@@ -4326,6 +4332,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
int kRem8 = (int)(currentK % 8);
|
int kRem8 = (int)(currentK % 8);
|
||||||
|
|
||||||
BigInteger divisor = (step * currentK) + BigInteger.One;
|
BigInteger divisor = (step * currentK) + BigInteger.One;
|
||||||
|
Stopwatch sw = new Stopwatch();
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
int remainder8 = (stepMod8 * kRem8 + 1) & 7;
|
int remainder8 = (stepMod8 * kRem8 + 1) & 7;
|
||||||
@@ -4342,80 +4349,14 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
goto MOVE_NEXT;
|
goto MOVE_NEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool divides;
|
||||||
|
// divides = BigInteger.ModPow(BigIntegerNumbers.Two, prime, divisor) == 1;
|
||||||
|
|
||||||
ulong effectiveExponent = 0UL;
|
ulong effectiveExponent = 0UL;
|
||||||
// if (divisor <= ulong.MaxValue)
|
sw.Reset();
|
||||||
// {
|
sw.Start();
|
||||||
// ulong divisor64 = (ulong)divisor;
|
bool decided = TryExactBitContradictionCheck(prime, divisor, effectiveExponent, out divides);
|
||||||
// MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor64);
|
sw.Stop();
|
||||||
// #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;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (divisorCycle > 0UL && (prime % divisorCycle) != 0UL)
|
|
||||||
// {
|
|
||||||
// if (hasLimit && currentK >= maxK)
|
|
||||||
// {
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// currentK += BigInteger.One;
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (divisorCycle > 0UL)
|
|
||||||
// {
|
|
||||||
// effectiveExponent = divisorCycle;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
bool decided = TryExactBitContradictionCheck(prime, divisor, effectiveExponent, out bool divides);
|
|
||||||
if (!decided)
|
if (!decided)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"BitContradiction check did not decide for p={prime}, divisor={divisor} (k={currentK}).");
|
throw new InvalidOperationException($"BitContradiction check did not decide for p={prime}, divisor={divisor} (k={currentK}).");
|
||||||
@@ -4426,6 +4367,9 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
foundDivisor = divisor;
|
foundDivisor = divisor;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
Console.WriteLine($"Prime {prime} ({sw.Elapsed}), excluded {divisor}");
|
||||||
|
}
|
||||||
|
|
||||||
RecordState(currentK);
|
RecordState(currentK);
|
||||||
MOVE_NEXT:
|
MOVE_NEXT:
|
||||||
@@ -4441,63 +4385,65 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool fits64 = minK <= ulong.MaxValue;
|
// bool fits64 = minK <= ulong.MaxValue;
|
||||||
bool limitFits64 = allowedMax.IsZero || allowedMax <= ulong.MaxValue;
|
// bool limitFits64 = allowedMax.IsZero || allowedMax <= ulong.MaxValue;
|
||||||
|
|
||||||
if (fits64 && limitFits64)
|
// if (fits64 && limitFits64)
|
||||||
{
|
// {
|
||||||
ulong allowedMax64 = allowedMax.IsZero ? ulong.MaxValue : (ulong)allowedMax;
|
// ulong allowedMax64 = allowedMax.IsZero ? ulong.MaxValue : (ulong)allowedMax;
|
||||||
return CheckDivisors64Bit(
|
// return CheckDivisors64Bit(
|
||||||
prime,
|
// prime,
|
||||||
primeDecimalMask,
|
// primeDecimalMask,
|
||||||
allowedMax64,
|
// allowedMax64,
|
||||||
(ulong)minK,
|
// (ulong)minK,
|
||||||
out processedAll,
|
// out processedAll,
|
||||||
out ulong found64);
|
// out ulong found64);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (fits64 && allowedMax > ulong.MaxValue)
|
// if (fits64 && allowedMax > ulong.MaxValue)
|
||||||
{
|
// {
|
||||||
ulong allowedMax64 = ulong.MaxValue;
|
// ulong allowedMax64 = ulong.MaxValue;
|
||||||
bool composite64 = CheckDivisors64Bit(
|
// bool composite64 = CheckDivisors64Bit(
|
||||||
prime,
|
// prime,
|
||||||
primeDecimalMask,
|
// primeDecimalMask,
|
||||||
allowedMax64,
|
// allowedMax64,
|
||||||
(ulong)minK,
|
// (ulong)minK,
|
||||||
out processedAll,
|
// out processedAll,
|
||||||
out ulong found64);
|
// out ulong found64);
|
||||||
if (composite64)
|
// if (composite64)
|
||||||
{
|
// {
|
||||||
foundDivisor = found64;
|
// foundDivisor = found64;
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
|
|
||||||
BigInteger iterations = ((BigInteger)(allowedMax64 - (((ulong)prime << 1) * (ulong)minK) - 1UL) / (ulong)step) + BigInteger.One;
|
// BigInteger iterations = ((BigInteger)(allowedMax64 - (((ulong)prime << 1) * (ulong)minK) - 1UL) / (ulong)step) + BigInteger.One;
|
||||||
BigInteger nextK = minK + iterations;
|
// BigInteger nextK = minK + iterations;
|
||||||
return CheckDivisorsLarge(
|
// return CheckDivisorsLarge(
|
||||||
prime,
|
// prime,
|
||||||
primeDecimalMask,
|
// primeDecimalMask,
|
||||||
allowedMax,
|
// allowedMax,
|
||||||
nextK,
|
// nextK,
|
||||||
step,
|
// step,
|
||||||
out processedAll,
|
// out processedAll,
|
||||||
out foundDivisor);
|
// out foundDivisor);
|
||||||
}
|
// }
|
||||||
|
|
||||||
return CheckDivisorsLarge(
|
// return CheckDivisorsLarge(
|
||||||
prime,
|
// prime,
|
||||||
primeDecimalMask,
|
// primeDecimalMask,
|
||||||
allowedMax,
|
// allowedMax,
|
||||||
minK,
|
// minK,
|
||||||
step,
|
// step,
|
||||||
out processedAll,
|
// out processedAll,
|
||||||
out foundDivisor);
|
// out foundDivisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static bool TryExactBitContradictionCheck(ulong prime, BigInteger divisor, ulong effectiveExponent, out bool divides)
|
private static bool TryExactBitContradictionCheck(ulong prime, BigInteger divisor, ulong effectiveExponent, out bool divides)
|
||||||
{
|
{
|
||||||
|
#if DETAILED_LOG
|
||||||
Stopwatch sw = Stopwatch.StartNew();
|
Stopwatch sw = Stopwatch.StartNew();
|
||||||
|
#endif
|
||||||
divides = false;
|
divides = false;
|
||||||
// const ulong DebugTopDownDivisor = 1209708008767UL;
|
// const ulong DebugTopDownDivisor = 1209708008767UL;
|
||||||
// const ulong DebugTopDownDivisor = 1007991562955719UL;
|
// const ulong DebugTopDownDivisor = 1007991562955719UL;
|
||||||
@@ -4551,7 +4497,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
ReadOnlySpan<int> oneOffsetsSlice = oneOffsets[..oneCount];
|
ReadOnlySpan<int> oneOffsetsSlice = oneOffsets[..oneCount];
|
||||||
ulong exponent = effectiveExponent > 0UL ? effectiveExponent : prime;
|
ulong exponent = effectiveExponent > 0UL ? effectiveExponent : prime;
|
||||||
// BitContradictionSolverWithQMultiplier.SetDebugEnabled(true);
|
// BitContradictionSolverWithQMultiplier.SetDebugEnabled(true);
|
||||||
bool decided = BitContradictionSolverWithQMultiplier.TryCheckDivisibilityFromOneOffsets(oneOffsetsSlice, exponent, out divides, out var reason);
|
bool decided = BitContradictionSolverWithAMultiplier.TryCheckDivisibilityFromOneOffsets(oneOffsetsSlice, exponent, out divides, out var reason);
|
||||||
// BitContradictionSolverWithQMultiplier.SetDebugEnabled(false);
|
// BitContradictionSolverWithQMultiplier.SetDebugEnabled(false);
|
||||||
// bool decided = BitContradictionSolverBaseline.TryCheckDivisibilityFromOneOffsets(oneOffsetsSlice, exponent, out divides, out var reason);
|
// bool decided = BitContradictionSolverBaseline.TryCheckDivisibilityFromOneOffsets(oneOffsetsSlice, exponent, out divides, out var reason);
|
||||||
if (decided && divides)
|
if (decided && divides)
|
||||||
@@ -4566,9 +4512,12 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DETAILED_LOG
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
|
#endif
|
||||||
if (decided && !divides)
|
if (decided && !divides)
|
||||||
{
|
{
|
||||||
|
#if DETAILED_LOG
|
||||||
if (reason == ContradictionReason.ParityUnreachable)
|
if (reason == ContradictionReason.ParityUnreachable)
|
||||||
{
|
{
|
||||||
var failure = BitContradictionSolverWithQMultiplier.LastTopDownFailure;
|
var failure = BitContradictionSolverWithQMultiplier.LastTopDownFailure;
|
||||||
@@ -4585,13 +4534,14 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
Console.WriteLine($"[bit-contradiction] bottom-up failed at column={info.Column} carry=[{info.CarryMin},{info.CarryMax}] forced={info.Forced} unknown={info.Unknown}");
|
Console.WriteLine($"[bit-contradiction] bottom-up failed at column={info.Column} carry=[{info.CarryMin},{info.CarryMax}] forced={info.Forced} unknown={info.Unknown}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
string message = $"[bit-contradiction] p={prime} ruled out divisor={divisor} ({reason})";
|
_bitContradictionStateRepository?.Upsert(prime, "excluded");
|
||||||
// Console.WriteLine(message);
|
|
||||||
_bitContradictionStateRepository?.Upsert(prime, message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DETAILED_LOG
|
||||||
Console.WriteLine($"[bitcontradiction ({sw.Elapsed})] Prime={prime}, Divisor={divisor} Decided={decided} Divides={divides} Reason={reason}");
|
Console.WriteLine($"[bitcontradiction ({sw.Elapsed})] Prime={prime}, Divisor={divisor} Decided={decided} Divides={divides} Reason={reason}");
|
||||||
|
#endif
|
||||||
return decided;
|
return decided;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in new issue
Block a user