From d0dbb8f92ac55fd733b2295523f151d6794bd5dd Mon Sep 17 00:00:00 2001 From: mlemanczyk Date: Tue, 13 Jan 2026 04:20:34 +0100 Subject: [PATCH] BitContradiction solvers improvements to increase early exclusions. --- .../BitContradictionSolverWithAMultiplier.cs | 285 +++++++++++++++++- .../BitContradictionSolverWithQMultiplier.cs | 128 +++++++- ...erDivisorByDivisorCpuTesterWithTemplate.cs | 198 +++++------- 3 files changed, 469 insertions(+), 142 deletions(-) diff --git a/PerfectNumbers.Core/ByDivisor/BitContradictionSolverWithAMultiplier.cs b/PerfectNumbers.Core/ByDivisor/BitContradictionSolverWithAMultiplier.cs index 1d223dc..043fe83 100644 --- a/PerfectNumbers.Core/ByDivisor/BitContradictionSolverWithAMultiplier.cs +++ b/PerfectNumbers.Core/ByDivisor/BitContradictionSolverWithAMultiplier.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Runtime.CompilerServices; namespace PerfectNumbers.Core.ByDivisor; @@ -14,9 +15,85 @@ internal static class BitContradictionSolverWithAMultiplier private const int TailCarryBatchColumns = 1024; private const int TailCarryBatchRepeats = 1; + private const int ModLocalPrefilterMaxABits = 20; + private static readonly int[] ModLocalPrefilterModuli = { 65537, 65521 }; + [ThreadStatic] private static TopDownPruneFailure? _lastTopDownFailure; 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? _topDownCache; + [ThreadStatic] + private static List? _topDownCacheOrder; + + private static long ComputeQOffsetsHash(ReadOnlySpan 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(); + _topDownCacheOrder ??= new List(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; internal static TopDownPruneFailure? LastTopDownFailure => _lastTopDownFailure; @@ -80,6 +157,109 @@ internal static class BitContradictionSolverWithAMultiplier return true; } + private static bool TryModularPrefilter( + ReadOnlySpan 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 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; + } + /// /// 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). @@ -145,16 +325,44 @@ internal static class BitContradictionSolverWithAMultiplier return true; } - if (!TryTopDownCarryPrune(qOneOffsets, qOneOffsetsLength, pLong, maxAllowedA, out _lastTopDownFailure)) + if (!TryModularPrefilter(qOneOffsets, p, maxAllowedA)) { reason = ContradictionReason.ParityUnreachable; 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 highColumn = pLong - 1; CarryRange carryLow = CarryRange.Zero; CarryRange carryHigh = CarryRange.Zero; + int bottomWindowStart = 0; + int bottomWindowEnd = 0; + int topWindowStart = qOneOffsetsLength; + int topWindowEnd = qOneOffsetsLength; + bool processTop = true; while (lowColumn <= highColumn) @@ -164,7 +372,7 @@ internal static class BitContradictionSolverWithAMultiplier 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; return true; @@ -174,7 +382,7 @@ internal static class BitContradictionSolverWithAMultiplier } 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; } @@ -237,6 +445,8 @@ internal static class BitContradictionSolverWithAMultiplier ref CarryRange carry, ref long maxKnownA, ref long segmentBase, + ref int windowStartIdx, + ref int windowEndIdx, sbyte[] segmentState0, sbyte[] segmentState1, out ContradictionReason reason) @@ -244,8 +454,53 @@ internal static class BitContradictionSolverWithAMultiplier long endColumn = startColumn + columnCount; int qOneOffsetsLength = qOneOffsets.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 unknown = 0L; long chosenIndex = -1L; @@ -266,7 +521,18 @@ internal static class BitContradictionSolverWithAMultiplier 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]; long aIndex = column - offset; @@ -418,6 +684,7 @@ internal static class BitContradictionSolverWithAMultiplier carry = next; } + column++; } reason = ContradictionReason.None; @@ -431,6 +698,8 @@ internal static class BitContradictionSolverWithAMultiplier long startHighColumn, int columnCount, CarryRange carryOut, + ref int windowStart, + ref int windowEnd, out CarryRange nextCarryOut, out TopDownPruneFailure? failure) { @@ -438,8 +707,6 @@ internal static class BitContradictionSolverWithAMultiplier long carryOutMin = carryOut.Min, carryOutMax = carryOut.Max, value; - int windowStart = qOneOffsetsLength; - int windowEnd = qOneOffsetsLength; long endColumn = startHighColumn - columnCount + 1; long column = startHighColumn; @@ -560,11 +827,11 @@ internal static class BitContradictionSolverWithAMultiplier out TopDownPruneFailure? failure) { failure = null; + int windowStart = qOneOffsetsLength; + int windowEnd = qOneOffsetsLength; long carryOutMin = 0, carryOutMax = 0, value; - int windowStart = qOneOffsetsLength; - int windowEnd = qOneOffsetsLength; long column = pLong - 1; while (column >= 0) diff --git a/PerfectNumbers.Core/ByDivisor/BitContradictionSolverWithQMultiplier.cs b/PerfectNumbers.Core/ByDivisor/BitContradictionSolverWithQMultiplier.cs index 89569bc..158eb57 100644 --- a/PerfectNumbers.Core/ByDivisor/BitContradictionSolverWithQMultiplier.cs +++ b/PerfectNumbers.Core/ByDivisor/BitContradictionSolverWithQMultiplier.cs @@ -52,6 +52,79 @@ internal static class BitContradictionSolverWithQMultiplier [ThreadStatic] 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? _topDownCache; + [ThreadStatic] + private static List? _topDownCacheOrder; + + private static long ComputeQOffsetsHash(ReadOnlySpan 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(); + _topDownCacheOrder ??= new List(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 BottomUpFailure? LastBottomUpFailure => _lastBottomUpFailure; @@ -188,17 +261,40 @@ internal static class BitContradictionSolverWithQMultiplier 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)) { - reason = ContradictionReason.ParityUnreachable; - Console.WriteLine($"[bit-contradiction] parity unreachable in top-down prune (failure={_lastTopDownFailure.HasValue})"); - return true; + _lastTopDownFailure = cached.Failure; + if (!cached.Success) + { + reason = ContradictionReason.ParityUnreachable; + Console.WriteLine($"[bit-contradiction] parity unreachable in top-down prune (failure={_lastTopDownFailure.HasValue})"); + 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 highColumn = pLong - 1; CarryRange carryLow = CarryRange.Zero; CarryRange carryHigh = CarryRange.Zero; + int bottomWindowStart = 0; + int bottomWindowEnd = 0; + int topWindowStart = qOneOffsetsLength; + int topWindowEnd = qOneOffsetsLength; + bool processTop = true; while (lowColumn <= highColumn) @@ -208,7 +304,7 @@ internal static class BitContradictionSolverWithQMultiplier 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; return true; @@ -218,7 +314,7 @@ internal static class BitContradictionSolverWithQMultiplier } 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; } @@ -281,6 +377,8 @@ internal static class BitContradictionSolverWithQMultiplier int columnCount, ref CarryRange carry, ref long segmentBase, + ref int windowStartIdx, + ref int windowEndIdx, sbyte[] segmentState0, sbyte[] segmentState1, out ContradictionReason reason) @@ -310,7 +408,18 @@ internal static class BitContradictionSolverWithQMultiplier 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]; long aIndex = column - offset; @@ -480,6 +589,8 @@ internal static class BitContradictionSolverWithQMultiplier long startHighColumn, int columnCount, CarryRange carryOut, + ref int windowStart, + ref int windowEnd, out CarryRange nextCarryOut, out TopDownPruneFailure? failure) { @@ -487,8 +598,6 @@ internal static class BitContradictionSolverWithQMultiplier long carryOutMin = carryOut.Min, carryOutMax = carryOut.Max, value; - int windowStart = qOneOffsetsLength; - int windowEnd = qOneOffsetsLength; long endColumn = startHighColumn - columnCount + 1; long column = startHighColumn; @@ -612,6 +721,7 @@ internal static class BitContradictionSolverWithQMultiplier long carryOutMin = 0, carryOutMax = 0, value; + int windowStart = qOneOffsetsLength; int windowEnd = qOneOffsetsLength; diff --git a/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTesterWithTemplate.cs b/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTesterWithTemplate.cs index 9ad0a3a..91e6501 100644 --- a/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTesterWithTemplate.cs +++ b/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTesterWithTemplate.cs @@ -1398,7 +1398,9 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool TryExactBitTreeCheck(ulong exponent, BigInteger divisor, out bool divides) { +#if DETAILED_LOG Stopwatch sw = Stopwatch.StartNew(); +#endif int qBitLength = GetBitLengthPortable(divisor); int aBitLength = GetBitLengthPortable(exponent); @@ -1504,13 +1506,17 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN return true; } } +#if DETAILED_LOG sw.Stop(); +#endif ArrayPool.Shared.Return(aWindowArray); divides = carry == 0; +#if DETAILED_LOG var elapsed = sw.Elapsed; Console.WriteLine($"[bittree ({elapsed})] Prime={exponent}, Divisor={divisor} Decided=True Divides={divides}"); +#endif if (!divides) { 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); BigInteger divisor = (step * currentK) + BigInteger.One; + Stopwatch sw = new Stopwatch(); while (true) { int remainder8 = (stepMod8 * kRem8 + 1) & 7; @@ -4342,80 +4349,14 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN goto MOVE_NEXT; } + bool divides; + // divides = BigInteger.ModPow(BigIntegerNumbers.Two, prime, divisor) == 1; + ulong effectiveExponent = 0UL; -// if (divisor <= ulong.MaxValue) -// { -// ulong divisor64 = (ulong)divisor; -// MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor64); -// #if DEVICE_GPU -// bool computed = MersenneNumberDivisorByDivisorTester.TryCalculateCycleLengthForExponentGpu( -// divisor64, -// Accelerator, -// prime, -// divisorData, -// out ulong computedCycle, -// out bool primeOrderFailed); -// #elif DEVICE_HYBRID -// bool computed = MersenneNumberDivisorByDivisorTester.TryCalculateCycleLengthForExponentHybrid( -// divisor64, -// Accelerator, -// prime, -// divisorData, -// out ulong computedCycle, -// out bool primeOrderFailed); -// #else -// bool computed = MersenneNumberDivisorByDivisorTester.TryCalculateCycleLengthForExponentCpu( -// divisor64, -// prime, -// divisorData, -// out ulong computedCycle, -// out bool primeOrderFailed); -// #endif -// ulong divisorCycle; -// if (!computed || computedCycle == 0UL) -// { -// #if DEVICE_GPU -// divisorCycle = MersenneNumberDivisorByDivisorTester.CalculateCycleLengthGpu( -// divisor64, -// Accelerator, -// divisorData, -// skipPrimeOrderHeuristic: primeOrderFailed); -// #elif DEVICE_HYBRID -// divisorCycle = MersenneNumberDivisorByDivisorTester.CalculateCycleLengthHybrid( -// divisor64, -// Accelerator, -// divisorData, -// skipPrimeOrderHeuristic: primeOrderFailed); -// #else -// divisorCycle = MersenneNumberDivisorByDivisorTester.CalculateCycleLengthCpu( -// divisor64, -// divisorData, -// skipPrimeOrderHeuristic: primeOrderFailed); -// #endif -// } -// else -// { -// divisorCycle = computedCycle; -// } - -// 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); + sw.Reset(); + sw.Start(); + bool decided = TryExactBitContradictionCheck(prime, divisor, effectiveExponent, out divides); + sw.Stop(); if (!decided) { 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; return true; } + else { + Console.WriteLine($"Prime {prime} ({sw.Elapsed}), excluded {divisor}"); + } RecordState(currentK); MOVE_NEXT: @@ -4441,63 +4385,65 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN return false; - bool fits64 = minK <= ulong.MaxValue; - bool limitFits64 = allowedMax.IsZero || allowedMax <= ulong.MaxValue; + // 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 && 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; - } + // 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); - } + // 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); + // return CheckDivisorsLarge( + // prime, + // primeDecimalMask, + // allowedMax, + // minK, + // step, + // out processedAll, + // out foundDivisor); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryExactBitContradictionCheck(ulong prime, BigInteger divisor, ulong effectiveExponent, out bool divides) { +#if DETAILED_LOG Stopwatch sw = Stopwatch.StartNew(); +#endif divides = false; // const ulong DebugTopDownDivisor = 1209708008767UL; // const ulong DebugTopDownDivisor = 1007991562955719UL; @@ -4551,7 +4497,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN ReadOnlySpan oneOffsetsSlice = oneOffsets[..oneCount]; ulong exponent = effectiveExponent > 0UL ? effectiveExponent : prime; // 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); // bool decided = BitContradictionSolverBaseline.TryCheckDivisibilityFromOneOffsets(oneOffsetsSlice, exponent, out divides, out var reason); if (decided && divides) @@ -4566,9 +4512,12 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN } } +#if DETAILED_LOG sw.Stop(); +#endif if (decided && !divides) { +#if DETAILED_LOG if (reason == ContradictionReason.ParityUnreachable) { 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}"); } } +#endif - string message = $"[bit-contradiction] p={prime} ruled out divisor={divisor} ({reason})"; - // Console.WriteLine(message); - _bitContradictionStateRepository?.Upsert(prime, message); + _bitContradictionStateRepository?.Upsert(prime, "excluded"); } +#if DETAILED_LOG Console.WriteLine($"[bitcontradiction ({sw.Elapsed})] Prime={prime}, Divisor={divisor} Decided={decided} Divides={divides} Reason={reason}"); +#endif return decided; } #endif