BitContradiction performance improvements.

This commit is contained in:
mlemanczyk committed 2026-01-22 15:33:57 +01:00
1 parent c63ff5df32
commit 13e1c7f34e
1 file changed
+171 -129
@@ -581,8 +581,7 @@ internal static class BitContradictionSolverWithAMultiplier
int upperT = column <= 0 ? 0 : column; int upperT = column <= 0 ? 0 : column;
int left = LowerBound(qOneOffsets, lowerT); FindBounds(qOneOffsets, lowerT, upperT, out int left, out int right);
int right = UpperBound(qOneOffsets, upperT);
int n = right - left; int n = right - left;
if (n < 0) return false; if (n < 0) return false;
@@ -705,14 +704,17 @@ internal static class BitContradictionSolverWithAMultiplier
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int LowerBound(ReadOnlySpan<int> values, int target) private static void FindBounds(ReadOnlySpan<int> values, int lowerTarget, int upperTarget, out int lower, out int upper)
{ {
// Find lower bound for lowerTarget
int lo = 0; int lo = 0;
int hi = values.Length; int valuesLength = values.Length;
int hi = valuesLength;
int mid;
while (lo < hi) while (lo < hi)
{ {
int mid = lo + ((hi - lo) >> 1); mid = lo + ((hi - lo) >> 1);
if (values[mid] < target) if (values[mid] < lowerTarget)
{ {
lo = mid + 1; lo = mid + 1;
} }
@@ -721,18 +723,17 @@ internal static class BitContradictionSolverWithAMultiplier
hi = mid; hi = mid;
} }
} }
return lo;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] lower = lo;
private static int UpperBound(ReadOnlySpan<int> values, int target)
{ // Find upper bound for upperTarget, starting from the lower bound's result.
int lo = 0; // Since upperTarget >= lowerTarget is assumed, we can start the search for the upper bound
int hi = values.Length; // from the position of the lower bound, effectively reducing the search space.
hi = valuesLength;
while (lo < hi) while (lo < hi)
{ {
int mid = lo + ((hi - lo) >> 1); mid = lo + ((hi - lo) >> 1);
if (values[mid] <= target) if (values[mid] <= upperTarget)
{ {
lo = mid + 1; lo = mid + 1;
} }
@@ -741,7 +742,8 @@ internal static class BitContradictionSolverWithAMultiplier
hi = mid; hi = mid;
} }
} }
return lo;
upper = lo;
} }
/// <summary> /// <summary>
@@ -842,8 +844,7 @@ internal static class BitContradictionSolverWithAMultiplier
column <= 0 ? 0 : column <= 0 ? 0 :
(column >= int.MaxValue ? int.MaxValue : (int)column); (column >= int.MaxValue ? int.MaxValue : (int)column);
int left = LowerBound(qOneOffsets, lowerT); FindBounds(qOneOffsets, lowerT, upperT, out int left, out int right);
int right = UpperBound(qOneOffsets, upperT);
int n = right - left; int n = right - left;
if (n < 0) if (n < 0)
@@ -972,31 +973,31 @@ internal static class BitContradictionSolverWithAMultiplier
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ComputeForcedRemainingAndClean_Prefix( private static void ComputeForcedRemainingAndClean_Prefix(
ulong[] qMask, ulong[] oneWin, ulong[] zeroWin, in ulong[] qMask, in ulong[] oneWin, in ulong[] zeroWin,
int words, ulong lastMask, int words, ulong lastMask,
out int forced, out int remaining, out bool clean) out int forced, out int remaining)
{ {
forced = 0; forced = 0;
remaining = 0; remaining = 0;
clean = true;
int wordsMinusOne = words - 1;
for (int i = 0; i < words; i++) for (int i = 0; i < words; i++)
{ {
ulong q = qMask[i]; ulong unknownBits = qMask[i];
if (i == words - 1) q &= lastMask; if (i == wordsMinusOne) unknownBits &= lastMask;
ulong one = oneWin[i]; ulong one = oneWin[i];
ulong known = one | zeroWin[i]; ulong known = one | zeroWin[i];
ulong forcedBits = q & one; // one is forcedBits here. We're reusing variable to limit registry pressure.
ulong unknownBits = q & ~known; one &= unknownBits;
unknownBits &= ~known;
forced += BitOperations.PopCount(forcedBits); forced += BitOperations.PopCount(one);
remaining += BitOperations.PopCount(unknownBits); remaining += BitOperations.PopCount(unknownBits);
}
}
if ((q & known) != 0) clean = false;
}
}
private static int ComputeQMod(ReadOnlySpan<int> qOneOffsets, int mod) private static int ComputeQMod(ReadOnlySpan<int> qOneOffsets, int mod)
{ {
int count = qOneOffsets.Length; int count = qOneOffsets.Length;
@@ -1094,41 +1095,41 @@ internal static class BitContradictionSolverWithAMultiplier
bool top0KnownOne, bool top0KnownOne,
bool top0KnownZero, bool top0KnownZero,
bool top1KnownOne, bool top1KnownOne,
bool top1KnownZero,
long zeroTailStart, long zeroTailStart,
out BigInteger minA, out BigInteger minA,
out BigInteger maxA) out BigInteger maxA)
{ {
_dynamicModChecks++; _dynamicModChecks++;
long effectiveTop = maxAllowedA; if (zeroTailStart <= maxAllowedA + 1)
if (zeroTailStart <= effectiveTop + 1)
{ {
effectiveTop = zeroTailStart - 1; zeroTailStart--;
} }
if (effectiveTop < 0) // zeroTailStart is effectiveTop from here. We're reusing variable to limit registry pressure.
if (zeroTailStart < 0)
{ {
minA = BigInteger.Zero; minA = BigInteger.Zero;
maxA = BigInteger.Zero; maxA = BigInteger.Zero;
return; return;
} }
int shift = effectiveTop + 1 > int.MaxValue ? int.MaxValue : (int)(effectiveTop + 1); int shift = zeroTailStart + 1 > int.MaxValue ? int.MaxValue : (int)(zeroTailStart + 1);
maxA = (BigInteger.One << shift) - BigInteger.One; maxA = (BigInteger.One << shift) - BigInteger.One;
minA = BigInteger.Zero; minA = BigInteger.Zero;
if (effectiveTop == maxAllowedA) if (zeroTailStart == maxAllowedA)
{ {
if (top0KnownOne) if (top0KnownOne)
{ {
int minShift = maxAllowedA > int.MaxValue ? int.MaxValue : (int)maxAllowedA; shift = maxAllowedA > int.MaxValue ? int.MaxValue : (int)maxAllowedA;
minA = BigInteger.One << minShift; minA = BigInteger.One << shift;
} }
else if (top0KnownZero && top1KnownOne && maxAllowedA - 1 >= 0) else if (top0KnownZero && top1KnownOne && maxAllowedA - 1 >= 0)
{ {
long index = maxAllowedA - 1; // zeroTailStart is index from here. We're reusing the variable to limit registry pressure.
int minShift = index > int.MaxValue ? int.MaxValue : (int)index; zeroTailStart = maxAllowedA - 1;
minA = BigInteger.One << minShift; shift = zeroTailStart > int.MaxValue ? int.MaxValue : (int)zeroTailStart;
minA = BigInteger.One << shift;
} }
} }
} }
@@ -1154,7 +1155,6 @@ internal static class BitContradictionSolverWithAMultiplier
bool top0KnownOne, bool top0KnownOne,
bool top0KnownZero, bool top0KnownZero,
bool top1KnownOne, bool top1KnownOne,
bool top1KnownZero,
long zeroTailStart) long zeroTailStart)
{ {
_dynamicModChecks++; _dynamicModChecks++;
@@ -1174,7 +1174,6 @@ internal static class BitContradictionSolverWithAMultiplier
top0KnownOne, top0KnownOne,
top0KnownZero, top0KnownZero,
top1KnownOne, top1KnownOne,
top1KnownZero,
zeroTailStart, zeroTailStart,
out BigInteger minA, out BigInteger minA,
out BigInteger maxA); out BigInteger maxA);
@@ -1798,7 +1797,7 @@ internal static class BitContradictionSolverWithAMultiplier
lowColumn, blockSize, lowColumn, blockSize,
ref carryLow, ref maxKnownA, ref segmentBase, ref carryLow, ref maxKnownA, ref segmentBase,
ref knownOne0, ref knownOne1, ref knownZero0, ref knownZero1, ref knownOne0, ref knownOne1, ref knownZero0, ref knownZero1,
windowSize, aOneWin, aZeroWin, ref windowColumn, windowSize, wordCount, aOneWin, aZeroWin, ref windowColumn,
out reason)) out reason))
#else #else
if (!TryProcessBottomUpBlockRowAware( if (!TryProcessBottomUpBlockRowAware(
@@ -1806,7 +1805,7 @@ internal static class BitContradictionSolverWithAMultiplier
lowColumn, blockSize, lowColumn, blockSize,
ref carryLow, ref maxKnownA, ref segmentBase, ref carryLow, ref maxKnownA, ref segmentBase,
ref knownOne0, ref knownOne1, ref knownZero0, ref knownZero1, ref knownOne0, ref knownOne1, ref knownZero0, ref knownZero1,
windowSize, aOneWin, aZeroWin, ref windowColumn)) windowSize, wordCount, aOneWin, aZeroWin, ref windowColumn))
#endif #endif
{ {
#if DETAILED_LOG #if DETAILED_LOG
@@ -2073,17 +2072,19 @@ internal static class BitContradictionSolverWithAMultiplier
int[] delta = GetStableUnknownDelta(unknown); int[] delta = GetStableUnknownDelta(unknown);
const long CarryDecimalMask = (1L << ColumnsAtOnce) - 1L; const long CarryDecimalMask = (1L << ColumnsAtOnce) - 1L;
int carryMask;
while (remaining >= ColumnsAtOnce) while (remaining >= ColumnsAtOnce)
{ {
int low = (int)(carryMax & CarryDecimalMask); carryMask = (int)(carryMax & CarryDecimalMask);
carryMax = (carryMax + delta[low]) >> ColumnsAtOnce; carryMax = (carryMax + delta[carryMask]) >> ColumnsAtOnce;
remaining -= ColumnsAtOnce; remaining -= ColumnsAtOnce;
} }
while (remaining > 0) while (remaining > 0)
{ {
int sMax = (((unknown ^ (int)carryMax) & 1) != 0) ? unknown : unknown - 1; // carryMask is sMax here. We're reusing variable to limit registry pressure.
carryMax = (carryMax + sMax - 1) >> 1; carryMask = (((unknown ^ (int)carryMax) & 1) != 0) ? unknown : unknown - 1;
carryMax = (carryMax + carryMask - 1) >> 1;
remaining--; remaining--;
} }
@@ -2108,20 +2109,27 @@ internal static class BitContradictionSolverWithAMultiplier
// Row-aware bottom-up block processing // Row-aware bottom-up block processing
// ----------------------------- // -----------------------------
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ShiftLeft1InPlace(ulong[] words, int wordCount, ulong lastWordMask) private static void ShiftLeft1InPlace(ulong[] wordsOne, ulong[] wordsZero, int wordCount, ulong lastWordMask)
{ {
ulong carry = 0;
for (int i = 0; i < wordCount; i++)
{
ulong w = words[i];
ulong newCarry = w >> 63;
words[i] = (w << 1) | carry;
carry = newCarry;
}
if (wordCount > 0) if (wordCount > 0)
{ {
words[wordCount - 1] &= lastWordMask; ulong carryOne = 0,
carryZero = 0;
for (int i = 0; i < wordCount; i++)
{
ulong w = wordsOne[i];
wordsOne[i] = (w << 1) | carryOne;
carryOne = w >> 63;
w = wordsZero[i];
wordsZero[i] = (w << 1) | carryZero;
carryZero = w >> 63;
}
wordCount--;
wordsOne[wordCount] &= lastWordMask;
wordsZero[wordCount] &= lastWordMask;
} }
} }
@@ -2229,23 +2237,28 @@ internal static class BitContradictionSolverWithAMultiplier
ulong[] knownZero1) ulong[] knownZero1)
{ {
if (aIndex < 0) return; if (aIndex < 0) return;
int rel, w;
ulong m;
if (aIndex >= segmentBase && aIndex < segmentBase + windowSize) if (aIndex >= segmentBase && aIndex < segmentBase + windowSize)
{ {
int rel = aIndex - segmentBase; rel = aIndex - segmentBase;
int w = rel >> 6; w = rel >> 6;
ulong m = 1UL << (rel & 63); m = 1UL << (rel & 63);
if (valueOne) if (valueOne)
knownOne0[w] |= m; knownOne0[w] |= m;
else else
knownZero0[w] |= m; knownZero0[w] |= m;
return; return;
} }
int seg1Start = segmentBase - windowSize;
if (aIndex >= seg1Start && aIndex < segmentBase) // windowSize is seg1Start here. We're reusing the variable to limit registry pressure.
windowSize = segmentBase - windowSize;
if (aIndex >= windowSize && aIndex < segmentBase)
{ {
int rel = aIndex - seg1Start; rel = aIndex - windowSize;
int w = rel >> 6; w = rel >> 6;
ulong m = 1UL << (rel & 63); m = 1UL << (rel & 63);
if (valueOne) if (valueOne)
knownOne1[w] |= m; knownOne1[w] |= m;
else else
@@ -2255,7 +2268,7 @@ internal static class BitContradictionSolverWithAMultiplier
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryFindSingleUnknownInQ_Prefix( private static bool TryFindSingleUnknownInQ_Prefix(
ulong[] qMask, ulong[] oneWin, ulong[] zeroWin, in ulong[] qMask, in ulong[] oneWin, in ulong[] zeroWin,
int words, ulong lastMask, int words, ulong lastMask,
out int offset) out int offset)
{ {
@@ -2267,15 +2280,14 @@ internal static class BitContradictionSolverWithAMultiplier
ulong q = qMask[w]; ulong q = qMask[w];
if (w == words - 1) q &= lastMask; if (w == words - 1) q &= lastMask;
ulong known = oneWin[w] | zeroWin[w]; ulong unknown = oneWin[w] | zeroWin[w];
ulong unk = q & ~known; unknown = q & ~unknown;
if (unk == 0) continue; if (unknown == 0) continue;
int pc = BitOperations.PopCount(unk); found += BitOperations.PopCount(unknown);
found += pc;
if (found > 1) return false; if (found > 1) return false;
int bit = BitOperations.TrailingZeroCount(unk); int bit = BitOperations.TrailingZeroCount(unknown);
offset = (w << 6) + bit; offset = (w << 6) + bit;
} }
@@ -2285,7 +2297,7 @@ internal static class BitContradictionSolverWithAMultiplier
#if DETAILED_LOG #if DETAILED_LOG
private static bool TryProcessBottomUpBlockRowAware( private static bool TryProcessBottomUpBlockRowAware(
ReadOnlySpan<int> qOneOffsets, ReadOnlySpan<int> qOneOffsets,
ulong[] qMaskWords, in ulong[] qMaskWords,
int qWordCount, int qWordCount,
ulong lastWordMask, ulong lastWordMask,
int maxAllowedA, int maxAllowedA,
@@ -2299,6 +2311,7 @@ internal static class BitContradictionSolverWithAMultiplier
ref ulong[] knownZero0, ref ulong[] knownZero0,
ref ulong[] knownZero1, ref ulong[] knownZero1,
int windowSize, int windowSize,
int wordCount,
ulong[] aOneWin, ulong[] aOneWin,
ulong[] aZeroWin, ulong[] aZeroWin,
ref int windowColumn, ref int windowColumn,
@@ -2306,7 +2319,7 @@ internal static class BitContradictionSolverWithAMultiplier
#else #else
private static bool TryProcessBottomUpBlockRowAware( private static bool TryProcessBottomUpBlockRowAware(
ReadOnlySpan<int> qOneOffsets, ReadOnlySpan<int> qOneOffsets,
ulong[] qMaskWords, in ulong[] qMaskWords,
int qWordCount, int qWordCount,
ulong lastWordMask, ulong lastWordMask,
int maxAllowedA, int maxAllowedA,
@@ -2320,6 +2333,7 @@ internal static class BitContradictionSolverWithAMultiplier
ref ulong[] knownZero0, ref ulong[] knownZero0,
ref ulong[] knownZero1, ref ulong[] knownZero1,
int windowSize, int windowSize,
int wordCount,
ulong[] aOneWin, ulong[] aOneWin,
ulong[] aZeroWin, ulong[] aZeroWin,
ref int windowColumn) ref int windowColumn)
@@ -2335,11 +2349,12 @@ internal static class BitContradictionSolverWithAMultiplier
// Ensure window is aligned. // Ensure window is aligned.
ArgumentOutOfRangeException.ThrowIfNotEqual(windowColumn, startColumn); ArgumentOutOfRangeException.ThrowIfNotEqual(windowColumn, startColumn);
int maxOffsetPlusOne = maxOffset + 1;
int qWordCountMinusOne = qWordCount - 1;
while (startColumn < endColumn) while (startColumn < endColumn)
{ {
// Segment rollover based on current column (because we may set aIndex within [startColumn-maxOffset..startColumn]). // Segment rollover based on current column (because we may set aIndex within [startColumn-maxOffset..startColumn]).
int bound = (startColumn / windowSize) * windowSize; int bound = (startColumn / windowSize) * windowSize;
if (startColumn == 0 && segmentBase != 0) throw new Exception("segmentBase must start at 0 for row-aware");
if (bound != segmentBase) if (bound != segmentBase)
{ {
@@ -2347,15 +2362,15 @@ internal static class BitContradictionSolverWithAMultiplier
{ {
temp = knownOne0; knownOne0 = knownOne1; knownOne1 = temp; temp = knownOne0; knownOne0 = knownOne1; knownOne1 = temp;
temp = knownZero0; knownZero0 = knownZero1; knownZero1 = temp; temp = knownZero0; knownZero0 = knownZero1; knownZero1 = temp;
Array.Clear(knownOne0); Array.Clear(knownOne0, 0, wordCount);
Array.Clear(knownZero0); Array.Clear(knownZero0, 0, wordCount);
} }
else else
{ {
Array.Clear(knownOne0); Array.Clear(knownOne0, 0, wordCount);
Array.Clear(knownOne1); Array.Clear(knownOne1, 0, wordCount);
Array.Clear(knownZero0); Array.Clear(knownZero0, 0, wordCount);
Array.Clear(knownZero1); Array.Clear(knownZero1, 0, wordCount);
// Also clear window because it may reference bits outside cache. // Also clear window because it may reference bits outside cache.
needRebuildWindow = true; needRebuildWindow = true;
} }
@@ -2363,23 +2378,36 @@ internal static class BitContradictionSolverWithAMultiplier
segmentBase = bound; segmentBase = bound;
if (needRebuildWindow) if (needRebuildWindow)
{ {
Array.Clear(aOneWin); Array.Clear(aOneWin, 0, qWordCount);
Array.Clear(aZeroWin); Array.Clear(aZeroWin, 0, qWordCount);
// Build window for current startColumn: bit t corresponds to a_{startColumn - t} // Build window for current startColumn: bit t corresponds to a_{startColumn - t}
for (int t = 0; t <= maxOffset; t++) for (int t = 0; t <= maxOffset; t++)
{ {
int aIndex = startColumn - t; int aIndex = startColumn - t;
int st = aIndex < 0 ? 2 : int st = aIndex < 0 || aIndex > maxAllowedA
(aIndex > maxAllowedA ? 2 : ? 2
GetAKnownStateRowAware(aIndex, segmentBase, windowSize, knownOne0, knownZero0, knownOne1, knownZero1)); : GetAKnownStateRowAware(aIndex, segmentBase, windowSize, knownOne0, knownZero0, knownOne1, knownZero1);
if (st == 1) aOneWin[t >> 6] |= 1UL << (t & 63); if (st == 1)
else if (st == 2) aZeroWin[t >> 6] |= 1UL << (t & 63); {
aOneWin[t >> 6] |= 1UL << (t & 63);
}
else if (st == 2)
{
aZeroWin[t >> 6] |= 1UL << (t & 63);
}
} }
if (qWordCount > 0) aOneWin[qWordCount - 1] &= lastWordMask; if (qWordCount > 0)
if (qWordCount > 0) aZeroWin[qWordCount - 1] &= lastWordMask; {
aOneWin[qWordCountMinusOne] &= lastWordMask;
}
if (qWordCount > 0)
{
aZeroWin[qWordCountMinusOne] &= lastWordMask;
}
needRebuildWindow = false; needRebuildWindow = false;
} }
@@ -2389,15 +2417,18 @@ internal static class BitContradictionSolverWithAMultiplier
ulong qLastEff = lastWordMask; ulong qLastEff = lastWordMask;
if (startColumn < maxOffset) if (startColumn < maxOffset)
{ {
GetQPrefixMask(startColumn, maxOffset + 1, qWordCount, out qWordsEff, out ulong prefixLast); GetQPrefixMask(startColumn, maxOffsetPlusOne, qWordCount, out qWordsEff, out qLastEff);
qLastEff = prefixLast;
} }
// StableUnknown batching is valid only if the window contains no known bits in the q-active positions. // StableUnknown batching is valid only if the window contains no known bits in the q-active positions.
if (startColumn >= maxOffset && startColumn <= maxAllowedA) if (startColumn >= maxOffset && startColumn <= maxAllowedA)
{ {
int remainingCols = endColumn - 1; int remainingCols = endColumn - 1;
if (remainingCols > maxAllowedA) remainingCols = maxAllowedA; if (remainingCols > maxAllowedA)
{
remainingCols = maxAllowedA;
}
remainingCols = remainingCols - startColumn + 1; remainingCols = remainingCols - startColumn + 1;
if (remainingCols > 0) if (remainingCols > 0)
{ {
@@ -2409,10 +2440,9 @@ internal static class BitContradictionSolverWithAMultiplier
_statBatchCols += remainingCols; _statBatchCols += remainingCols;
#endif #endif
int rem = remainingCols; while (remainingCols > 0)
while (rem > 0)
{ {
int step = rem > TailCarryBatchColumns ? TailCarryBatchColumns : rem; int step = remainingCols > TailCarryBatchColumns ? TailCarryBatchColumns : remainingCols;
#if DETAILED_LOG #if DETAILED_LOG
if (!TryAdvanceStableUnknown(ref carry, stableUnknown, step, out var propagateReason)) if (!TryAdvanceStableUnknown(ref carry, stableUnknown, step, out var propagateReason))
#else #else
@@ -2427,12 +2457,12 @@ internal static class BitContradictionSolverWithAMultiplier
startColumn += step; startColumn += step;
windowColumn = startColumn; windowColumn = startColumn;
rem -= step; remainingCols -= step;
} }
// Reset window to unknown (no known bits) because we skipped updating it across columns. // Reset window to unknown (no known bits) because we skipped updating it across columns.
Array.Clear(aOneWin); Array.Clear(aOneWin, 0, qWordCount);
Array.Clear(aZeroWin); Array.Clear(aZeroWin, 0, qWordCount);
continue; continue;
} }
@@ -2451,7 +2481,7 @@ internal static class BitContradictionSolverWithAMultiplier
// if (qWordCount > 0) aZeroWin[qWordCount - 1] &= lastWordMask; // if (qWordCount > 0) aZeroWin[qWordCount - 1] &= lastWordMask;
// } // }
ComputeForcedRemainingAndClean_Prefix(qMaskWords, aOneWin, aZeroWin, qWordsEff, qLastEff, out int forced, out int remaining, out _); ComputeForcedRemainingAndClean_Prefix(qMaskWords, aOneWin, aZeroWin, qWordsEff, qLastEff, out int forced, out int remaining);
// If exactly one unknown contribution among q=1 positions AND carry parity is fixed, // If exactly one unknown contribution among q=1 positions AND carry parity is fixed,
// we can force the corresponding a bit to satisfy the required result bit parity. // we can force the corresponding a bit to satisfy the required result bit parity.
@@ -2469,6 +2499,7 @@ internal static class BitContradictionSolverWithAMultiplier
int x = (requiredBit ^ carryParity ^ (forced & 1)) & 1; int x = (requiredBit ^ carryParity ^ (forced & 1)) & 1;
bool needOne = x != 0; bool needOne = x != 0;
int w, b;
// Check contradiction with already-known value. // Check contradiction with already-known value.
int state = GetAKnownStateRowAware(aIndex, segmentBase, windowSize, knownOne0, knownZero0, knownOne1, knownZero1); int state = GetAKnownStateRowAware(aIndex, segmentBase, windowSize, knownOne0, knownZero0, knownOne1, knownZero1);
if (needOne) if (needOne)
@@ -2484,9 +2515,9 @@ internal static class BitContradictionSolverWithAMultiplier
SetAKnownRowAware(aIndex, true, segmentBase, windowSize, knownOne0, knownZero0, knownOne1, knownZero1); SetAKnownRowAware(aIndex, true, segmentBase, windowSize, knownOne0, knownZero0, knownOne1, knownZero1);
// Reflect in window at offset t // Reflect in window at offset t
int w = t >> 6; int b = t & 63; w = t >> 6;
b = t & 63;
aOneWin[w] |= 1UL << b; aOneWin[w] |= 1UL << b;
forced += 1; forced += 1;
} }
else else
@@ -2501,12 +2532,16 @@ internal static class BitContradictionSolverWithAMultiplier
SetAKnownRowAware(aIndex, false, segmentBase, windowSize, knownOne0, knownZero0, knownOne1, knownZero1); SetAKnownRowAware(aIndex, false, segmentBase, windowSize, knownOne0, knownZero0, knownOne1, knownZero1);
int w = t >> 6; int b = t & 63; w = t >> 6;
b = t & 63;
aZeroWin[w] |= 1UL << b; aZeroWin[w] |= 1UL << b;
} }
remaining = 0; remaining = 0;
if (aIndex > maxKnownA) maxKnownA = aIndex; if (aIndex > maxKnownA)
{
maxKnownA = aIndex;
}
} }
} }
@@ -2522,20 +2557,27 @@ internal static class BitContradictionSolverWithAMultiplier
// Advance to next column: shift window, then insert status of a_{startColumn+1} into bit0. // Advance to next column: shift window, then insert status of a_{startColumn+1} into bit0.
int nextA = startColumn + 1; int nextA = startColumn + 1;
ShiftLeft1InPlace(aOneWin, qWordCount, lastWordMask); ShiftLeft1InPlace(aOneWin, aZeroWin, qWordCount, lastWordMask);
ShiftLeft1InPlace(aZeroWin, qWordCount, lastWordMask);
// Insert bit0 for nextA // Insert bit0 for nextA
int nextState; int nextState;
if (nextA > maxAllowedA) if (nextA > maxAllowedA)
{
nextState = 2; nextState = 2;
}
else else
{
nextState = GetAKnownStateRowAware(nextA, segmentBase, windowSize, knownOne0, knownZero0, knownOne1, knownZero1); nextState = GetAKnownStateRowAware(nextA, segmentBase, windowSize, knownOne0, knownZero0, knownOne1, knownZero1);
}
if (nextState == 1) if (nextState == 1)
{
aOneWin[0] |= 1UL; aOneWin[0] |= 1UL;
}
else if (nextState == 2) else if (nextState == 2)
{
aZeroWin[0] |= 1UL; aZeroWin[0] |= 1UL;
}
startColumn++; startColumn++;
windowColumn = startColumn; windowColumn = startColumn;
@@ -2928,7 +2970,6 @@ internal static class BitContradictionSolverWithAMultiplier
} }
top0KnownZero = true; top0KnownZero = true;
UpdateZeroTailFromTop(ref zeroTailStart, topIndex0, topIndex1, top0KnownZero, top1KnownZero);
} }
else if (chosenIndex == topIndex1) else if (chosenIndex == topIndex1)
{ {
@@ -2941,15 +2982,16 @@ internal static class BitContradictionSolverWithAMultiplier
} }
top1KnownZero = true; top1KnownZero = true;
UpdateZeroTailFromTop(ref zeroTailStart, topIndex0, topIndex1, top0KnownZero, top1KnownZero);
} }
UpdateZeroTailFromTop(ref zeroTailStart, topIndex0, topIndex1, top0KnownZero, top1KnownZero);
} }
} }
// if (topChanged) // if (topChanged)
// { // {
// var qHash = ComputeQOffsetsHash(qOneOffsets); // var qHash = ComputeQOffsetsHash(qOneOffsets);
// if (!TryDynamicModularRangePrune(qOneOffsets, qHash, prime, maxAllowedA, top0KnownOne, top0KnownZero, top1KnownOne, top1KnownZero, zeroTailStart)) // if (!TryDynamicModularRangePrune(qOneOffsets, qHash, prime, maxAllowedA, top0KnownOne, top0KnownZero, top1KnownOne, zeroTailStart))
// { // {
// #if DETAILED_LOG // #if DETAILED_LOG
// reason = ContradictionReason.ParityUnreachable; // reason = ContradictionReason.ParityUnreachable;
@@ -3429,10 +3471,8 @@ internal static class BitContradictionSolverWithAMultiplier
start = 0; start = 0;
} }
int lower = LowerBound(qOneOffsets, (int)start); FindBounds(qOneOffsets, (int)start, (int)column, out int lower, out int upper);
int upper = UpperBound(qOneOffsets, (int)column); return upper - lower >= TopDownBorrowMinUnknown;
int unknownTop = upper - lower;
return unknownTop >= TopDownBorrowMinUnknown;
} }
private static bool AdvanceTopDownUnknown1(ref long carryOutMin, ref long carryOutMax, int steps) private static bool AdvanceTopDownUnknown1(ref long carryOutMin, ref long carryOutMax, int steps)
@@ -3486,30 +3526,32 @@ internal static class BitContradictionSolverWithAMultiplier
return 0; return 0;
} }
int end = start + length - 1; length += start - 1;
int firstWord = start >> 6; int firstWord = start >> 6;
int lastWord = end >> 6; int lastWord = length >> 6;
int startBit = start & 63; start &= 63;
int endBit = end & 63; length &= 63;
ulong mask;
if (firstWord == lastWord) if (firstWord == lastWord)
{ {
ulong mask = (endBit == 63 ? ulong.MaxValue : ((1UL << (endBit + 1)) - 1UL)) & (ulong.MaxValue << startBit); mask = (length == 63 ? ulong.MaxValue : ((1UL << (length + 1)) - 1UL)) & (ulong.MaxValue << start);
return BitOperations.PopCount(bits[firstWord] & mask); return BitOperations.PopCount(bits[firstWord] & mask);
} }
int count = 0; mask = ulong.MaxValue << start;
ulong firstMask = ulong.MaxValue << startBit; // start is count from here. We're reusing the variable to limit registry pressure.
count += BitOperations.PopCount(bits[firstWord] & firstMask); start = BitOperations.PopCount(bits[firstWord] & mask);
for (int word = firstWord + 1; word < lastWord; word++) firstWord++;
for (; firstWord < lastWord; firstWord++)
{ {
count += BitOperations.PopCount(bits[word]); start += BitOperations.PopCount(bits[firstWord]);
} }
ulong lastMask = endBit == 63 ? ulong.MaxValue : ((1UL << (endBit + 1)) - 1UL); mask = length == 63 ? ulong.MaxValue : ((1UL << (length + 1)) - 1UL);
count += BitOperations.PopCount(bits[lastWord] & lastMask); start += BitOperations.PopCount(bits[lastWord] & mask);
return count; return start;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]