BitContradiction performance improvements. Exact powers of 2 cases will reuse previous calculation results and transform them for the next q divisor.
This commit is contained in:
1 parent
b014fdb7f1
commit
b2a6a1c332
2 files changed
+137
-26
No files matched your search
@@ -14,6 +14,14 @@ internal sealed class PrivateWorkSet
|
|||||||
public ulong[] QMaskWords = [];
|
public ulong[] QMaskWords = [];
|
||||||
public ulong[] AOneWin = [];
|
public ulong[] AOneWin = [];
|
||||||
public ulong[] AZeroWin = [];
|
public ulong[] AZeroWin = [];
|
||||||
|
public int[] PrevOffsets = [];
|
||||||
|
public int PrevOffsetsLength;
|
||||||
|
public bool HasPrevOffsets;
|
||||||
|
public BigInteger LastInvMod2k = BigInteger.Zero;
|
||||||
|
public bool HasLastInvMod2k;
|
||||||
|
public BigInteger LastPow2QMod2k = BigInteger.Zero;
|
||||||
|
public BigInteger LastPow2K = BigInteger.Zero;
|
||||||
|
public bool HasLastPow2QMod2k;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void EnsureCapacity(int wordCount, int qWordCount)
|
public void EnsureCapacity(int wordCount, int qWordCount)
|
||||||
@@ -1288,12 +1296,16 @@ internal static class BitContradictionSolverWithAMultiplier
|
|||||||
internal static bool TryCheckDivisibilityFromOneOffsets(
|
internal static bool TryCheckDivisibilityFromOneOffsets(
|
||||||
ReadOnlySpan<int> qOneOffsets,
|
ReadOnlySpan<int> qOneOffsets,
|
||||||
ulong p,
|
ulong p,
|
||||||
|
BigInteger k,
|
||||||
|
bool isPowerOfTwo,
|
||||||
out bool divides,
|
out bool divides,
|
||||||
out ContradictionReason reason)
|
out ContradictionReason reason)
|
||||||
#else
|
#else
|
||||||
internal static bool TryCheckDivisibilityFromOneOffsets(
|
internal static bool TryCheckDivisibilityFromOneOffsets(
|
||||||
ReadOnlySpan<int> qOneOffsets,
|
ReadOnlySpan<int> qOneOffsets,
|
||||||
ulong p,
|
ulong p,
|
||||||
|
BigInteger k,
|
||||||
|
bool isPowerOfTwo,
|
||||||
out bool divides)
|
out bool divides)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
@@ -1397,37 +1409,84 @@ internal static class BitContradictionSolverWithAMultiplier
|
|||||||
BigInteger mask = Pow2Provider.BigIntegerMinusOne(ForcedBits);
|
BigInteger mask = Pow2Provider.BigIntegerMinusOne(ForcedBits);
|
||||||
|
|
||||||
// Build q (low 1024 bits) from qOneOffsets. q is small anyway, but do it generically.
|
// Build q (low 1024 bits) from qOneOffsets. q is small anyway, but do it generically.
|
||||||
BigInteger qMod2k = BigInteger.Zero;
|
BigInteger qMod2k;
|
||||||
|
bool usedPow2Transform = false;
|
||||||
|
if (isPowerOfTwo && workSet.HasLastPow2QMod2k && workSet.LastPow2K != BigInteger.Zero && k == (workSet.LastPow2K << 1))
|
||||||
|
{
|
||||||
|
qMod2k = ((workSet.LastPow2QMod2k << 1) - BigInteger.One) & mask;
|
||||||
|
usedPow2Transform = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qMod2k = BigInteger.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
int i, t;
|
int i, t;
|
||||||
for (i = 0; i < qOneOffsetsLength; i++)
|
for (i = 0; i < qOneOffsetsLength; i++)
|
||||||
{
|
{
|
||||||
t = qOneOffsets[i];
|
t = qOneOffsets[i];
|
||||||
|
if (!usedPow2Transform)
|
||||||
|
{
|
||||||
qMod2k |= Pow2Provider.BigInteger(t);
|
qMod2k |= Pow2Provider.BigInteger(t);
|
||||||
|
}
|
||||||
qMaskWords[t >> 6] |= 1UL << (t & 63);
|
qMaskWords[t >> 6] |= 1UL << (t & 63);
|
||||||
}
|
}
|
||||||
|
|
||||||
// q must be odd for any Mersenne divisor candidate, and we always generate odd q candidates. No need to check it.
|
if (!usedPow2Transform)
|
||||||
// if (qMod2k.IsEven)
|
|
||||||
// {
|
|
||||||
// Console.WriteLine("Pruned by qMod2k.IsEven");
|
|
||||||
// #if DETAILED_LOG
|
|
||||||
// reason = ContradictionReason.ParityUnreachable;
|
|
||||||
// #endif
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Compute inv = q^{-1} mod 2^1024 using Hensel/Newton iteration in Z/2^kZ.
|
|
||||||
BigInteger aLow = qMod2k; // OK seed for odd q.
|
|
||||||
|
|
||||||
// maxKnownA stands for bits here. We're reusing the variable to limit registry pressure.
|
|
||||||
int maxKnownA;
|
|
||||||
for (maxKnownA = 1; maxKnownA < ForcedBits; maxKnownA <<= 1)
|
|
||||||
{
|
{
|
||||||
aLow = (aLow * (BigIntegerNumbers.Two - qMod2k * aLow)) & mask;
|
qMod2k &= mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compute inv = q^{-1} mod 2^1024 using Hensel/Newton iteration in Z/2^kZ.
|
||||||
|
BigInteger invSeed;
|
||||||
|
int maxKnownA;
|
||||||
|
if (workSet.HasLastInvMod2k)
|
||||||
|
{
|
||||||
|
invSeed = workSet.LastInvMod2k;
|
||||||
|
for (int iter = 0; iter < 2; iter++)
|
||||||
|
{
|
||||||
|
invSeed = (invSeed * (BigIntegerNumbers.Two - qMod2k * invSeed)) & mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (((qMod2k * invSeed) & mask) != BigInteger.One)
|
||||||
|
{
|
||||||
|
invSeed = qMod2k;
|
||||||
|
for (maxKnownA = 1; maxKnownA < ForcedBits; maxKnownA <<= 1)
|
||||||
|
{
|
||||||
|
invSeed = (invSeed * (BigIntegerNumbers.Two - qMod2k * invSeed)) & mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
invSeed = qMod2k;
|
||||||
|
for (maxKnownA = 1; maxKnownA < ForcedBits; maxKnownA <<= 1)
|
||||||
|
{
|
||||||
|
invSeed = (invSeed * (BigIntegerNumbers.Two - qMod2k * invSeed)) & mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger aLow = (-invSeed) & mask;
|
||||||
|
|
||||||
|
if (isPowerOfTwo)
|
||||||
|
{
|
||||||
|
workSet.LastPow2QMod2k = qMod2k;
|
||||||
|
workSet.LastPow2K = k;
|
||||||
|
workSet.HasLastPow2QMod2k = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
workSet.LastInvMod2k = invSeed;
|
||||||
|
workSet.HasLastInvMod2k = true;
|
||||||
|
if (workSet.PrevOffsets.Length < qOneOffsetsLength)
|
||||||
|
{
|
||||||
|
Array.Resize(ref workSet.PrevOffsets, qOneOffsetsLength);
|
||||||
|
}
|
||||||
|
qOneOffsets.CopyTo(workSet.PrevOffsets);
|
||||||
|
workSet.PrevOffsetsLength = qOneOffsetsLength;
|
||||||
|
workSet.HasPrevOffsets = true;
|
||||||
|
|
||||||
// a ≡ -aLow (mod 2^512)
|
// a ≡ -aLow (mod 2^512)
|
||||||
aLow = (-aLow) & mask;
|
|
||||||
|
|
||||||
// Apply constraints for bits i in [0..min(511, maxAllowedA)] into knownOne0/knownZero0.
|
// Apply constraints for bits i in [0..min(511, maxAllowedA)] into knownOne0/knownZero0.
|
||||||
// NOTE: now windowSize >= 512, so wordCount is enough to store these bits.
|
// NOTE: now windowSize >= 512, so wordCount is enough to store these bits.
|
||||||
@@ -1731,7 +1790,13 @@ internal static class BitContradictionSolverWithAMultiplier
|
|||||||
internal static bool TryCheckDivisibilityFromOneOffsets(
|
internal static bool TryCheckDivisibilityFromOneOffsets(
|
||||||
ReadOnlySpan<int> qOneOffsets,
|
ReadOnlySpan<int> qOneOffsets,
|
||||||
ulong p,
|
ulong p,
|
||||||
out bool divides) => TryCheckDivisibilityFromOneOffsets(qOneOffsets, p, out divides, out _);
|
out bool divides) => TryCheckDivisibilityFromOneOffsets(qOneOffsets, p, BigInteger.Zero, false, out divides, out _);
|
||||||
|
#endif
|
||||||
|
#if !DETAILED_LOG
|
||||||
|
internal static bool TryCheckDivisibilityFromOneOffsets(
|
||||||
|
ReadOnlySpan<int> qOneOffsets,
|
||||||
|
ulong p,
|
||||||
|
out bool divides) => TryCheckDivisibilityFromOneOffsets(qOneOffsets, p, BigInteger.Zero, false, out divides);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|||||||
@@ -1409,7 +1409,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
computedCycle = prime;
|
computedCycle = prime;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
decided = TryExactBitTreeCheck(computedCycle, divisor, out divides);
|
decided = TryExactBitTreeCheck(computedCycle, divisor, currentK, out divides);
|
||||||
if (!decided)
|
if (!decided)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"BitTree check did not decide for p={prime}, divisor={divisor} (k={currentK}).");
|
throw new InvalidOperationException($"BitTree check did not decide for p={prime}, divisor={divisor} (k={currentK}).");
|
||||||
@@ -1437,7 +1437,7 @@ 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, BigInteger currentK, out bool divides)
|
||||||
{
|
{
|
||||||
#if DETAILED_LOG
|
#if DETAILED_LOG
|
||||||
Stopwatch sw = Stopwatch.StartNew();
|
Stopwatch sw = Stopwatch.StartNew();
|
||||||
@@ -4617,7 +4617,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
|
|
||||||
sw.Reset();
|
sw.Reset();
|
||||||
sw.Start();
|
sw.Start();
|
||||||
bool decided = TryExactBitContradictionCheck(prime, divisor, out divides);
|
bool decided = TryExactBitContradictionCheck(prime, divisor, currentK, out divides);
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
if (!decided)
|
if (!decided)
|
||||||
{
|
{
|
||||||
@@ -4711,9 +4711,17 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
|
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static int[]? _qBits;
|
private static int[]? _qBits;
|
||||||
|
[ThreadStatic]
|
||||||
|
private static int[]? _pow2Offsets;
|
||||||
|
[ThreadStatic]
|
||||||
|
private static int _pow2OffsetCount;
|
||||||
|
[ThreadStatic]
|
||||||
|
private static BigInteger _pow2K;
|
||||||
|
[ThreadStatic]
|
||||||
|
private static bool _hasPow2K;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static bool TryExactBitContradictionCheck(ulong prime, BigInteger divisor, out bool divides)
|
private static bool TryExactBitContradictionCheck(ulong prime, BigInteger divisor, BigInteger currentK, out bool divides)
|
||||||
{
|
{
|
||||||
divides = false;
|
divides = false;
|
||||||
// const ulong DebugTopDownDivisor = 1209708008767UL;
|
// const ulong DebugTopDownDivisor = 1209708008767UL;
|
||||||
@@ -4774,6 +4782,29 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
int[] oneOffsets = _qBits!;
|
int[] oneOffsets = _qBits!;
|
||||||
// Span<int> oneOffsets = stackalloc int[qBitLength];
|
// Span<int> oneOffsets = stackalloc int[qBitLength];
|
||||||
int oneCount = 0;
|
int oneCount = 0;
|
||||||
|
bool isPowerOfTwo = currentK.IsPowerOfTwo;
|
||||||
|
bool usedPow2Offsets = false;
|
||||||
|
int[]? pow2Offsets = _pow2Offsets;
|
||||||
|
int pow2OffsetCount = _pow2OffsetCount;
|
||||||
|
BigInteger pow2K = _pow2K;
|
||||||
|
bool hasPow2K = _hasPow2K;
|
||||||
|
if (isPowerOfTwo && hasPow2K && pow2Offsets != null && pow2OffsetCount > 0 && currentK == (pow2K << 1))
|
||||||
|
{
|
||||||
|
int needed = pow2OffsetCount + 1;
|
||||||
|
if (needed <= oneOffsets.Length)
|
||||||
|
{
|
||||||
|
oneOffsets[0] = 0;
|
||||||
|
for (int i = 0; i < pow2OffsetCount; i++)
|
||||||
|
{
|
||||||
|
oneOffsets[i + 1] = pow2Offsets[i] + 1;
|
||||||
|
}
|
||||||
|
oneCount = needed;
|
||||||
|
usedPow2Offsets = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!usedPow2Offsets)
|
||||||
|
{
|
||||||
for (int i = 0; i < qBitLength; i++)
|
for (int i = 0; i < qBitLength; i++)
|
||||||
{
|
{
|
||||||
if (((divisor >> i) & BigInteger.One) == BigInteger.One)
|
if (((divisor >> i) & BigInteger.One) == BigInteger.One)
|
||||||
@@ -4781,6 +4812,21 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
oneOffsets[oneCount++] = i;
|
oneOffsets[oneCount++] = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPowerOfTwo)
|
||||||
|
{
|
||||||
|
int[]? pow2OffsetsTarget = _pow2Offsets;
|
||||||
|
if (pow2OffsetsTarget == null || pow2OffsetsTarget.Length < oneCount)
|
||||||
|
{
|
||||||
|
pow2OffsetsTarget = new int[oneCount];
|
||||||
|
_pow2Offsets = pow2OffsetsTarget;
|
||||||
|
}
|
||||||
|
Array.Copy(oneOffsets, pow2OffsetsTarget, oneCount);
|
||||||
|
_pow2OffsetCount = oneCount;
|
||||||
|
_pow2K = currentK;
|
||||||
|
_hasPow2K = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (oneCount == 0)
|
if (oneCount == 0)
|
||||||
{
|
{
|
||||||
@@ -4791,9 +4837,9 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
ReadOnlySpan<int> oneOffsetsSlice = oneOffsets[..oneCount];
|
ReadOnlySpan<int> oneOffsetsSlice = oneOffsets[..oneCount];
|
||||||
// BitContradictionSolverWithQMultiplier.SetDebugEnabled(true);
|
// BitContradictionSolverWithQMultiplier.SetDebugEnabled(true);
|
||||||
#if DETAILED_LOG
|
#if DETAILED_LOG
|
||||||
bool decided = BitContradictionSolverWithAMultiplier.TryCheckDivisibilityFromOneOffsets(oneOffsetsSlice, prime, out divides, out var reason);
|
bool decided = BitContradictionSolverWithAMultiplier.TryCheckDivisibilityFromOneOffsets(oneOffsetsSlice, prime, currentK, isPowerOfTwo, out divides, out var reason);
|
||||||
#else
|
#else
|
||||||
bool decided = BitContradictionSolverWithAMultiplier.TryCheckDivisibilityFromOneOffsets(oneOffsetsSlice, prime, out divides);
|
bool decided = BitContradictionSolverWithAMultiplier.TryCheckDivisibilityFromOneOffsets(oneOffsetsSlice, prime, currentK, isPowerOfTwo, out divides);
|
||||||
#endif
|
#endif
|
||||||
// BitContradictionSolverWithQMultiplier.SetDebugEnabled(false);
|
// BitContradictionSolverWithQMultiplier.SetDebugEnabled(false);
|
||||||
if (decided && divides)
|
if (decided && divides)
|
||||||
|
|||||||
Reference in new issue
Block a user