From fea95509a7537b36ef0a523995faabff531ecc21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Thu, 12 Feb 2026 17:04:23 +0100 Subject: [PATCH] BitContradiction adjusted for p >= 138 mln, based on carry out analysis of the first 1024 columns. --- .../BitContradictionSolverWithAMultiplier.cs | 265 ++++++++++++++---- ...erDivisorByDivisorCpuTesterWithTemplate.cs | 15 + 2 files changed, 222 insertions(+), 58 deletions(-) diff --git a/PerfectNumbers.Core/ByDivisor/BitContradictionSolverWithAMultiplier.cs b/PerfectNumbers.Core/ByDivisor/BitContradictionSolverWithAMultiplier.cs index e1bf9ee..0c5884a 100644 --- a/PerfectNumbers.Core/ByDivisor/BitContradictionSolverWithAMultiplier.cs +++ b/PerfectNumbers.Core/ByDivisor/BitContradictionSolverWithAMultiplier.cs @@ -9,6 +9,7 @@ internal sealed class PrivateWorkSet { // Active hot-path scratch buffer. public ulong[] KnownOne1 = []; + public int[] CarryDropActiveAIndices = []; #if false private const int QPrefixReverseCacheSize = 1024; @@ -57,13 +58,18 @@ internal sealed class PrivateWorkSet #endif [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void EnsureCapacity(int wordCount, int qWordCount) + public void EnsureCapacity(int wordCount, int qWordCount, int carryDropActiveCount) { if (KnownOne1.Length < wordCount) { EnsureUlong(ref KnownOne1, wordCount); } + if (CarryDropActiveAIndices.Length < carryDropActiveCount) + { + EnsureInt(ref CarryDropActiveAIndices, carryDropActiveCount); + } + #if false if (KnownOne0.Length < wordCount) { @@ -124,10 +130,9 @@ internal sealed class PrivateWorkSet [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void EnsureUlong(ref ulong[] arr, int needed) => Array.Resize(ref arr, needed); -#if false [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void EnsureInt(ref int[] arr, int needed) => Array.Resize(ref arr, needed); -#endif + } /// /// Helper utilities for the BitContradiction divisor search. These methods implement the @@ -191,6 +196,7 @@ internal static class BitContradictionSolverWithAMultiplier private static ulong _prunedWithCarryDominance15161718; private static ulong _prunedWithCarryDominanceReached10AndStayedAbove5; private static ulong _prunedWithCarryDominance1011121314; + private static ulong _prunedWithHighDensityWithShortLowStreaksCarry; #endif @@ -250,7 +256,8 @@ internal static class BitContradictionSolverWithAMultiplier $"CarryDominance8910AndMore={_prunedWithCarryDominance8910}, " + $"CarryDominance15161718={_prunedWithCarryDominance15161718}, " + $"CarryDominanceReached10AndStayedAbove5={_prunedWithCarryDominanceReached10AndStayedAbove5}, " + - $"CarryDominance1011121314={_prunedWithCarryDominance1011121314}" + $"CarryDominance1011121314={_prunedWithCarryDominance1011121314}, " + + $"HighDensityWithShortLowStreaksCarry={_prunedWithHighDensityWithShortLowStreaksCarry}" ); } #endif @@ -2318,13 +2325,36 @@ internal static class BitContradictionSolverWithAMultiplier Dominance15161718Drop = 4, DominanceReached10AndStayedAbove5 = 5, Dominance1011121314 = 6, + HighDensityWithShortLowStreaks = 7, } + private static readonly BigInteger _testCase = BigInteger.Parse("337429718205079"); + +// #if WITH_PRUNE_STATS + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void TraceCarryDropHeuristicPrune( + int pLong, + BigInteger qExact, + CarryDropPruneReason pruneReason, + bool reached10, + bool reachedBelow5, + int reached5, + int zeroCount) + { + if (pLong == 138000953 && qExact.Equals(_testCase)) + { + Console.WriteLine($"CarryDrop target reached: p={pLong}, q={qExact}, reason={pruneReason}, reached10={reached10}, reachedBelow5={reachedBelow5}, reached5={reached5}, zeroCount={zeroCount}"); + } + } +// #endif + private static bool TryCarryDropHeuristicPrune( + BigInteger qExact, ReadOnlySpan qOneOffsets, int pLong, int maxAllowedA, ulong[] aBitsBuffer, + int[] activeAIndicesBuffer, out CarryDropPruneReason pruneReason) { pruneReason = CarryDropPruneReason.None; @@ -2350,7 +2380,8 @@ internal static class BitContradictionSolverWithAMultiplier } int wordCount = (bitLimit + 63) >> 6; - if ((uint)wordCount > (uint)aBitsBuffer.Length) + int qOneOffsetsLength = qOneOffsets.Length; + if ((uint)wordCount > (uint)aBitsBuffer.Length || (uint)qOneOffsetsLength > (uint)activeAIndicesBuffer.Length) { return false; } @@ -2360,10 +2391,9 @@ internal static class BitContradictionSolverWithAMultiplier long carry = 0; bool hadPositiveCarry = false; int upperOffsetExclusive = 1; - int qOneOffsetsLength = qOneOffsets.Length; - - int totalCarry67Count = 0, + int totalCarry6Count = 0, + totalCarry7Count = 0, totalCarry8Count = 0, totalCarry9Count = 0, totalCarry10Count = 0, @@ -2371,9 +2401,15 @@ internal static class BitContradictionSolverWithAMultiplier totalCarry12Count = 0, totalCarry13Count = 0, totalCarry14Count = 0, - totalCarry15Count = 0;//, totalCarry16Count = 0, totalCarry17Count = 0, totalCarry18Count = 0; + totalCarry15Count = 0, + totalCarry01234Count = 0, + totalCarryFirst32Count = 0, + totalCarryAbove10 = 0, totalCarryBelow10 = 0, + longestStreakBelow10 = 0, shortestStreakBelow10 = int.MaxValue, lastStreakBelow10 = 0, + lowestCarryAfter10 = int.MaxValue; + // , totalCarry16Count = 0, totalCarry17Count = 0, totalCarry18Count = 0; int totalCarryOtherPositive67Count = 0, - // totalCarryOtherPositive8910OtherCount = 0, + totalCarryOtherPositive8910OtherCount = 0, totalCarryOtherPositive1011121314OtherCount = 0, totalCarryOtherPositive15161718OtherCount = 0; bool hadZeroBefore = false; @@ -2381,7 +2417,12 @@ internal static class BitContradictionSolverWithAMultiplier bool reachedBelow5 = false; int reached5 = 0; int zeroCount = 0; - for (int blockStart = 0; blockStart < bitLimit; blockStart += CarryDropHeuristicBlockBits) + bool restoredTo10 = false; + ushort first10 = 0; + ushort totalTransitionsOver10 = 0; + long previousCarryOut = 0; + int activeContributionCount = 0; + for (ushort blockStart = 0; blockStart < bitLimit; blockStart += CarryDropHeuristicBlockBits) { int blockEnd = blockStart + CarryDropHeuristicBlockBits; if (blockEnd > bitLimit) @@ -2389,22 +2430,25 @@ internal static class BitContradictionSolverWithAMultiplier blockEnd = bitLimit; } - int blockCarry67Count = 0; - int blockCarryOtherPositive67Count = 0; - - for (int col = blockStart; col < blockEnd; col++) + for (ushort col = blockStart; col < blockEnd; col++) { + // Optimization: maintain rolling a-indices for active q offsets. + // Each active a-index advances by +1 per column, so we avoid recomputing (col - offset) + // in the hot loop while preserving the exact same contribution set and carry semantics. while (upperOffsetExclusive < qOneOffsetsLength && qOneOffsets[upperOffsetExclusive] <= col) { + activeAIndicesBuffer[activeContributionCount] = col - qOneOffsets[upperOffsetExclusive]; + activeContributionCount++; upperOffsetExclusive++; } long sumWithoutCurrent = carry; - for (int i = 1; i < upperOffsetExclusive; i++) + for (int i = 0; i < activeContributionCount; i++) { - int aIndex = col - qOneOffsets[i]; + int aIndex = activeAIndicesBuffer[i]; ulong word = aBitsBuffer[aIndex >> 6]; sumWithoutCurrent += (long)((word >> (aIndex & 63)) & 1UL); + activeAIndicesBuffer[i] = aIndex + 1; } int aBit = (1 - ((int)sumWithoutCurrent & 1)) & 1; @@ -2414,29 +2458,46 @@ internal static class BitContradictionSolverWithAMultiplier } long carryOut = (sumWithoutCurrent + aBit) >> 1; - if (carryOut == 6 || carryOut == 7) + + if (col < 32) { - blockCarry67Count++; + totalCarryFirst32Count += (int)carryOut; + } + + if ((previousCarryOut < 10 && carryOut >= 10) || + (previousCarryOut >= 10 && carryOut < 10)) + { + totalTransitionsOver10++; + } + + if (carryOut == 6) + { + totalCarry6Count++; + } + else if (carryOut == 7) + { + totalCarry7Count++; } else if (carryOut > 0) { - blockCarryOtherPositive67Count++; + totalCarryOtherPositive67Count++; } - // if (carryOut == 8) - // { - // totalCarry8Count++; - // } - // else if (carryOut == 9) - // { - // totalCarry9Count++; - // } - // else + if (carryOut == 8) + { + totalCarry8Count++; + } + else if (carryOut == 9) + { + totalCarry9Count++; + } + else if (carryOut == 10) { if (!reached10) { reached10 = true; + first10 = col; } totalCarry10Count++; @@ -2459,7 +2520,7 @@ internal static class BitContradictionSolverWithAMultiplier } else if (carryOut > 0) { - // totalCarryOtherPositive8910OtherCount++; + totalCarryOtherPositive8910OtherCount++; totalCarryOtherPositive1011121314OtherCount++; } @@ -2468,14 +2529,52 @@ internal static class BitContradictionSolverWithAMultiplier if (carryOut == 5) { reached5++; + restoredTo10 = false; } else if (carryOut < 5) { reachedBelow5 = true; } + else if (reached5 >= 1 && carryOut >= 10) + { + restoredTo10 = true; + } + + if (carryOut < 10) + { + totalCarryBelow10++; + lastStreakBelow10++; + if (carryOut < lowestCarryAfter10) + { + lowestCarryAfter10 = (int)carryOut; + } + } + else if (carryOut >= 10) + { + totalCarryAbove10++; + if (lastStreakBelow10 > longestStreakBelow10) + { + longestStreakBelow10 = lastStreakBelow10; + } + + if (lastStreakBelow10 < shortestStreakBelow10) + { + shortestStreakBelow10 = lastStreakBelow10; + } + + if (lastStreakBelow10 > 0) + { + lastStreakBelow10 = 0; + } + } } - if (carryOut >= 15) + if (carryOut <= 4) + { + totalCarry01234Count++; + } + + if (carryOut >= 14) // if (carryOut == 15) { totalCarry15Count++; @@ -2502,6 +2601,9 @@ internal static class BitContradictionSolverWithAMultiplier if (hadZeroBefore) { pruneReason = CarryDropPruneReason.ZeroDrop; +#if WITH_PRUNE_STATS + TraceCarryDropHeuristicPrune(pLong, qExact, pruneReason, reached10, reachedBelow5, reached5, zeroCount); +#endif return true; } @@ -2520,29 +2622,40 @@ internal static class BitContradictionSolverWithAMultiplier carry = carryOut; } - - totalCarry67Count += blockCarry67Count; - totalCarryOtherPositive67Count += blockCarryOtherPositive67Count; } - if (reached10 && !reachedBelow5 && reached5 <= 1 && zeroCount <= 5) + if (reached10 && zeroCount <= 5 && !(reached5 == 0 && zeroCount <= 4) && + !reachedBelow5 && reached5 <= 1 && (reached5 == 0 || restoredTo10) + ) { pruneReason = CarryDropPruneReason.DominanceReached10AndStayedAbove5; + TraceCarryDropHeuristicPrune(pLong, qExact, pruneReason, reached10, reachedBelow5, reached5, zeroCount); +#if WITH_PRUNE_STATS +#endif return true; } if (totalCarry12Count > totalCarry10Count && totalCarry12Count > totalCarry11Count && totalCarry12Count > totalCarry13Count && totalCarry12Count > totalCarry14Count && - totalCarry11Count > 100 && totalCarry13Count > 100 + totalCarry11Count > 100 && totalCarry13Count > 100 && (first10 < 34 || totalCarry01234Count < 21) && + totalCarryFirst32Count > 118 ) { pruneReason = CarryDropPruneReason.Dominance1011121314; +#if WITH_PRUNE_STATS + TraceCarryDropHeuristicPrune(pLong, qExact, pruneReason, reached10, reachedBelow5, reached5, zeroCount); +#endif return true; - } - - if (totalCarry67Count > totalCarryOtherPositive67Count) + } + + var totalCarry67Count = totalCarry6Count + totalCarry7Count; + if (totalCarry67Count > totalCarryOtherPositive67Count || + (totalCarry67Count > totalCarry8Count && zeroCount < first10 && lowestCarryAfter10 >= 3 && (totalCarry7Count > totalCarry8Count || (totalCarry7Count & 1) == 1))) { pruneReason = CarryDropPruneReason.Dominance67ThenZeroDrop; +#if WITH_PRUNE_STATS + TraceCarryDropHeuristicPrune(pLong, qExact, pruneReason, reached10, reachedBelow5, reached5, zeroCount); +#endif return true; } @@ -2553,15 +2666,49 @@ internal static class BitContradictionSolverWithAMultiplier ) { pruneReason = CarryDropPruneReason.Dominance15161718Drop; +#if WITH_PRUNE_STATS + TraceCarryDropHeuristicPrune(pLong, qExact, pruneReason, reached10, reachedBelow5, reached5, zeroCount); +#endif return true; } - // if (totalCarry8Count + totalCarry9Count + totalCarry10Count > totalCarryOtherPositive8910OtherCount && - // totalCarry8Count >= 150 && totalCarry9Count >= 150 && totalCarry10Count >= 150) - // { - // pruneReason = CarryDropPruneReason.Dominance10AndMoreDrop; - // return true; - // } + if (totalCarry8Count + totalCarry9Count + totalCarry10Count > totalCarryOtherPositive8910OtherCount && + totalCarry8Count >= 150 && totalCarry9Count >= 150 && totalCarry10Count >= 150 && + (totalCarry10Count >= totalCarry9Count || totalCarry8Count >= totalCarry9Count || totalCarry10Count < 190)) + { + pruneReason = CarryDropPruneReason.Dominance10AndMoreDrop; +#if WITH_PRUNE_STATS + TraceCarryDropHeuristicPrune(pLong, qExact, pruneReason, reached10, reachedBelow5, reached5, zeroCount); +#endif + return true; + } + + if ((totalCarry8Count & 1) == 0 && (totalCarry9Count & 1) == 0 && (totalCarry10Count & 1) == 0) + { + pruneReason = CarryDropPruneReason.Dominance10AndMoreDrop; +#if WITH_PRUNE_STATS + TraceCarryDropHeuristicPrune(pLong, qExact, pruneReason, reached10, reachedBelow5, reached5, zeroCount); +#endif + return true; + } + + if (totalCarryAbove10 >= 850 && totalCarryBelow10 < 100 && shortestStreakBelow10 <= 3 && longestStreakBelow10 <= 12 && totalCarry11Count < 150 && lastStreakBelow10 < 12) + { + pruneReason = CarryDropPruneReason.HighDensityWithShortLowStreaks; +#if WITH_PRUNE_STATS + TraceCarryDropHeuristicPrune(pLong, qExact, pruneReason, reached10, reachedBelow5, reached5, zeroCount); +#endif + return true; + } + + if (reached10 && reached5 == 0 && !reachedBelow5 && totalTransitionsOver10 < 35) + { + pruneReason = CarryDropPruneReason.DominanceReached10AndStayedAbove5; +#if WITH_PRUNE_STATS + TraceCarryDropHeuristicPrune(pLong, qExact, pruneReason, reached10, reachedBelow5, reached5, zeroCount); +#endif + return true; + } return false; } @@ -2738,8 +2885,9 @@ internal static class BitContradictionSolverWithAMultiplier } int carryDropWordCount = (carryDropBitLimit + 63) >> 6; - workSet.EnsureCapacity(carryDropWordCount, 0); + workSet.EnsureCapacity(carryDropWordCount, 0, qOneOffsetsLength); ulong[] knownOne1 = workSet.KnownOne1; + int[] carryDropActiveAIndices = workSet.CarryDropActiveAIndices; Array.Clear(knownOne1, 0, carryDropWordCount); #if false Array.Clear(qMaskWords, 0, qWordCount); @@ -2759,7 +2907,7 @@ internal static class BitContradictionSolverWithAMultiplier return true; } - if (TryCarryDropHeuristicPrune(qOneOffsets, pLong, maxAllowedA, knownOne1, out CarryDropPruneReason carryDropPruneReason)) + if (TryCarryDropHeuristicPrune(qExact, qOneOffsets, pLong, maxAllowedA, knownOne1, carryDropActiveAIndices, out CarryDropPruneReason carryDropPruneReason)) { divides = false; #if WITH_PRUNE_STATS @@ -2787,13 +2935,22 @@ internal static class BitContradictionSolverWithAMultiplier { IncrementPruneCounter(ref _prunedWithCarryDropHeuristic); } + else if (carryDropPruneReason == CarryDropPruneReason.HighDensityWithShortLowStreaks) + { + IncrementPruneCounter(ref _prunedWithHighDensityWithShortLowStreaksCarry); + } #endif #if DETAILED_LOG reason = ContradictionReason.ParityUnreachable; #endif -// #if WITH_PRUNE_STATS +#if WITH_PRUNE_STATS + if (pLong == 138000953 && qExact.Equals(_testCase)) + { + Console.WriteLine($"Validating heuristic prune: p={pLong}, q={qExact}, reason={carryDropPruneReason}"); + } + +#endif ValidateHeuristicPruneAgainstPowmod2OrThrow(qExact, pLong, $"CarryDropHeuristic:{carryDropPruneReason}"); -// #endif return true; } #if false @@ -3084,8 +3241,8 @@ internal static class BitContradictionSolverWithAMultiplier #if WITH_PRUNE_STATS IncrementPruneCounter(ref _prunedWithTryPropagateCarry1); - ValidateHeuristicPruneAgainstPowmod2OrThrow(qExact, pLong, "TryPropagateCarry1"); #endif + ValidateHeuristicPruneAgainstPowmod2OrThrow(qExact, pLong, "TryPropagateCarry1"); return true; } @@ -5707,11 +5864,3 @@ internal static class BitContradictionSolverWithAMultiplier return true; } } - - - - - - - - diff --git a/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTesterWithTemplate.cs b/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTesterWithTemplate.cs index eaa0e71..b9d5d7d 100644 --- a/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTesterWithTemplate.cs +++ b/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTesterWithTemplate.cs @@ -62,6 +62,9 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN private GpuUInt128WorkSet _divisorScanGpuWorkSet; private static readonly ConcurrentDictionary _checkedPow2MinusOne = new(); private ulong _currentPrime; +#if WITH_PRUNE_STATS + private readonly TimeSpan _slowCalculationThreshold = TimeSpan.FromMilliseconds(700); +#endif #if DEVICE_GPU && DivisorSet_BitContradiction [MethodImpl(MethodImplOptions.AggressiveOptimization)] @@ -5104,6 +5107,10 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN BigInteger divisor = (step * currentK) + BigInteger.One; _qBits = new int[(int)prime]; Stopwatch sw = new Stopwatch(); +#if WITH_PRUNE_STATS + var slowCalculationThreshold = _slowCalculationThreshold; +#endif + // sw.Start(); while (true) { @@ -5143,6 +5150,13 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN } else { +#if WITH_PRUNE_STATS + if (sw.Elapsed >= slowCalculationThreshold) + { + Console.WriteLine($"SLOW CALCULATIONS: Prime {prime} ({sw.Elapsed}: {divisor})"); + _consolePrintOut = 0; + } +#else var printToConsole = (_consolePrintOut++) == PerfectNumberConstants.ConsoleInterval; if (printToConsole) { @@ -5152,6 +5166,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN // sw.Reset(); // sw.Start(); } +#endif } #if DETAILED_LOG