Improvements to recognize cases when we only find trivial divisors for p. Not fully fixed, yet.
This commit is contained in:
1 parent
0d11199ef2
commit
f3ed5b04a1
3 files changed
+185
-5
No files matched your search
@@ -127,8 +127,15 @@ internal static class Program
|
|||||||
throw new FormatException($"Invalid divisor value '{parts[1]}' for p={p} in {path}.");
|
throw new FormatException($"Invalid divisor value '{parts[1]}' for p={p} in {path}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsProperMersenneDivisor(p, divisor))
|
||||||
|
{
|
||||||
result[p] = divisor;
|
result[p] = divisor;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"Skipping invalid divisor entry p={p}, divisor={divisor} (not a proper divisor of 2^{p}-1 or fails powmod check).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -186,6 +193,45 @@ internal static class Program
|
|||||||
return k;
|
return k;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsProperMersenneDivisor(long p, in BigInteger divisor)
|
||||||
|
{
|
||||||
|
if (p <= 1 || divisor <= BigInteger.One)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bitLength = GetBitLength(divisor);
|
||||||
|
if (bitLength > p)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitLength == p && IsPowerOfTwo(divisor + BigInteger.One))
|
||||||
|
{
|
||||||
|
// Divisor equals the Mersenne number itself.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BigInteger.ModPow(2, p, divisor) == BigInteger.One;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetBitLength(in BigInteger value)
|
||||||
|
{
|
||||||
|
if (value.IsZero)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] bytes = value.ToByteArray(isUnsigned: true, isBigEndian: false);
|
||||||
|
byte msb = bytes[^1];
|
||||||
|
return ((bytes.Length - 1) * 8) + (BitOperations.Log2(msb) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsPowerOfTwo(in BigInteger value)
|
||||||
|
{
|
||||||
|
return value > BigInteger.One && (value & (value - BigInteger.One)) == BigInteger.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
private static void WriteOutput(IReadOnlyCollection<Transition> transitions)
|
private static void WriteOutput(IReadOnlyCollection<Transition> transitions)
|
||||||
{
|
{
|
||||||
using var writer = new StreamWriter(OutputFileName, false, Encoding.UTF8);
|
using var writer = new StreamWriter(OutputFileName, false, Encoding.UTF8);
|
||||||
|
|||||||
@@ -68,6 +68,128 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
private (int Start, int End)[] _preparedSpecialRanges = Array.Empty<(int Start, int End)>();
|
private (int Start, int End)[] _preparedSpecialRanges = Array.Empty<(int Start, int End)>();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static bool IsMersenneValue(ulong prime, in BigInteger divisor)
|
||||||
|
{
|
||||||
|
if (divisor <= BigInteger.One || prime == 0UL || prime > int.MaxValue)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int divisorBitLength = GetBitLengthPortable(divisor);
|
||||||
|
if (divisorBitLength != (int)prime)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger plusOne = divisor + BigInteger.One;
|
||||||
|
return plusOne > BigInteger.One && IsPowerOfTwoBig(plusOne);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static int GetBitLengthPortable(in BigInteger value)
|
||||||
|
{
|
||||||
|
if (value.IsZero)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] bytes = value.ToByteArray(isUnsigned: true, isBigEndian: false);
|
||||||
|
byte msb = bytes[^1];
|
||||||
|
return ((bytes.Length - 1) * 8) + (BitOperations.Log2(msb) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static bool IsPowerOfTwoBig(in BigInteger value)
|
||||||
|
{
|
||||||
|
return value > BigInteger.One && (value & (value - BigInteger.One)) == BigInteger.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static bool TryGetMersenneStopK(ulong prime, in BigInteger step, out BigInteger stopK, out BigInteger tailStartK)
|
||||||
|
{
|
||||||
|
stopK = BigInteger.Zero;
|
||||||
|
tailStartK = BigInteger.Zero;
|
||||||
|
|
||||||
|
if (prime == 0UL || prime > int.MaxValue)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bits;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bits = checked((int)prime);
|
||||||
|
}
|
||||||
|
catch (OverflowException)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger mersenne;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mersenne = (BigInteger.One << bits) - BigInteger.One;
|
||||||
|
}
|
||||||
|
catch (OutOfMemoryException)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
BigInteger denominator = step;
|
||||||
|
if (denominator <= BigInteger.Zero)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopK = (mersenne - BigInteger.One) / denominator;
|
||||||
|
if (stopK < BigInteger.One)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tailCount = PerfectNumberConstants.ByDivisorMersenneTailCount;
|
||||||
|
if (tailCount < 0)
|
||||||
|
{
|
||||||
|
tailCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger tail = stopK - tailCount;
|
||||||
|
tailStartK = tail > BigInteger.One ? tail : BigInteger.One;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static bool TryClampMaxKForMersenneTail(ulong prime, in BigInteger step, ref BigInteger maxKAllowed)
|
||||||
|
{
|
||||||
|
if (maxKAllowed <= BigInteger.Zero)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryGetMersenneStopK(prime, step, out BigInteger stopK, out BigInteger tailStartK))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxKAllowed < tailStartK)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger cap = stopK - BigInteger.One;
|
||||||
|
if (cap < BigInteger.One)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxKAllowed > cap)
|
||||||
|
{
|
||||||
|
maxKAllowed = cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public void ResetStateTracking()
|
public void ResetStateTracking()
|
||||||
{
|
{
|
||||||
@@ -288,6 +410,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
|
|
||||||
BigInteger normalizedMinK = minK >= BigInteger.One ? minK : BigInteger.One;
|
BigInteger normalizedMinK = minK >= BigInteger.One ? minK : BigInteger.One;
|
||||||
BigInteger maxKAllowed = (allowedMax - BigInteger.One) / step;
|
BigInteger maxKAllowed = (allowedMax - BigInteger.One) / step;
|
||||||
|
TryClampMaxKForMersenneTail(prime, step, ref maxKAllowed);
|
||||||
if (maxKAllowed < normalizedMinK)
|
if (maxKAllowed < normalizedMinK)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -455,6 +578,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
processedAll = true;
|
processedAll = true;
|
||||||
BigInteger stepBig = step64;
|
BigInteger stepBig = step64;
|
||||||
BigInteger maxKAllowed = allowedMax == 0UL ? BigInteger.Zero : (((BigInteger)allowedMax - BigInteger.One) / stepBig);
|
BigInteger maxKAllowed = allowedMax == 0UL ? BigInteger.Zero : (((BigInteger)allowedMax - BigInteger.One) / stepBig);
|
||||||
|
TryClampMaxKForMersenneTail(prime, stepBig, ref maxKAllowed);
|
||||||
|
|
||||||
ReadOnlySpan<int> specialPercents = SpecialPercentPromille;
|
ReadOnlySpan<int> specialPercents = SpecialPercentPromille;
|
||||||
ReadOnlySpan<(int Start, int End)> groups = PercentGroupsPromille;
|
ReadOnlySpan<(int Start, int End)> groups = PercentGroupsPromille;
|
||||||
@@ -760,7 +884,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
}
|
}
|
||||||
|
|
||||||
RecordState(currentK);
|
RecordState(currentK);
|
||||||
if (divisorCycle == prime)
|
if (divisorCycle == prime && !IsMersenneValue(prime, candidate))
|
||||||
{
|
{
|
||||||
// A cycle equal to the tested exponent (which is prime in this path) guarantees that the candidate divides
|
// A cycle equal to the tested exponent (which is prime in this path) guarantees that the candidate divides
|
||||||
// the corresponding Mersenne number because the order of 2 modulo the divisor is exactly p.
|
// the corresponding Mersenne number because the order of 2 modulo the divisor is exactly p.
|
||||||
@@ -1320,7 +1444,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
BigInteger divisorBig = (BigInteger)divisor;
|
BigInteger divisorBig = (BigInteger)divisor;
|
||||||
RecordState((BigInteger)k);
|
RecordState((BigInteger)k);
|
||||||
BigInteger powResult = BigInteger.ModPow(2, prime, divisorBig);
|
BigInteger powResult = BigInteger.ModPow(2, prime, divisorBig);
|
||||||
if (powResult.IsOne)
|
if (powResult.IsOne && !IsMersenneValue(prime, divisorBig))
|
||||||
{
|
{
|
||||||
foundDivisor = divisorBig;
|
foundDivisor = divisorBig;
|
||||||
return true;
|
return true;
|
||||||
@@ -1364,6 +1488,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
}
|
}
|
||||||
|
|
||||||
BigInteger maxKAllowed = (allowedMax - BigInteger.One) / step;
|
BigInteger maxKAllowed = (allowedMax - BigInteger.One) / step;
|
||||||
|
TryClampMaxKForMersenneTail(prime, step, ref maxKAllowed);
|
||||||
if (currentK > maxKAllowed)
|
if (currentK > maxKAllowed)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -1453,7 +1578,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
{
|
{
|
||||||
RecordState(currentK);
|
RecordState(currentK);
|
||||||
BigInteger powResult = BigInteger.ModPow(BigIntegerNumbers.Two, primeBig, divisor);
|
BigInteger powResult = BigInteger.ModPow(BigIntegerNumbers.Two, primeBig, divisor);
|
||||||
if (powResult.IsOne)
|
if (powResult.IsOne && !IsMersenneValue(prime, divisor))
|
||||||
{
|
{
|
||||||
foundDivisor = divisor;
|
foundDivisor = divisor;
|
||||||
processedAll = true;
|
processedAll = true;
|
||||||
@@ -1498,6 +1623,13 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
}
|
}
|
||||||
|
|
||||||
ulong maxKAllowed = (allowedMax - 1UL) / step;
|
ulong maxKAllowed = (allowedMax - 1UL) / step;
|
||||||
|
{
|
||||||
|
BigInteger maxKBig = maxKAllowed;
|
||||||
|
if (TryClampMaxKForMersenneTail(prime, (BigInteger)step, ref maxKBig))
|
||||||
|
{
|
||||||
|
maxKAllowed = maxKBig > ulong.MaxValue ? ulong.MaxValue : (ulong)maxKBig;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (endK > maxKAllowed)
|
if (endK > maxKAllowed)
|
||||||
{
|
{
|
||||||
endK = maxKAllowed;
|
endK = maxKAllowed;
|
||||||
@@ -1642,7 +1774,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
}
|
}
|
||||||
|
|
||||||
RecordState(currentK);
|
RecordState(currentK);
|
||||||
if (divisorCycle == prime)
|
if (divisorCycle == prime && !IsMersenneValue(prime, divisor))
|
||||||
{
|
{
|
||||||
foundDivisor = divisor;
|
foundDivisor = divisor;
|
||||||
return true;
|
return true;
|
||||||
@@ -2120,7 +2252,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
|
|||||||
{
|
{
|
||||||
RecordState(currentK, phase);
|
RecordState(currentK, phase);
|
||||||
BigInteger powResult = BigInteger.ModPow(2, prime, divisor);
|
BigInteger powResult = BigInteger.ModPow(2, prime, divisor);
|
||||||
if (powResult.IsOne)
|
if (powResult.IsOne && !IsMersenneValue(prime, divisor))
|
||||||
{
|
{
|
||||||
foundDivisor = divisor;
|
foundDivisor = divisor;
|
||||||
processedAll = true;
|
processedAll = true;
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ public static class PerfectNumberConstants
|
|||||||
public const int ByDivisorStateSaveInterval = 200_000;
|
public const int ByDivisorStateSaveInterval = 200_000;
|
||||||
public const string ByDivisorStateDirectory = "Checks";
|
public const string ByDivisorStateDirectory = "Checks";
|
||||||
public const int PollardRhoMilliseconds = 100;
|
public const int PollardRhoMilliseconds = 100;
|
||||||
|
public const int ByDivisorMersenneTailCount = 8;
|
||||||
|
public const int ByDivisorMersenneBitLimit = 4096;
|
||||||
// TODO: Load these limits from the benchmark-driven configuration so CPU and GPU scans stay aligned with the optimal
|
// TODO: Load these limits from the benchmark-driven configuration so CPU and GPU scans stay aligned with the optimal
|
||||||
// divisor-cycle datasets we generate offline.
|
// divisor-cycle datasets we generate offline.
|
||||||
// TODO: Promote these magic numbers into a runtime profile derived from EvenPerfectBitScanner.Benchmarks so we can retune
|
// TODO: Promote these magic numbers into a runtime profile derived from EvenPerfectBitScanner.Benchmarks so we can retune
|
||||||
|
|||||||
Reference in new issue
Block a user