Pools made thread static. Various small optimizations. "in" parameter added to read-only method parameters
This commit is contained in:
1 parent
7c71d5165e
commit
d3695a3dc3
10 files changed
+1712
-1774
No files matched your search
@@ -70,7 +70,7 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
|||||||
return new DummySession();
|
return new DummySession();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PrepareCandidates(ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues)
|
public void PrepareCandidates(in ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < primes.Length; i++)
|
for (int i = 0; i < primes.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -80,7 +80,7 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
|||||||
|
|
||||||
private sealed class DummySession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession
|
private sealed class DummySession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession
|
||||||
{
|
{
|
||||||
public void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, ReadOnlySpan<ulong> primes, Span<byte> hits)
|
public void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, in ReadOnlySpan<ulong> primes, Span<byte> hits)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Numerics;
|
|
||||||
using PerfectNumbers.Core;
|
|
||||||
|
|
||||||
namespace PerfectNumbers.Core.Cpu;
|
namespace PerfectNumbers.Core.Cpu;
|
||||||
|
|
||||||
public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDivisorByDivisorTester
|
public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDivisorByDivisorTester
|
||||||
{
|
{
|
||||||
private readonly object _sync = new();
|
private readonly object _sync = new();
|
||||||
private readonly ConcurrentBag<DivisorScanSession> _sessionPool = new();
|
private readonly ConcurrentBag<DivisorScanSession> _sessionPool = [];
|
||||||
private ulong _divisorLimit;
|
private ulong _divisorLimit;
|
||||||
private ulong _lastStatusDivisor;
|
private ulong _lastStatusDivisor;
|
||||||
private bool _isConfigured;
|
|
||||||
private int _batchSize = 1_024;
|
private int _batchSize = 1_024;
|
||||||
|
|
||||||
|
|
||||||
@@ -24,55 +20,25 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
|
|
||||||
public void ConfigureFromMaxPrime(ulong maxPrime)
|
public void ConfigureFromMaxPrime(ulong maxPrime)
|
||||||
{
|
{
|
||||||
lock (_sync)
|
_divisorLimit = ComputeDivisorLimitFromMaxPrime(maxPrime);
|
||||||
{
|
_lastStatusDivisor = 0UL;
|
||||||
_divisorLimit = ComputeDivisorLimitFromMaxPrime(maxPrime);
|
|
||||||
_lastStatusDivisor = 0UL;
|
|
||||||
_isConfigured = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong DivisorLimit
|
public ulong DivisorLimit
|
||||||
{
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
lock (_sync)
|
return _divisorLimit;
|
||||||
{
|
|
||||||
if (!_isConfigured)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return _divisorLimit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong GetAllowedMaxDivisor(ulong prime)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ulong GetAllowedMaxDivisor(ulong prime) => ComputeAllowedMaxDivisor(prime, _divisorLimit);
|
||||||
|
|
||||||
|
public bool IsPrime(ulong prime, out bool divisorsExhausted)
|
||||||
{
|
{
|
||||||
lock (_sync)
|
ulong allowedMax = ComputeAllowedMaxDivisor(prime, _divisorLimit);
|
||||||
{
|
|
||||||
if (!_isConfigured)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return ComputeAllowedMaxDivisor(prime, _divisorLimit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsPrime(ulong prime, out bool divisorsExhausted)
|
|
||||||
{
|
|
||||||
ulong allowedMax;
|
|
||||||
lock (_sync)
|
|
||||||
{
|
|
||||||
if (!_isConfigured)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester.");
|
|
||||||
}
|
|
||||||
|
|
||||||
allowedMax = ComputeAllowedMaxDivisor(prime, _divisorLimit);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allowedMax < 3UL)
|
if (allowedMax < 3UL)
|
||||||
{
|
{
|
||||||
@@ -81,13 +47,11 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
}
|
}
|
||||||
|
|
||||||
ulong processedCount;
|
ulong processedCount;
|
||||||
ulong lastProcessed;
|
|
||||||
bool processedAll;
|
bool processedAll;
|
||||||
|
|
||||||
bool composite = CheckDivisors(
|
bool composite = CheckDivisors(
|
||||||
prime,
|
prime,
|
||||||
allowedMax,
|
allowedMax,
|
||||||
out lastProcessed,
|
|
||||||
out processedAll,
|
out processedAll,
|
||||||
out processedCount);
|
out processedCount);
|
||||||
|
|
||||||
@@ -109,26 +73,11 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PrepareCandidates(ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues)
|
public void PrepareCandidates(in ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues)
|
||||||
{
|
{
|
||||||
if (allowedMaxValues.Length < primes.Length)
|
ulong divisorLimit = _divisorLimit;
|
||||||
{
|
int length = primes.Length;
|
||||||
throw new ArgumentException("allowedMaxValues span must be at least as long as primes span.", nameof(allowedMaxValues));
|
for (int index = 0; index < length; index++)
|
||||||
}
|
|
||||||
|
|
||||||
ulong divisorLimit;
|
|
||||||
|
|
||||||
lock (_sync)
|
|
||||||
{
|
|
||||||
if (!_isConfigured)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester.");
|
|
||||||
}
|
|
||||||
|
|
||||||
divisorLimit = _divisorLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int index = 0; index < primes.Length; index++)
|
|
||||||
{
|
{
|
||||||
allowedMaxValues[index] = ComputeAllowedMaxDivisor(primes[index], divisorLimit);
|
allowedMaxValues[index] = ComputeAllowedMaxDivisor(primes[index], divisorLimit);
|
||||||
}
|
}
|
||||||
@@ -136,21 +85,13 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
|
|
||||||
public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession()
|
public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession()
|
||||||
{
|
{
|
||||||
lock (_sync)
|
if (_sessionPool.TryTake(out DivisorScanSession? session))
|
||||||
{
|
{
|
||||||
if (!_isConfigured)
|
session.Reset();
|
||||||
{
|
return session;
|
||||||
throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester.");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (_sessionPool.TryTake(out DivisorScanSession? session))
|
return new DivisorScanSession(this);
|
||||||
{
|
|
||||||
session.Reset();
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new DivisorScanSession(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReturnSession(DivisorScanSession session)
|
private void ReturnSession(DivisorScanSession session)
|
||||||
@@ -161,11 +102,9 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
private bool CheckDivisors(
|
private bool CheckDivisors(
|
||||||
ulong prime,
|
ulong prime,
|
||||||
ulong allowedMax,
|
ulong allowedMax,
|
||||||
out ulong lastProcessed,
|
|
||||||
out bool processedAll,
|
out bool processedAll,
|
||||||
out ulong processedCount)
|
out ulong processedCount)
|
||||||
{
|
{
|
||||||
lastProcessed = 0UL;
|
|
||||||
processedCount = 0UL;
|
processedCount = 0UL;
|
||||||
processedAll = false;
|
processedAll = false;
|
||||||
|
|
||||||
@@ -214,7 +153,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
{
|
{
|
||||||
ulong candidate = (ulong)divisor;
|
ulong candidate = (ulong)divisor;
|
||||||
processedCount++;
|
processedCount++;
|
||||||
lastProcessed = candidate;
|
|
||||||
|
|
||||||
bool admissible = lastIsSeven
|
bool admissible = lastIsSeven
|
||||||
? (remainder10 == 3 || remainder10 == 7 || remainder10 == 9)
|
? (remainder10 == 3 || remainder10 == 7 || remainder10 == 9)
|
||||||
@@ -348,13 +286,8 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
_disposed = false;
|
_disposed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, ReadOnlySpan<ulong> primes, Span<byte> hits)
|
public void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, in ReadOnlySpan<ulong> primes, Span<byte> hits)
|
||||||
{
|
{
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
throw new ObjectDisposedException(nameof(DivisorScanSession));
|
|
||||||
}
|
|
||||||
|
|
||||||
int length = primes.Length;
|
int length = primes.Length;
|
||||||
if (length == 0)
|
if (length == 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace PerfectNumbers.Core;
|
||||||
|
|
||||||
|
public readonly struct FactorEntry128(in UInt128 value, int exponent)
|
||||||
|
{
|
||||||
|
public readonly UInt128 Value = value;
|
||||||
|
|
||||||
|
public readonly int Exponent = exponent;
|
||||||
|
}
|
||||||
@@ -148,7 +148,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PrepareCandidates(ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues)
|
public void PrepareCandidates(in ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues)
|
||||||
{
|
{
|
||||||
if (allowedMaxValues.Length < primes.Length)
|
if (allowedMaxValues.Length < primes.Length)
|
||||||
{
|
{
|
||||||
@@ -583,7 +583,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
_disposed = false;
|
_disposed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, ReadOnlySpan<ulong> primes, Span<byte> hits)
|
public void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, in ReadOnlySpan<ulong> primes, Span<byte> hits)
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ public interface IMersenneNumberDivisorByDivisorTester
|
|||||||
|
|
||||||
IDivisorScanSession CreateDivisorSession();
|
IDivisorScanSession CreateDivisorSession();
|
||||||
|
|
||||||
void PrepareCandidates(ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues);
|
void PrepareCandidates(in ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues);
|
||||||
|
|
||||||
public interface IDivisorScanSession : IDisposable
|
public interface IDivisorScanSession : IDisposable
|
||||||
{
|
{
|
||||||
void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, ReadOnlySpan<ulong> primes, Span<byte> hits);
|
void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, in ReadOnlySpan<ulong> primes, Span<byte> hits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,125 +10,122 @@ namespace PerfectNumbers.Core;
|
|||||||
|
|
||||||
public static class MersenneNumberDivisorByDivisorTester
|
public static class MersenneNumberDivisorByDivisorTester
|
||||||
{
|
{
|
||||||
public static void Run(
|
public static void Run(
|
||||||
List<ulong> candidates,
|
List<ulong> candidates,
|
||||||
IMersenneNumberDivisorByDivisorTester tester,
|
IMersenneNumberDivisorByDivisorTester tester,
|
||||||
Dictionary<ulong, (bool DetailedCheck, bool PassedAllTests)>? previousResults,
|
Dictionary<ulong, (bool DetailedCheck, bool PassedAllTests)>? previousResults,
|
||||||
ulong startPrime,
|
ulong startPrime,
|
||||||
Action markComposite,
|
Action markComposite,
|
||||||
Action clearComposite,
|
Action clearComposite,
|
||||||
Action<ulong, bool, bool, bool> printResult,
|
Action<ulong, bool, bool, bool> printResult,
|
||||||
int threadCount)
|
int threadCount)
|
||||||
{
|
{
|
||||||
if (candidates.Count == 0)
|
if (candidates.Count == 0)
|
||||||
{
|
{
|
||||||
Console.WriteLine("No candidates were provided for --mersenne=bydivisor.");
|
Console.WriteLine("No candidates were provided for --mersenne=bydivisor.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = threadCount;
|
_ = threadCount;
|
||||||
|
|
||||||
candidates.Sort();
|
candidates.Sort();
|
||||||
Span<ulong> candidateSpan = CollectionsMarshal.AsSpan(candidates);
|
Span<ulong> candidateSpan = CollectionsMarshal.AsSpan(candidates);
|
||||||
int candidateCount = candidateSpan.Length;
|
int candidateCount = candidateSpan.Length;
|
||||||
|
|
||||||
bool applyStartPrime = startPrime > 0UL;
|
bool applyStartPrime = startPrime > 0UL;
|
||||||
int skippedByPreviousResults = 0;
|
int skippedByPreviousResults = 0;
|
||||||
|
|
||||||
if (previousResults is not null && previousResults.Count > 0)
|
ArrayPool<ulong> pool = ThreadStaticPools.UlongPool;
|
||||||
{
|
if (previousResults is not null && previousResults.Count > 0)
|
||||||
int recordedCount = previousResults.Count;
|
{
|
||||||
ulong[] recordedCandidates = ArrayPool<ulong>.Shared.Rent(recordedCount);
|
int recordedCount = previousResults.Count;
|
||||||
try
|
ulong[] recordedCandidates = pool.Rent(recordedCount);
|
||||||
{
|
|
||||||
Span<ulong> recordedSpan = recordedCandidates.AsSpan(0, recordedCount);
|
|
||||||
int recordedIndex = 0;
|
|
||||||
foreach (ulong value in previousResults.Keys)
|
|
||||||
{
|
|
||||||
recordedSpan[recordedIndex++] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
recordedSpan.Sort();
|
Span<ulong> recordedSpan = recordedCandidates.AsSpan(0, recordedCount);
|
||||||
|
int recordedIndex = 0;
|
||||||
|
foreach (ulong value in previousResults.Keys)
|
||||||
|
{
|
||||||
|
recordedSpan[recordedIndex++] = value;
|
||||||
|
}
|
||||||
|
|
||||||
int writeIndex = 0;
|
recordedSpan.Sort();
|
||||||
int skipIndex = 0;
|
|
||||||
|
|
||||||
for (int readIndex = 0; readIndex < candidateCount; readIndex++)
|
int writeIndex = 0;
|
||||||
{
|
int skipIndex = 0;
|
||||||
ulong candidate = candidateSpan[readIndex];
|
|
||||||
|
|
||||||
while (skipIndex < recordedCount && recordedSpan[skipIndex] < candidate)
|
for (int readIndex = 0; readIndex < candidateCount; readIndex++)
|
||||||
{
|
{
|
||||||
skipIndex++;
|
ulong candidate = candidateSpan[readIndex];
|
||||||
}
|
|
||||||
|
|
||||||
if (skipIndex < recordedCount && recordedSpan[skipIndex] == candidate)
|
while (skipIndex < recordedCount && recordedSpan[skipIndex] < candidate)
|
||||||
{
|
{
|
||||||
skippedByPreviousResults++;
|
skipIndex++;
|
||||||
skipIndex++;
|
}
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
candidateSpan[writeIndex++] = candidate;
|
if (skipIndex < recordedCount && recordedSpan[skipIndex] == candidate)
|
||||||
}
|
{
|
||||||
|
skippedByPreviousResults++;
|
||||||
|
skipIndex++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (writeIndex < candidateCount)
|
candidateSpan[writeIndex++] = candidate;
|
||||||
{
|
}
|
||||||
candidates.RemoveRange(writeIndex, candidateCount - writeIndex);
|
|
||||||
candidateSpan = CollectionsMarshal.AsSpan(candidates);
|
|
||||||
candidateCount = candidateSpan.Length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
ArrayPool<ulong>.Shared.Return(recordedCandidates, clearArray: true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (skippedByPreviousResults > 0)
|
if (writeIndex < candidateCount)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Skipped {skippedByPreviousResults.ToString(CultureInfo.InvariantCulture)} candidates excluded by previous results.");
|
candidates.RemoveRange(writeIndex, candidateCount - writeIndex);
|
||||||
}
|
candidateSpan = CollectionsMarshal.AsSpan(candidates);
|
||||||
|
candidateCount = candidateSpan.Length;
|
||||||
|
}
|
||||||
|
|
||||||
int startIndex = 0;
|
pool.Return(recordedCandidates, clearArray: true);
|
||||||
if (applyStartPrime)
|
}
|
||||||
{
|
|
||||||
startIndex = candidates.BinarySearch(startPrime);
|
|
||||||
if (startIndex < 0)
|
|
||||||
{
|
|
||||||
startIndex = ~startIndex;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
while (startIndex > 0 && candidates[startIndex - 1] >= startPrime)
|
|
||||||
{
|
|
||||||
startIndex--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<ulong> primesToTest = candidates;
|
if (skippedByPreviousResults > 0)
|
||||||
Span<ulong> primesSpan = CollectionsMarshal.AsSpan(primesToTest);
|
{
|
||||||
ulong maxPrime = 0UL;
|
Console.WriteLine($"Skipped {skippedByPreviousResults.ToString(CultureInfo.InvariantCulture)} candidates excluded by previous results.");
|
||||||
int primeWriteIndex = 0;
|
}
|
||||||
|
|
||||||
|
int startIndex = 0;
|
||||||
|
if (applyStartPrime)
|
||||||
|
{
|
||||||
|
startIndex = candidates.BinarySearch(startPrime);
|
||||||
|
if (startIndex < 0)
|
||||||
|
{
|
||||||
|
startIndex = ~startIndex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (startIndex > 0 && candidates[startIndex - 1] >= startPrime)
|
||||||
|
{
|
||||||
|
startIndex--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ulong> primesToTest = candidates;
|
||||||
|
Span<ulong> primesSpan = CollectionsMarshal.AsSpan(primesToTest);
|
||||||
|
ulong maxPrime = 0UL;
|
||||||
|
int primeWriteIndex = 0;
|
||||||
|
|
||||||
// This implementation is terribly slow, while this method expect prime p given as --filter-p input already.
|
// This implementation is terribly slow, while this method expect prime p given as --filter-p input already.
|
||||||
// We don't need to additionally check it.
|
// We don't need to additionally check it.
|
||||||
|
|
||||||
// using IEnumerator<ulong> primeEnumerator = Prime.Numbers.GetEnumerator();
|
// using IEnumerator<ulong> primeEnumerator = Prime.Numbers.GetEnumerator();
|
||||||
// bool hasPrime = primeEnumerator.MoveNext();
|
// bool hasPrime = primeEnumerator.MoveNext();
|
||||||
// ulong currentPrime = hasPrime ? primeEnumerator.Current : 0UL;
|
// ulong currentPrime = hasPrime ? primeEnumerator.Current : 0UL;
|
||||||
|
|
||||||
for (int index = startIndex; index < candidateCount; index++)
|
for (int index = startIndex; index < candidateCount; index++)
|
||||||
{
|
{
|
||||||
ulong candidate = primesSpan[index];
|
ulong candidate = primesSpan[index];
|
||||||
|
|
||||||
if (candidate <= 1UL)
|
if (candidate <= 1UL)
|
||||||
{
|
{
|
||||||
markComposite();
|
markComposite();
|
||||||
printResult(candidate, false, false, false);
|
printResult(candidate, false, false, false);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This implementation is terribly slow, while this method expect prime p given as --filter-p input already.
|
// This implementation is terribly slow, while this method expect prime p given as --filter-p input already.
|
||||||
// We don't need to additionally check it.
|
// We don't need to additionally check it.
|
||||||
@@ -154,123 +151,118 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
|
|
||||||
primesSpan[primeWriteIndex++] = candidate;
|
primesSpan[primeWriteIndex++] = candidate;
|
||||||
|
|
||||||
if (candidate > maxPrime)
|
if (candidate > maxPrime)
|
||||||
{
|
{
|
||||||
maxPrime = candidate;
|
maxPrime = candidate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (primeWriteIndex < primesToTest.Count)
|
if (primeWriteIndex < primesToTest.Count)
|
||||||
{
|
{
|
||||||
primesToTest.RemoveRange(primeWriteIndex, primesToTest.Count - primeWriteIndex);
|
primesToTest.RemoveRange(primeWriteIndex, primesToTest.Count - primeWriteIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (primesToTest.Count == 0)
|
if (primesToTest.Count == 0)
|
||||||
{
|
{
|
||||||
if (applyStartPrime)
|
if (applyStartPrime)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"No primes greater than or equal to {startPrime.ToString(CultureInfo.InvariantCulture)} were found for --mersenne=bydivisor.");
|
Console.WriteLine($"No primes greater than or equal to {startPrime.ToString(CultureInfo.InvariantCulture)} were found for --mersenne=bydivisor.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine("No prime candidates remain for --mersenne=bydivisor after filtering.");
|
Console.WriteLine("No prime candidates remain for --mersenne=bydivisor after filtering.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxPrime <= 1UL)
|
if (maxPrime <= 1UL)
|
||||||
{
|
{
|
||||||
Console.WriteLine("The filter specified by --filter-p must contain at least one prime exponent greater than 1 for --mersenne=bydivisor.");
|
Console.WriteLine("The filter specified by --filter-p must contain at least one prime exponent greater than 1 for --mersenne=bydivisor.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tester.ConfigureFromMaxPrime(maxPrime);
|
tester.ConfigureFromMaxPrime(maxPrime);
|
||||||
|
|
||||||
int primeCount = primesToTest.Count;
|
int primeCount = primesToTest.Count;
|
||||||
ulong[] primeBatch = ArrayPool<ulong>.Shared.Rent(primeCount);
|
ulong[] primeBatch = pool.Rent(primeCount);
|
||||||
ulong[] allowedMaxBatch = ArrayPool<ulong>.Shared.Rent(primeCount);
|
ulong[] allowedMaxBatch = pool.Rent(primeCount);
|
||||||
List<ulong> filteredPrimes = new(primeCount);
|
List<ulong> filteredPrimes = new(primeCount);
|
||||||
|
|
||||||
try
|
Span<ulong> primeSpan = primeBatch.AsSpan(0, primeCount);
|
||||||
{
|
Span<ulong> allowedMaxSpan = allowedMaxBatch.AsSpan(0, primeCount);
|
||||||
Span<ulong> primeSpan = primeBatch.AsSpan(0, primeCount);
|
|
||||||
Span<ulong> allowedMaxSpan = allowedMaxBatch.AsSpan(0, primeCount);
|
|
||||||
|
|
||||||
for (int i = 0; i < primeCount; i++)
|
for (int i = 0; i < primeCount; i++)
|
||||||
{
|
{
|
||||||
primeSpan[i] = primesToTest[i];
|
primeSpan[i] = primesToTest[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
tester.PrepareCandidates(primeSpan, allowedMaxSpan);
|
tester.PrepareCandidates(primeSpan, allowedMaxSpan);
|
||||||
|
|
||||||
for (int i = 0; i < primeCount; i++)
|
for (int i = 0; i < primeCount; i++)
|
||||||
{
|
{
|
||||||
ulong prime = primeSpan[i];
|
ulong prime = primeSpan[i];
|
||||||
ulong allowedMax = allowedMaxSpan[i];
|
ulong allowedMax = allowedMaxSpan[i];
|
||||||
|
|
||||||
if (allowedMax < 3UL)
|
if (allowedMax < 3UL)
|
||||||
{
|
{
|
||||||
clearComposite();
|
clearComposite();
|
||||||
printResult(prime, true, true, true);
|
printResult(prime, true, true, true);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredPrimes.Add(prime);
|
filteredPrimes.Add(prime);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
ArrayPool<ulong>.Shared.Return(allowedMaxBatch, clearArray: true);
|
|
||||||
ArrayPool<ulong>.Shared.Return(primeBatch, clearArray: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filteredPrimes.Count == 0)
|
pool.Return(allowedMaxBatch, clearArray: true);
|
||||||
{
|
pool.Return(primeBatch, clearArray: true);
|
||||||
if (applyStartPrime)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"No primes greater than or equal to {startPrime.ToString(CultureInfo.InvariantCulture)} were found for --mersenne=bydivisor.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
if (filteredPrimes.Count == 0)
|
||||||
}
|
{
|
||||||
|
if (applyStartPrime)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"No primes greater than or equal to {startPrime.ToString(CultureInfo.InvariantCulture)} were found for --mersenne=bydivisor.");
|
||||||
|
}
|
||||||
|
|
||||||
int workerCount = threadCount <= 0 ? Environment.ProcessorCount : threadCount;
|
return;
|
||||||
if (workerCount < 1)
|
}
|
||||||
{
|
|
||||||
workerCount = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessPrime(ulong prime)
|
int workerCount = threadCount <= 0 ? Environment.ProcessorCount : threadCount;
|
||||||
{
|
if (workerCount < 1)
|
||||||
bool isPrime = tester.IsPrime(prime, out bool divisorsExhausted);
|
{
|
||||||
|
workerCount = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isPrime)
|
void ProcessPrime(ulong prime)
|
||||||
{
|
{
|
||||||
markComposite();
|
bool isPrime = tester.IsPrime(prime, out bool divisorsExhausted);
|
||||||
printResult(prime, true, true, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearComposite();
|
if (!isPrime)
|
||||||
printResult(prime, true, divisorsExhausted, true);
|
{
|
||||||
}
|
markComposite();
|
||||||
|
printResult(prime, true, true, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (workerCount == 1)
|
clearComposite();
|
||||||
{
|
printResult(prime, true, divisorsExhausted, true);
|
||||||
foreach (ulong prime in filteredPrimes)
|
}
|
||||||
{
|
|
||||||
ProcessPrime(prime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ParallelOptions options = new()
|
|
||||||
{
|
|
||||||
MaxDegreeOfParallelism = workerCount
|
|
||||||
};
|
|
||||||
|
|
||||||
Parallel.ForEach(filteredPrimes, options, ProcessPrime);
|
if (workerCount == 1)
|
||||||
}
|
{
|
||||||
}
|
foreach (ulong prime in filteredPrimes)
|
||||||
|
{
|
||||||
|
ProcessPrime(prime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ParallelOptions options = new()
|
||||||
|
{
|
||||||
|
MaxDegreeOfParallelism = workerCount
|
||||||
|
};
|
||||||
|
|
||||||
|
Parallel.ForEach(filteredPrimes, options, ProcessPrime);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -38,8 +38,8 @@ namespace PerfectNumbers.Core
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly PartialFactorResult Empty = new PartialFactorResult
|
public static readonly PartialFactorResult Empty = new()
|
||||||
{
|
{
|
||||||
Cofactor = 1UL,
|
Cofactor = 1UL,
|
||||||
FullyFactored = true,
|
FullyFactored = true,
|
||||||
};
|
};
|
||||||
@@ -70,7 +70,7 @@ namespace PerfectNumbers.Core
|
|||||||
if (factors is not null)
|
if (factors is not null)
|
||||||
{
|
{
|
||||||
Factors = null;
|
Factors = null;
|
||||||
ArrayPool<FactorEntry>.Shared.Return(factors, clearArray: false);
|
ThreadStaticPools.FactorEntryPool.Return(factors, clearArray: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
_next = s_poolHead;
|
_next = s_poolHead;
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ namespace PerfectNumbers.Core;
|
|||||||
internal static partial class PrimeOrderCalculator
|
internal static partial class PrimeOrderCalculator
|
||||||
{
|
{
|
||||||
public static UInt128 Calculate(
|
public static UInt128 Calculate(
|
||||||
UInt128 prime,
|
in UInt128 prime,
|
||||||
UInt128? previousOrder,
|
in UInt128? previousOrder,
|
||||||
PrimeOrderSearchConfig config,
|
in PrimeOrderSearchConfig config,
|
||||||
PrimeOrderHeuristicDevice device)
|
in PrimeOrderHeuristicDevice device)
|
||||||
{
|
{
|
||||||
var scope = UsePow2Mode(device);
|
var scope = UsePow2Mode(device);
|
||||||
MontgomeryDivisorData divisorData;
|
MontgomeryDivisorData divisorData;
|
||||||
@@ -49,7 +49,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UInt128 CalculateWideInternal(UInt128 prime, UInt128? previousOrder, in MontgomeryDivisorData divisorData, PrimeOrderSearchConfig config)
|
private static UInt128 CalculateWideInternal(in UInt128 prime, in UInt128? previousOrder, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config)
|
||||||
{
|
{
|
||||||
if (prime <= UInt128.One)
|
if (prime <= UInt128.One)
|
||||||
{
|
{
|
||||||
@@ -79,12 +79,12 @@ internal static partial class PrimeOrderCalculator
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static UInt128 RunHeuristicPipelineWide(
|
private static UInt128 RunHeuristicPipelineWide(
|
||||||
UInt128 prime,
|
in UInt128 prime,
|
||||||
UInt128? previousOrder,
|
in UInt128? previousOrder,
|
||||||
in MontgomeryDivisorData divisorData,
|
in MontgomeryDivisorData divisorData,
|
||||||
PrimeOrderSearchConfig config,
|
in PrimeOrderSearchConfig config,
|
||||||
UInt128 phi,
|
in UInt128 phi,
|
||||||
PartialFactorResult128 phiFactors)
|
in PartialFactorResult128 phiFactors)
|
||||||
{
|
{
|
||||||
if (phiFactors.FullyFactored && TrySpecialMaxWide(phi, prime, divisorData, phiFactors))
|
if (phiFactors.FullyFactored && TrySpecialMaxWide(phi, prime, divisorData, phiFactors))
|
||||||
{
|
{
|
||||||
@@ -112,12 +112,12 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return candidateOrder;
|
return candidateOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UInt128 FinishStrictlyWide(UInt128 prime, in MontgomeryDivisorData divisorData)
|
private static UInt128 FinishStrictlyWide(in UInt128 prime, in MontgomeryDivisorData divisorData)
|
||||||
{
|
{
|
||||||
return CalculateByFactorizationWide(prime, divisorData);
|
return CalculateByFactorizationWide(prime, divisorData);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TrySpecialMaxWide(UInt128 phi, UInt128 prime, in MontgomeryDivisorData divisorData, PartialFactorResult128 factors)
|
private static bool TrySpecialMaxWide(in UInt128 phi, in UInt128 prime, in MontgomeryDivisorData divisorData, in PartialFactorResult128 factors)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<FactorEntry128> factorSpan = factors.Factors;
|
ReadOnlySpan<FactorEntry128> factorSpan = factors.Factors;
|
||||||
int length = factors.Count;
|
int length = factors.Count;
|
||||||
@@ -134,7 +134,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UInt128 InitializeStartingOrderWide(UInt128 prime, UInt128 phi, in MontgomeryDivisorData divisorData)
|
private static UInt128 InitializeStartingOrderWide(in UInt128 prime, in UInt128 phi, in MontgomeryDivisorData divisorData)
|
||||||
{
|
{
|
||||||
UInt128 order = phi;
|
UInt128 order = phi;
|
||||||
UInt128 mod8 = prime & (UInt128)7UL;
|
UInt128 mod8 = prime & (UInt128)7UL;
|
||||||
@@ -150,19 +150,20 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UInt128 ExponentLoweringWide(UInt128 order, UInt128 prime, in MontgomeryDivisorData divisorData, PartialFactorResult128 factors)
|
private static UInt128 ExponentLoweringWide(UInt128 order, in UInt128 prime, in MontgomeryDivisorData divisorData, in PartialFactorResult128 factors)
|
||||||
{
|
{
|
||||||
FactorEntry128[]? tempArray = null;
|
ArrayPool<FactorEntry128> pool = ThreadStaticPools.FactorEntry128Pool;
|
||||||
|
ReadOnlySpan<FactorEntry128> factorSpan = factors.Factors;
|
||||||
|
int length = factors.Count;
|
||||||
|
// TODO: This should never trigger from production code - check
|
||||||
|
// if (length == 0)
|
||||||
|
// {
|
||||||
|
// return order;
|
||||||
|
// }
|
||||||
|
FactorEntry128[]? tempArray = pool.Rent(length + 1);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ReadOnlySpan<FactorEntry128> factorSpan = factors.Factors;
|
|
||||||
int length = factors.Count;
|
|
||||||
if (length == 0)
|
|
||||||
{
|
|
||||||
return order;
|
|
||||||
}
|
|
||||||
|
|
||||||
tempArray = ArrayPool<FactorEntry128>.Shared.Rent(length + 1);
|
|
||||||
Span<FactorEntry128> buffer = tempArray.AsSpan(0, length);
|
Span<FactorEntry128> buffer = tempArray.AsSpan(0, length);
|
||||||
factorSpan.CopyTo(buffer);
|
factorSpan.CopyTo(buffer);
|
||||||
|
|
||||||
@@ -198,14 +199,11 @@ internal static partial class PrimeOrderCalculator
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (tempArray is not null)
|
pool.Return(tempArray, clearArray: false);
|
||||||
{
|
|
||||||
ArrayPool<FactorEntry128>.Shared.Return(tempArray, clearArray: false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryConfirmOrderWide(UInt128 prime, UInt128 order, in MontgomeryDivisorData divisorData, PrimeOrderSearchConfig config)
|
private static bool TryConfirmOrderWide(in UInt128 prime, in UInt128 order, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config)
|
||||||
{
|
{
|
||||||
if (order == UInt128.Zero)
|
if (order == UInt128.Zero)
|
||||||
{
|
{
|
||||||
@@ -263,11 +261,11 @@ internal static partial class PrimeOrderCalculator
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryHeuristicFinishWide(
|
private static bool TryHeuristicFinishWide(
|
||||||
UInt128 prime,
|
in UInt128 prime,
|
||||||
UInt128 order,
|
in UInt128 order,
|
||||||
UInt128? previousOrder,
|
in UInt128? previousOrder,
|
||||||
in MontgomeryDivisorData divisorData,
|
in MontgomeryDivisorData divisorData,
|
||||||
PrimeOrderSearchConfig config,
|
in PrimeOrderSearchConfig config,
|
||||||
out UInt128 result)
|
out UInt128 result)
|
||||||
{
|
{
|
||||||
result = UInt128.Zero;
|
result = UInt128.Zero;
|
||||||
@@ -339,7 +337,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SortCandidatesWide(UInt128 prime, UInt128? previousOrder, List<UInt128> candidates)
|
private static void SortCandidatesWide(UInt128 prime, in UInt128? previousOrder, List<UInt128> candidates)
|
||||||
{
|
{
|
||||||
UInt128 previous = previousOrder ?? UInt128.Zero;
|
UInt128 previous = previousOrder ?? UInt128.Zero;
|
||||||
int previousGroup = previousOrder.HasValue ? GetGroupWide(previousOrder.Value, prime) : 1;
|
int previousGroup = previousOrder.HasValue ? GetGroupWide(previousOrder.Value, prime) : 1;
|
||||||
@@ -367,9 +365,9 @@ internal static partial class PrimeOrderCalculator
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static CandidateKey128 BuildKeyWide(
|
private static CandidateKey128 BuildKeyWide(
|
||||||
UInt128 value,
|
in UInt128 value,
|
||||||
UInt128 prime,
|
in UInt128 prime,
|
||||||
UInt128 previous,
|
in UInt128 previous,
|
||||||
int previousGroup,
|
int previousGroup,
|
||||||
bool hasPrevious)
|
bool hasPrevious)
|
||||||
{
|
{
|
||||||
@@ -391,7 +389,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return new CandidateKey128(primary, false, distance, false, value);
|
return new CandidateKey128(primary, false, distance, false, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int CompareComponents(bool descendingX, UInt128 valueX, bool descendingY, UInt128 valueY)
|
private static int CompareComponents(bool descendingX, in UInt128 valueX, bool descendingY, in UInt128 valueY)
|
||||||
{
|
{
|
||||||
if (descendingX == descendingY)
|
if (descendingX == descendingY)
|
||||||
{
|
{
|
||||||
@@ -416,7 +414,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return valueX > valueY ? 1 : -1;
|
return valueX > valueY ? 1 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetGroupWide(UInt128 value, UInt128 prime)
|
private static int GetGroupWide(in UInt128 value, in UInt128 prime)
|
||||||
{
|
{
|
||||||
UInt128 threshold1 = prime >> 3;
|
UInt128 threshold1 = prime >> 3;
|
||||||
if (value <= threshold1)
|
if (value <= threshold1)
|
||||||
@@ -439,7 +437,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void BuildCandidatesWide(UInt128 order, FactorEntry128[] factors, int count, List<UInt128> candidates, int limit)
|
private static void BuildCandidatesWide(in UInt128 order, FactorEntry128[] factors, int count, List<UInt128> candidates, int limit)
|
||||||
{
|
{
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
{
|
{
|
||||||
@@ -452,10 +450,10 @@ internal static partial class PrimeOrderCalculator
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void BuildCandidatesRecursiveWide(
|
private static void BuildCandidatesRecursiveWide(
|
||||||
UInt128 order,
|
in UInt128 order,
|
||||||
ReadOnlySpan<FactorEntry128> factors,
|
in ReadOnlySpan<FactorEntry128> factors,
|
||||||
int index,
|
int index,
|
||||||
UInt128 divisorProduct,
|
in UInt128 divisorProduct,
|
||||||
List<UInt128> candidates,
|
List<UInt128> candidates,
|
||||||
int limit)
|
int limit)
|
||||||
{
|
{
|
||||||
@@ -511,7 +509,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryConfirmCandidateWide(UInt128 prime, UInt128 candidate, PrimeOrderSearchConfig config, ref int powUsed, int powBudget, in MontgomeryDivisorData divisorData)
|
private static bool TryConfirmCandidateWide(in UInt128 prime, in UInt128 candidate, in PrimeOrderSearchConfig config, ref int powUsed, int powBudget, in MontgomeryDivisorData divisorData)
|
||||||
{
|
{
|
||||||
PartialFactorResult128 factorization = PartialFactorWide(candidate, config);
|
PartialFactorResult128 factorization = PartialFactorWide(candidate, config);
|
||||||
if (factorization.Factors is null)
|
if (factorization.Factors is null)
|
||||||
@@ -564,7 +562,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PartialFactorResult128 PartialFactorWide(UInt128 value, PrimeOrderSearchConfig config)
|
private static PartialFactorResult128 PartialFactorWide(in UInt128 value, in PrimeOrderSearchConfig config)
|
||||||
{
|
{
|
||||||
if (value <= UInt128.One)
|
if (value <= UInt128.One)
|
||||||
{
|
{
|
||||||
@@ -664,7 +662,8 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return new PartialFactorResult128(null, value, false, 0);
|
return new PartialFactorResult128(null, value, false, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
FactorEntry128[]? rented = counts.Count > 0 ? ArrayPool<FactorEntry128>.Shared.Rent(counts.Count) : null;
|
ArrayPool<FactorEntry128>? pool = null;
|
||||||
|
FactorEntry128[]? rented = counts.Count > 0 ? (pool = ThreadStaticPools.FactorEntry128Pool).Rent(counts.Count) : null;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
if (rented is not null)
|
if (rented is not null)
|
||||||
{
|
{
|
||||||
@@ -676,20 +675,20 @@ internal static partial class PrimeOrderCalculator
|
|||||||
}
|
}
|
||||||
|
|
||||||
FactorEntry128[]? resultArray = null;
|
FactorEntry128[]? resultArray = null;
|
||||||
if (rented is not null)
|
if (pool is not null)
|
||||||
{
|
{
|
||||||
Span<FactorEntry128> span = rented.AsSpan(0, index);
|
Span<FactorEntry128> span = rented.AsSpan(0, index);
|
||||||
span.Sort(static (a, b) => a.Value.CompareTo(b.Value));
|
span.Sort(static (a, b) => a.Value.CompareTo(b.Value));
|
||||||
resultArray = new FactorEntry128[index];
|
resultArray = new FactorEntry128[index];
|
||||||
span.CopyTo(resultArray);
|
span.CopyTo(resultArray);
|
||||||
ArrayPool<FactorEntry128>.Shared.Return(rented, clearArray: false);
|
pool.Return(rented!, clearArray: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fullyFactored = cofactor == UInt128.One;
|
bool fullyFactored = cofactor == UInt128.One;
|
||||||
return new PartialFactorResult128(resultArray, cofactor, fullyFactored, index);
|
return new PartialFactorResult128(resultArray, cofactor, fullyFactored, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UInt128 PopulateSmallPrimeFactorsCpuWide(UInt128 value, uint limit, Dictionary<UInt128, int> counts)
|
private static UInt128 PopulateSmallPrimeFactorsCpuWide(in UInt128 value, uint limit, Dictionary<UInt128, int> counts)
|
||||||
{
|
{
|
||||||
UInt128 remaining = value;
|
UInt128 remaining = value;
|
||||||
uint[] primes = PrimesGenerator.SmallPrimes;
|
uint[] primes = PrimesGenerator.SmallPrimes;
|
||||||
@@ -729,7 +728,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
|
|
||||||
return remaining;
|
return remaining;
|
||||||
}
|
}
|
||||||
private static bool TryPollardRhoWide(UInt128 n, Stopwatch stopwatch, long budgetTicks, out UInt128 factor)
|
private static bool TryPollardRhoWide(in UInt128 n, Stopwatch stopwatch, long budgetTicks, out UInt128 factor)
|
||||||
{
|
{
|
||||||
factor = UInt128.Zero;
|
factor = UInt128.Zero;
|
||||||
if ((n & UInt128.One) == UInt128.Zero)
|
if ((n & UInt128.One) == UInt128.Zero)
|
||||||
@@ -771,7 +770,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UInt128 AdvancePolynomialWide(UInt128 x, UInt128 c, UInt128 modulus)
|
private static UInt128 AdvancePolynomialWide(in UInt128 x, in UInt128 c, in UInt128 modulus)
|
||||||
{
|
{
|
||||||
BigInteger value = (BigInteger)x;
|
BigInteger value = (BigInteger)x;
|
||||||
value = (value * value + (BigInteger)c) % (BigInteger)modulus;
|
value = (value * value + (BigInteger)c) % (BigInteger)modulus;
|
||||||
@@ -809,7 +808,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int TrailingZeroCountWide(UInt128 value)
|
private static int TrailingZeroCountWide(in UInt128 value)
|
||||||
{
|
{
|
||||||
if (value == UInt128.Zero)
|
if (value == UInt128.Zero)
|
||||||
{
|
{
|
||||||
@@ -865,7 +864,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void FactorCompletelyWide(UInt128 value, Dictionary<UInt128, int> counts)
|
private static void FactorCompletelyWide(in UInt128 value, Dictionary<UInt128, int> counts)
|
||||||
{
|
{
|
||||||
if (value <= UInt128.One)
|
if (value <= UInt128.One)
|
||||||
{
|
{
|
||||||
@@ -883,7 +882,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
FactorCompletelyWide(value / factor, counts);
|
FactorCompletelyWide(value / factor, counts);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UInt128 PollardRhoWide(UInt128 n)
|
private static UInt128 PollardRhoWide(in UInt128 n)
|
||||||
{
|
{
|
||||||
if ((n & UInt128.One) == UInt128.Zero)
|
if ((n & UInt128.One) == UInt128.Zero)
|
||||||
{
|
{
|
||||||
@@ -916,7 +915,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsPrimeWide(UInt128 value)
|
private static bool IsPrimeWide(in UInt128 value)
|
||||||
{
|
{
|
||||||
if (value <= ulong.MaxValue)
|
if (value <= ulong.MaxValue)
|
||||||
{
|
{
|
||||||
@@ -996,7 +995,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UInt128 Pow2ModWide(UInt128 exponent, in UInt128 modulus, in MontgomeryDivisorData divisorData)
|
private static UInt128 Pow2ModWide(in UInt128 exponent, in UInt128 modulus, in MontgomeryDivisorData divisorData)
|
||||||
{
|
{
|
||||||
if (modulus == UInt128.One)
|
if (modulus == UInt128.One)
|
||||||
{
|
{
|
||||||
@@ -1026,7 +1025,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return exponent.Pow2MontgomeryModWindowed(modulus);
|
return exponent.Pow2MontgomeryModWindowed(modulus);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddFactor(Dictionary<UInt128, int> counts, UInt128 prime, int exponent)
|
private static void AddFactor(Dictionary<UInt128, int> counts, in UInt128 prime, int exponent)
|
||||||
{
|
{
|
||||||
if (counts.TryGetValue(prime, out int existing))
|
if (counts.TryGetValue(prime, out int existing))
|
||||||
{
|
{
|
||||||
@@ -1038,60 +1037,30 @@ internal static partial class PrimeOrderCalculator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly struct CandidateKey128
|
private readonly struct CandidateKey128(int primary, bool secondaryDescending, in UInt128 secondary, bool tertiaryDescending, in UInt128 tertiary)
|
||||||
{
|
{
|
||||||
public CandidateKey128(int primary, bool secondaryDescending, UInt128 secondary, bool tertiaryDescending, UInt128 tertiary)
|
public int Primary { get; } = primary;
|
||||||
{
|
|
||||||
Primary = primary;
|
|
||||||
SecondaryDescending = secondaryDescending;
|
|
||||||
Secondary = secondary;
|
|
||||||
TertiaryDescending = tertiaryDescending;
|
|
||||||
Tertiary = tertiary;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Primary { get; }
|
public bool SecondaryDescending { get; } = secondaryDescending;
|
||||||
|
|
||||||
public bool SecondaryDescending { get; }
|
public UInt128 Secondary { get; } = secondary;
|
||||||
|
|
||||||
public UInt128 Secondary { get; }
|
public bool TertiaryDescending { get; } = tertiaryDescending;
|
||||||
|
|
||||||
public bool TertiaryDescending { get; }
|
public UInt128 Tertiary { get; } = tertiary;
|
||||||
|
}
|
||||||
|
|
||||||
public UInt128 Tertiary { get; }
|
private readonly struct PartialFactorResult128(FactorEntry128[]? factors, UInt128 cofactor, bool fullyFactored, int count)
|
||||||
}
|
{
|
||||||
|
public FactorEntry128[]? Factors { get; } = factors;
|
||||||
|
|
||||||
private readonly struct FactorEntry128
|
public UInt128 Cofactor { get; } = cofactor;
|
||||||
{
|
|
||||||
public FactorEntry128(UInt128 value, int exponent)
|
|
||||||
{
|
|
||||||
Value = value;
|
|
||||||
Exponent = exponent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UInt128 Value { get; }
|
public bool FullyFactored { get; } = fullyFactored;
|
||||||
|
|
||||||
public int Exponent { get; }
|
public int Count { get; } = count;
|
||||||
}
|
|
||||||
|
|
||||||
private readonly struct PartialFactorResult128
|
public static PartialFactorResult128 Empty => new(null, UInt128.One, true, 0);
|
||||||
{
|
|
||||||
public PartialFactorResult128(FactorEntry128[]? factors, UInt128 cofactor, bool fullyFactored, int count)
|
|
||||||
{
|
|
||||||
Factors = factors;
|
|
||||||
Cofactor = cofactor;
|
|
||||||
FullyFactored = fullyFactored;
|
|
||||||
Count = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FactorEntry128[]? Factors { get; }
|
|
||||||
|
|
||||||
public UInt128 Cofactor { get; }
|
|
||||||
|
|
||||||
public bool FullyFactored { get; }
|
|
||||||
|
|
||||||
public int Count { get; }
|
|
||||||
|
|
||||||
public static PartialFactorResult128 Empty => new(null, UInt128.One, true, 0);
|
|
||||||
|
|
||||||
public PartialFactorResult128 WithAdditionalPrime(UInt128 prime)
|
public PartialFactorResult128 WithAdditionalPrime(UInt128 prime)
|
||||||
{
|
{
|
||||||
|
|||||||
+1372
-1377
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,41 @@
|
|||||||
|
using System.Buffers;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core
|
||||||
|
{
|
||||||
|
public static class ThreadStaticPools
|
||||||
|
{
|
||||||
|
[ThreadStatic]
|
||||||
|
private static ArrayPool<FactorEntry>? _factorEntryPool;
|
||||||
|
public static ArrayPool<FactorEntry> FactorEntryPool
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _factorEntryPool ??= ArrayPool<FactorEntry>.Create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static ArrayPool<FactorEntry128>? _factorEntry128Pool;
|
||||||
|
public static ArrayPool<FactorEntry128> FactorEntry128Pool
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _factorEntry128Pool ??= ArrayPool<FactorEntry128>.Create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static ArrayPool<ulong>? _ulongPool;
|
||||||
|
public static ArrayPool<ulong> UlongPool
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _ulongPool ??= ArrayPool<ulong>.Create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user