Small optimizations

This commit is contained in:
mlemanczyk committed 2025-10-16 19:46:25 +02:00
1 parent 07395830a5
commit aa4ff799f3
1 file changed
+428 -415
+428 -415
View File
@@ -47,17 +47,17 @@ internal static partial class PrimeOrderCalculator
[ThreadStatic] [ThreadStatic]
private static bool s_debugLoggingEnabled; private static bool s_debugLoggingEnabled;
[ThreadStatic] [ThreadStatic]
private static CycleRemainderStepper[]? s_smallPrimeCycleSteppers; private static CycleRemainderStepper[]? s_smallPrimeCycleSteppers;
[ThreadStatic] [ThreadStatic]
private static ulong[]? s_smallPrimeStepperPreviousValues; private static ulong[]? s_smallPrimeStepperPreviousValues;
[ThreadStatic] [ThreadStatic]
private static ulong[]? s_smallPrimeStepperRemainders; private static ulong[]? s_smallPrimeStepperRemainders;
[ThreadStatic] [ThreadStatic]
private static bool[]? s_smallPrimeStepperHasState; private static bool[]? s_smallPrimeStepperHasState;
@@ -531,7 +531,7 @@ internal static partial class PrimeOrderCalculator
} }
ThreadStaticPools.ReturnUlongList(candidates); ThreadStaticPools.ReturnUlongList(candidates);
// DebugLog("No candidate confirmed"); // DebugLog("No candidate confirmed");
return false; return false;
} }
finally finally
@@ -786,374 +786,387 @@ internal static partial class PrimeOrderCalculator
} }
private static PartialFactorResult PartialFactor(ulong value, in PrimeOrderSearchConfig config) private static PartialFactorResult PartialFactor(ulong value, in PrimeOrderSearchConfig config)
{ {
if (value <= 1UL) if (value <= 1UL)
{ {
return PartialFactorResult.Empty; return PartialFactorResult.Empty;
} }
const int FactorSlotCount = GpuSmallPrimeFactorSlots; const int FactorSlotCount = GpuSmallPrimeFactorSlots;
ulong[] primeSlotsArray = ThreadStaticPools.UlongPool.Rent(FactorSlotCount);
int[] exponentSlotsArray = ThreadStaticPools.IntPool.Rent(FactorSlotCount);
Span<ulong> primeSlots = primeSlotsArray.AsSpan(0, FactorSlotCount);
Span<int> exponentSlots = exponentSlotsArray.AsSpan(0, FactorSlotCount);
// We don't need to worry about leftovers, because we always use indexes within the calculated counts
// primeSlots.Clear();
// exponentSlots.Clear();
int factorCount = 0; // stackalloc is faster than pooling
Dictionary<ulong, int>? counts = null; // ulong[] primeSlotsArray = ThreadStaticPools.UlongPool.Rent(FactorSlotCount);
bool useDictionary = false; // int[] exponentSlotsArray = ThreadStaticPools.IntPool.Rent(FactorSlotCount);
// Span<ulong> primeSlots = primeSlotsArray.AsSpan(0, FactorSlotCount);
// Span<int> exponentSlots = exponentSlotsArray.AsSpan(0, FactorSlotCount);
Span<ulong> primeSlots = stackalloc ulong[FactorSlotCount];
Span<int> exponentSlots = stackalloc int[FactorSlotCount];
// We don't need to worry about leftovers, because we always use indexes within the calculated counts
// primeSlots.Clear();
// exponentSlots.Clear();
uint limit = config.SmallFactorLimit == 0 ? uint.MaxValue : config.SmallFactorLimit; int factorCount = 0;
ulong remaining = value; Dictionary<ulong, int>? counts = null;
bool useDictionary = false;
bool gpuFactored = false; uint limit = config.SmallFactorLimit == 0 ? uint.MaxValue : config.SmallFactorLimit;
if (IsGpuHeuristicDevice) ulong remaining = value;
{
gpuFactored = PrimeOrderGpuHeuristics.TryPartialFactor(value, limit, primeSlots, exponentSlots, out factorCount, out remaining, out _);
}
List<ulong> pending = ThreadStaticPools.RentUlongList(2); bool gpuFactored = false;
Stack<ulong>? compositeStack = null; if (IsGpuHeuristicDevice)
PartialFactorResult result; {
gpuFactored = PrimeOrderGpuHeuristics.TryPartialFactor(value, limit, primeSlots, exponentSlots, out factorCount, out remaining, out _);
}
if (!gpuFactored) List<ulong> pending = ThreadStaticPools.RentUlongList(2);
{ Stack<ulong>? compositeStack = null;
// We don't need to worry about leftovers, because we always use indexes within the calculated counts PartialFactorResult result;
// primeSlots.Clear();
// exponentSlots.Clear();
if (!TryPopulateSmallPrimeFactorsCpu(value, limit, primeSlots, exponentSlots, out factorCount, out remaining))
{
useDictionary = true;
counts = ThreadStaticPools.RentUlongIntDictionary(Math.Max(factorCount, 8));
remaining = PopulateSmallPrimeFactorsCpu(value, limit, counts);
}
}
if (remaining > 1UL) if (!gpuFactored)
{ {
pending.Add(remaining); // We don't need to worry about leftovers, because we always use indexes within the calculated counts
} // primeSlots.Clear();
// exponentSlots.Clear();
if (!TryPopulateSmallPrimeFactorsCpu(value, limit, primeSlots, exponentSlots, out factorCount, out remaining))
{
useDictionary = true;
counts = ThreadStaticPools.RentUlongIntDictionary(Math.Max(factorCount, 8));
remaining = PopulateSmallPrimeFactorsCpu(value, limit, counts);
}
}
if (config.PollardRhoMilliseconds > 0 && pending.Count > 0) if (remaining > 1UL)
{ {
long deadlineTimestamp = CreateDeadlineTimestamp(config.PollardRhoMilliseconds); pending.Add(remaining);
compositeStack = ThreadStaticPools.RentUlongStack(Math.Max(pending.Count * 2, 4)); }
compositeStack.Push(remaining);
pending.Clear();
long timestamp = 0L; if (config.PollardRhoMilliseconds > 0 && pending.Count > 0)
while (compositeStack.Count > 0) {
{ long deadlineTimestamp = CreateDeadlineTimestamp(config.PollardRhoMilliseconds);
ulong composite = compositeStack.Pop(); compositeStack = ThreadStaticPools.RentUlongStack(Math.Max(pending.Count << 1, 4));
if (composite <= 1UL) compositeStack.Push(remaining);
{ pending.Clear();
continue;
}
bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite); long timestamp = 0L;
if (isPrime) while (compositeStack.Count > 0)
{ {
AddFactorToCollector(ref useDictionary, ref counts, primeSlots, exponentSlots, ref factorCount, composite, 1); ulong composite = compositeStack.Pop();
continue; // composite will never be smaller than 1 on the execution path
} // if (composite <= 1UL)
// {
// continue;
// }
timestamp = Stopwatch.GetTimestamp(); bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite);
if (timestamp > deadlineTimestamp) if (isPrime)
{ {
pending.Add(composite); AddFactorToCollector(ref useDictionary, ref counts, primeSlots, exponentSlots, ref factorCount, composite, 1);
continue; continue;
} }
if (!TryPollardRho(composite, deadlineTimestamp, out ulong factor)) timestamp = Stopwatch.GetTimestamp();
{ if (timestamp > deadlineTimestamp)
pending.Add(composite); {
continue; pending.Add(composite);
} continue;
}
ulong quotient = composite / factor; if (!TryPollardRho(composite, deadlineTimestamp, out ulong factor))
compositeStack.Push(factor); {
compositeStack.Push(quotient); pending.Add(composite);
} continue;
} }
ulong cofactor = 1UL; ulong quotient = composite / factor;
int pendingCount = pending.Count; compositeStack.Push(factor);
for (int i = 0; i < pendingCount; i++) compositeStack.Push(quotient);
{ }
ulong composite = pending[i]; }
bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite);
if (isPrime)
{
AddFactorToCollector(ref useDictionary, ref counts, primeSlots, exponentSlots, ref factorCount, composite, 1);
}
else if (composite > 1UL)
{
cofactor = checked(cofactor * composite);
}
}
ArrayPool<FactorEntry> pool = ThreadStaticPools.FactorEntryPool; ulong cofactor = 1UL;
if (useDictionary) int pendingCount = pending.Count;
{ for (int i = 0; i < pendingCount; i++)
if ((counts is null || counts.Count == 0) && cofactor == value) {
{ ulong composite = pending[i];
result = PartialFactorResult.Rent(null, value, false, 0); bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite);
goto ReturnResult; if (isPrime)
} {
AddFactorToCollector(ref useDictionary, ref counts, primeSlots, exponentSlots, ref factorCount, composite, 1);
}
// composite is never smaller on the execution path
// else if (composite > 1UL)
else
{
cofactor = checked(cofactor * composite);
}
}
if (counts is null || counts.Count == 0) ArrayPool<FactorEntry> pool = ThreadStaticPools.FactorEntryPool;
{ if (useDictionary)
result = PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0); {
goto ReturnResult; if ((counts is null || counts.Count == 0) && cofactor == value)
} {
result = PartialFactorResult.Rent(null, value, false, 0);
goto ReturnResult;
}
FactorEntry[] factors = pool.Rent(counts.Count); if (counts is null || counts.Count == 0)
int index = 0; {
foreach (KeyValuePair<ulong, int> entry in counts) result = PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0);
{ goto ReturnResult;
factors[index] = new FactorEntry(entry.Key, entry.Value); }
index++;
}
Span<FactorEntry> span = factors.AsSpan(0, index); FactorEntry[] factors = pool.Rent(counts.Count);
span.Sort(static (a, b) => a.Value.CompareTo(b.Value)); int index = 0;
foreach (KeyValuePair<ulong, int> entry in counts)
{
factors[index] = new FactorEntry(entry.Key, entry.Value);
index++;
}
bool fullyFactored = cofactor == 1UL; Span<FactorEntry> span = factors.AsSpan(0, index);
result = PartialFactorResult.Rent(factors, cofactor, fullyFactored, index); span.Sort(static (a, b) => a.Value.CompareTo(b.Value));
goto ReturnResult;
}
int actualCount = 0; bool fullyFactored = cofactor == 1UL;
for (int i = 0; i < factorCount; i++) result = PartialFactorResult.Rent(factors, cofactor, fullyFactored, index);
{ goto ReturnResult;
if (primeSlots[i] != 0UL && exponentSlots[i] != 0) }
{
actualCount++;
}
}
if (actualCount == 0) int actualCount = 0;
{ for (int i = 0; i < factorCount; i++)
if (cofactor == value) {
{ if (primeSlots[i] != 0UL && exponentSlots[i] != 0)
result = PartialFactorResult.Rent(null, value, false, 0); {
goto ReturnResult; actualCount++;
} }
}
result = PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0); if (actualCount == 0)
goto ReturnResult; {
} if (cofactor == value)
{
result = PartialFactorResult.Rent(null, value, false, 0);
goto ReturnResult;
}
FactorEntry[] array = pool.Rent(actualCount); result = PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0);
int arrayIndex = 0; goto ReturnResult;
for (int i = 0; i < factorCount; i++) }
{
ulong primeValue = primeSlots[i];
int exponentValue = exponentSlots[i];
if (primeValue == 0UL || exponentValue == 0)
{
continue;
}
array[arrayIndex] = new FactorEntry(primeValue, exponentValue); FactorEntry[] array = pool.Rent(actualCount);
arrayIndex++; int arrayIndex = 0;
} for (int i = 0; i < factorCount; i++)
{
ulong primeValue = primeSlots[i];
int exponentValue = exponentSlots[i];
if (primeValue == 0UL || exponentValue == 0)
{
continue;
}
Span<FactorEntry> arraySpan = array.AsSpan(0, arrayIndex); array[arrayIndex] = new FactorEntry(primeValue, exponentValue);
arraySpan.Sort(static (a, b) => a.Value.CompareTo(b.Value)); arrayIndex++;
bool fullyFactoredArray = cofactor == 1UL; }
result = PartialFactorResult.Rent(array, cofactor, fullyFactoredArray, arrayIndex);
ReturnResult: Span<FactorEntry> arraySpan = array.AsSpan(0, arrayIndex);
ThreadStaticPools.UlongPool.Return(primeSlotsArray); arraySpan.Sort(static (a, b) => a.Value.CompareTo(b.Value));
ThreadStaticPools.IntPool.Return(exponentSlotsArray); bool fullyFactoredArray = cofactor == 1UL;
result = PartialFactorResult.Rent(array, cofactor, fullyFactoredArray, arrayIndex);
if (counts is not null) ReturnResult:
{ // ThreadStaticPools.UlongPool.Return(primeSlotsArray);
ThreadStaticPools.ReturnUlongIntDictionary(counts); // ThreadStaticPools.IntPool.Return(exponentSlotsArray);
}
if (compositeStack is not null) if (counts is not null)
{ {
compositeStack.Clear(); ThreadStaticPools.ReturnUlongIntDictionary(counts);
ThreadStaticPools.ReturnUlongStack(compositeStack); }
}
pending.Clear(); if (compositeStack is not null)
ThreadStaticPools.ReturnUlongList(pending); {
return result; compositeStack.Clear();
} ThreadStaticPools.ReturnUlongStack(compositeStack);
}
private static CycleRemainderStepper[] EnsureSmallPrimeCycleSteppers(uint[] primes) pending.Clear();
{ ThreadStaticPools.ReturnUlongList(pending);
int primeCount = primes.Length; return result;
CycleRemainderStepper[]? steppers = s_smallPrimeCycleSteppers; }
if (steppers is null || steppers.Length < primeCount)
{
int length = primeCount;
steppers = new CycleRemainderStepper[length];
for (int i = 0; i < length; i++)
{
steppers[i] = new CycleRemainderStepper(primes[i]);
}
s_smallPrimeCycleSteppers = steppers; private static CycleRemainderStepper[] EnsureSmallPrimeCycleSteppers(uint[] primes)
s_smallPrimeStepperPreviousValues = new ulong[length]; {
s_smallPrimeStepperRemainders = new ulong[length]; int primeCount = primes.Length;
s_smallPrimeStepperHasState = new bool[length]; CycleRemainderStepper[]? steppers = s_smallPrimeCycleSteppers;
return steppers; if (steppers is null || steppers.Length < primeCount)
} {
int length = primeCount;
steppers = new CycleRemainderStepper[length];
for (int i = 0; i < length; i++)
{
steppers[i] = new CycleRemainderStepper(primes[i]);
}
if (s_smallPrimeStepperPreviousValues is null || s_smallPrimeStepperPreviousValues.Length != steppers.Length) s_smallPrimeCycleSteppers = steppers;
{ s_smallPrimeStepperPreviousValues = new ulong[length];
var previousValues = new ulong[steppers.Length]; s_smallPrimeStepperRemainders = new ulong[length];
if (s_smallPrimeStepperPreviousValues is { } existingPrevious) s_smallPrimeStepperHasState = new bool[length];
{ return steppers;
System.Array.Copy(existingPrevious, previousValues, Math.Min(existingPrevious.Length, previousValues.Length)); }
}
s_smallPrimeStepperPreviousValues = previousValues; if (s_smallPrimeStepperPreviousValues is null || s_smallPrimeStepperPreviousValues.Length != steppers.Length)
} {
var previousValues = new ulong[steppers.Length];
if (s_smallPrimeStepperPreviousValues is { } existingPrevious)
{
System.Array.Copy(existingPrevious, previousValues, Math.Min(existingPrevious.Length, previousValues.Length));
}
if (s_smallPrimeStepperRemainders is null || s_smallPrimeStepperRemainders.Length != steppers.Length) s_smallPrimeStepperPreviousValues = previousValues;
{ }
var remainders = new ulong[steppers.Length];
if (s_smallPrimeStepperRemainders is { } existingRemainders)
{
System.Array.Copy(existingRemainders, remainders, Math.Min(existingRemainders.Length, remainders.Length));
}
s_smallPrimeStepperRemainders = remainders; if (s_smallPrimeStepperRemainders is null || s_smallPrimeStepperRemainders.Length != steppers.Length)
} {
var remainders = new ulong[steppers.Length];
if (s_smallPrimeStepperRemainders is { } existingRemainders)
{
System.Array.Copy(existingRemainders, remainders, Math.Min(existingRemainders.Length, remainders.Length));
}
if (s_smallPrimeStepperHasState is null || s_smallPrimeStepperHasState.Length != steppers.Length) s_smallPrimeStepperRemainders = remainders;
{ }
var hasState = new bool[steppers.Length];
if (s_smallPrimeStepperHasState is { } existingHasState)
{
System.Array.Copy(existingHasState, hasState, Math.Min(existingHasState.Length, hasState.Length));
}
s_smallPrimeStepperHasState = hasState; if (s_smallPrimeStepperHasState is null || s_smallPrimeStepperHasState.Length != steppers.Length)
} {
var hasState = new bool[steppers.Length];
if (s_smallPrimeStepperHasState is { } existingHasState)
{
System.Array.Copy(existingHasState, hasState, Math.Min(existingHasState.Length, hasState.Length));
}
return steppers; s_smallPrimeStepperHasState = hasState;
} }
private const int GpuSmallPrimeFactorSlots = 64; return steppers;
}
private static bool TryPopulateSmallPrimeFactorsCpu( private const int GpuSmallPrimeFactorSlots = 64;
ulong value,
uint limit,
Span<ulong> primeTargets,
Span<int> exponentTargets,
out int factorCount,
out ulong remaining)
{
factorCount = 0;
remaining = value;
if (value <= 1UL) private static bool TryPopulateSmallPrimeFactorsCpu(
{ ulong value,
return true; uint limit,
} Span<ulong> primeTargets,
Span<int> exponentTargets,
out int factorCount,
out ulong remaining)
{
factorCount = 0;
remaining = value;
int capacity = Math.Min(primeTargets.Length, exponentTargets.Length); // value will never <= 1 in production code
if (capacity == 0) // if (value <= 1UL)
{ // {
return false; // return true;
} // }
uint[] primes = PrimesGenerator.SmallPrimes; int capacity = Math.Min(primeTargets.Length, exponentTargets.Length);
ulong[] squares = PrimesGenerator.SmallPrimesPow2; // capacity will never equal 0 in production code
int primeCount = primes.Length; // if (capacity == 0)
ulong remainingLocal = value; // {
uint effectiveLimit = limit == 0 ? uint.MaxValue : limit; // return false;
CycleRemainderStepper[] cycleSteppers = EnsureSmallPrimeCycleSteppers(primes); // }
ulong[] stepperPreviousValues = s_smallPrimeStepperPreviousValues!;
ulong[] stepperRemainders = s_smallPrimeStepperRemainders!;
bool[] stepperHasState = s_smallPrimeStepperHasState!;
ulong valueForSteppers = value;
for (int i = 0; i < primeCount && remainingLocal > 1UL; i++) uint[] primes = PrimesGenerator.SmallPrimes;
{ ulong[] squares = PrimesGenerator.SmallPrimesPow2;
uint primeCandidate = primes[i]; int primeCount = primes.Length;
if (primeCandidate > effectiveLimit) ulong remainingLocal = value;
{ uint effectiveLimit = limit == 0 ? uint.MaxValue : limit;
break; CycleRemainderStepper[] cycleSteppers = EnsureSmallPrimeCycleSteppers(primes);
} ulong[] stepperPreviousValues = s_smallPrimeStepperPreviousValues!;
ulong[] stepperRemainders = s_smallPrimeStepperRemainders!;
bool[] stepperHasState = s_smallPrimeStepperHasState!;
ulong valueForSteppers = value;
ulong primeSquare = squares[i]; for (int i = 0; i < primeCount && remainingLocal > 1UL; i++)
if (primeSquare != 0UL && primeSquare > remainingLocal) {
{ uint primeCandidate = primes[i];
break; if (primeCandidate > effectiveLimit)
} {
break;
}
ulong primeValue = primeCandidate; ulong primeSquare = squares[i];
if (primeValue == 0UL) // primeSquare will never == 0 in production code
{ // if (primeSquare != 0 && primeSquare > remainingLocal)
continue; if (primeSquare > remainingLocal)
} {
break;
}
ref CycleRemainderStepper stepper = ref cycleSteppers[i]; // primeCandidate will never equal 0 in production code
ulong remainder; // if (primeCandidate == 0UL)
// {
// continue;
// }
if (!stepperHasState[i]) ref CycleRemainderStepper stepper = ref cycleSteppers[i];
{ ulong remainder;
stepper.Reset();
remainder = stepper.Initialize(valueForSteppers);
stepperHasState[i] = true;
}
else
{
ulong previousValue = stepperPreviousValues[i];
if (valueForSteppers > previousValue)
{
remainder = stepper.ComputeNext(valueForSteppers);
}
else if (valueForSteppers < previousValue)
{
stepper.Reset();
remainder = stepper.Initialize(valueForSteppers);
}
else
{
remainder = stepperRemainders[i];
}
}
stepperPreviousValues[i] = valueForSteppers; if (!stepperHasState[i])
stepperRemainders[i] = remainder; {
stepper.Reset();
remainder = stepper.Initialize(valueForSteppers);
stepperHasState[i] = true;
}
else
{
ulong previousValue = stepperPreviousValues[i];
if (valueForSteppers > previousValue)
{
remainder = stepper.ComputeNext(valueForSteppers);
}
else if (valueForSteppers < previousValue)
{
stepper.Reset();
remainder = stepper.Initialize(valueForSteppers);
}
else
{
remainder = stepperRemainders[i];
}
}
if (remainder != 0UL) stepperPreviousValues[i] = valueForSteppers;
{ stepperRemainders[i] = remainder;
continue;
}
int exponent = 0;
do
{
remainingLocal /= primeValue;
exponent++;
}
while ((remainingLocal % primeValue) == 0UL);
if (factorCount >= capacity) if (remainder != 0UL)
{ {
return false; continue;
} }
primeTargets[factorCount] = primeValue; // TODO: Can we calculate this using residue steppers? We can try reusing modulo calculation for division result or the other way around
exponentTargets[factorCount] = exponent; int exponent = 0;
factorCount++; do
} {
remainingLocal /= primeCandidate;
exponent++;
}
while ((remainingLocal % primeCandidate) == 0UL);
remaining = remainingLocal; if (factorCount >= capacity)
return true; {
} throw new InvalidOperationException($"Capacity is smaller than factor count");
}
private static ulong PopulateSmallPrimeFactorsCpu(ulong value, uint limit, Dictionary<ulong, int> counts) primeTargets[factorCount] = primeCandidate;
exponentTargets[factorCount] = exponent;
factorCount++;
}
remaining = remainingLocal;
return true;
}
private static ulong PopulateSmallPrimeFactorsCpu(ulong value, uint limit, Dictionary<ulong, int> counts)
{ {
ulong remaining = value; ulong remaining = value;
uint[] primes = PrimesGenerator.SmallPrimes; uint[] primes = PrimesGenerator.SmallPrimes;
@@ -1193,111 +1206,111 @@ internal static partial class PrimeOrderCalculator
return remaining; return remaining;
} }
private static bool TryPopulateSmallPrimeFactorsGpu(ulong value, uint limit, Dictionary<ulong, int> counts, out ulong remaining) private static bool TryPopulateSmallPrimeFactorsGpu(ulong value, uint limit, Dictionary<ulong, int> counts, out ulong remaining)
{ {
ulong[] primeBufferArray = ThreadStaticPools.UlongPool.Rent(GpuSmallPrimeFactorSlots); ulong[] primeBufferArray = ThreadStaticPools.UlongPool.Rent(GpuSmallPrimeFactorSlots);
int[] exponentBufferArray = ThreadStaticPools.IntPool.Rent(GpuSmallPrimeFactorSlots); int[] exponentBufferArray = ThreadStaticPools.IntPool.Rent(GpuSmallPrimeFactorSlots);
Span<ulong> primeBuffer = primeBufferArray.AsSpan(0, GpuSmallPrimeFactorSlots); Span<ulong> primeBuffer = primeBufferArray.AsSpan(0, GpuSmallPrimeFactorSlots);
Span<int> exponentBuffer = exponentBufferArray.AsSpan(0, GpuSmallPrimeFactorSlots); Span<int> exponentBuffer = exponentBufferArray.AsSpan(0, GpuSmallPrimeFactorSlots);
primeBuffer.Clear(); primeBuffer.Clear();
exponentBuffer.Clear(); exponentBuffer.Clear();
if (!PrimeOrderGpuHeuristics.TryPartialFactor(value, limit, primeBuffer, exponentBuffer, out int factorCount, out ulong gpuRemaining, out _)) if (!PrimeOrderGpuHeuristics.TryPartialFactor(value, limit, primeBuffer, exponentBuffer, out int factorCount, out ulong gpuRemaining, out _))
{ {
remaining = value; remaining = value;
ThreadStaticPools.UlongPool.Return(primeBufferArray); ThreadStaticPools.UlongPool.Return(primeBufferArray);
ThreadStaticPools.IntPool.Return(exponentBufferArray); ThreadStaticPools.IntPool.Return(exponentBufferArray);
return false; return false;
} }
remaining = gpuRemaining; remaining = gpuRemaining;
for (int i = 0; i < factorCount; i++) for (int i = 0; i < factorCount; i++)
{ {
ulong primeValue = primeBuffer[i]; ulong primeValue = primeBuffer[i];
int exponent = exponentBuffer[i]; int exponent = exponentBuffer[i];
if (primeValue == 0UL || exponent == 0) if (primeValue == 0UL || exponent == 0)
{ {
continue; continue;
} }
counts[primeValue] = exponent; counts[primeValue] = exponent;
} }
ThreadStaticPools.UlongPool.Return(primeBufferArray); ThreadStaticPools.UlongPool.Return(primeBufferArray);
ThreadStaticPools.IntPool.Return(exponentBufferArray); ThreadStaticPools.IntPool.Return(exponentBufferArray);
return true; return true;
} }
private static long CreateDeadlineTimestamp(int milliseconds) private static long CreateDeadlineTimestamp(int milliseconds)
{ {
if (milliseconds <= 0) if (milliseconds <= 0)
{ {
return long.MaxValue; return long.MaxValue;
} }
long stopwatchTicks = ConvertMillisecondsToStopwatchTicks(milliseconds); long stopwatchTicks = ConvertMillisecondsToStopwatchTicks(milliseconds);
long startTimestamp = Stopwatch.GetTimestamp(); long startTimestamp = Stopwatch.GetTimestamp();
long deadline = startTimestamp + stopwatchTicks; long deadline = startTimestamp + stopwatchTicks;
if (deadline < startTimestamp) if (deadline < startTimestamp)
{ {
return long.MaxValue; return long.MaxValue;
} }
return deadline; return deadline;
} }
private static long ConvertMillisecondsToStopwatchTicks(int milliseconds) private static long ConvertMillisecondsToStopwatchTicks(int milliseconds)
{ {
long frequency = Stopwatch.Frequency; long frequency = Stopwatch.Frequency;
long baseTicks = (frequency / 1000L) * milliseconds; long baseTicks = (frequency / 1000L) * milliseconds;
long remainder = (frequency % 1000L) * milliseconds; long remainder = (frequency % 1000L) * milliseconds;
return baseTicks + (remainder / 1000L); return baseTicks + (remainder / 1000L);
} }
private static bool TryPollardRho(ulong n, long deadlineTimestamp, out ulong factor) private static bool TryPollardRho(ulong n, long deadlineTimestamp, out ulong factor)
{ {
factor = 0UL; factor = 0UL;
if ((n & 1UL) == 0UL) if ((n & 1UL) == 0UL)
{ {
factor = 2UL; factor = 2UL;
return true; return true;
} }
long timestamp = 0L; // reused for deadline checks. long timestamp = 0L; // reused for deadline checks.
while (true) while (true)
{ {
timestamp = Stopwatch.GetTimestamp(); timestamp = Stopwatch.GetTimestamp();
if (timestamp > deadlineTimestamp) if (timestamp > deadlineTimestamp)
{ {
return false; return false;
} }
ulong c = (DeterministicRandom.NextUInt64() % (n - 1UL)) + 1UL; ulong c = (DeterministicRandom.NextUInt64() % (n - 1UL)) + 1UL;
ulong x = (DeterministicRandom.NextUInt64() % (n - 2UL)) + 2UL; ulong x = (DeterministicRandom.NextUInt64() % (n - 2UL)) + 2UL;
ulong y = x; ulong y = x;
ulong d = 1UL; ulong d = 1UL;
while (d == 1UL) while (d == 1UL)
{ {
timestamp = Stopwatch.GetTimestamp(); timestamp = Stopwatch.GetTimestamp();
if (timestamp > deadlineTimestamp) if (timestamp > deadlineTimestamp)
{ {
return false; return false;
} }
x = AdvancePolynomial(x, c, n); x = AdvancePolynomial(x, c, n);
y = AdvancePolynomial(y, c, n); y = AdvancePolynomial(y, c, n);
y = AdvancePolynomial(y, c, n); y = AdvancePolynomial(y, c, n);
ulong diff = x > y ? x - y : y - x; ulong diff = x > y ? x - y : y - x;
d = BinaryGcd(diff, n); d = BinaryGcd(diff, n);
} }
if (d != n) if (d != n)
{ {
factor = d; factor = d;
return true; return true;
} }
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong AdvancePolynomial(ulong x, ulong c, ulong modulus) private static ulong AdvancePolynomial(ulong x, ulong c, ulong modulus)