using System.Runtime.CompilerServices;
namespace PerfectNumbers.Core.Gpu;
///
/// Tracks successive residues for a single divisor while kernels advance exponent candidates on the GPU path.
/// Callers must seed the stepper once per divisor and then supply exponents in strictly ascending order.
///
internal struct ExponentRemainderStepperGpu
{
private readonly MontgomeryDivisorData _divisor;
private readonly ulong _modulus;
private readonly ulong _nPrime;
private readonly ulong _montgomeryOne;
private ulong _previousExponent;
private ulong _currentMontgomery;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ExponentRemainderStepperGpu(in MontgomeryDivisorData divisor)
{
_divisor = divisor;
_modulus = divisor.Modulus;
_nPrime = divisor.NPrime;
_montgomeryOne = divisor.MontgomeryOne;
_previousExponent = 0UL;
_currentMontgomery = divisor.MontgomeryOne;
}
public ulong PreviousExponent
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _previousExponent;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MatchesDivisor(in MontgomeryDivisorData divisor)
{
return _modulus == divisor.Modulus && _nPrime == divisor.NPrime && _montgomeryOne == divisor.MontgomeryOne;
}
///
/// Clears the cached Montgomery residue so the stepper can be reused for another divisor.
/// Callers must reinitialize the stepper before consuming any residues after a reset.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset()
{
_previousExponent = 0UL;
_currentMontgomery = _montgomeryOne;
}
///
/// Initializes the stepper for GPU calculations using the first exponent in a strictly ascending sequence.
/// The returned value is the canonical residue corresponding to the provided exponent.
///
/// First exponent evaluated for the current divisor. The caller must reuse the same divisor metadata.
/// The canonical residue produced by 2^exponent mod divisor.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ulong InitializeGpu(ulong exponent)
{
InitializeGpuState(exponent);
return ReduceCurrent();
}
///
/// Initializes the GPU stepping state and reports whether the seed exponent already yields unity.
///
/// First exponent evaluated for the current divisor. Must match the divisor passed to the constructor.
/// when the seed exponent produces a Montgomery residue equal to one.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool InitializeGpuIsUnity(ulong exponent)
{
InitializeGpuState(exponent);
return _currentMontgomery == _montgomeryOne;
}
///
/// Advances the GPU stepping state to the next exponent in a strictly ascending sequence that shares the same divisor.
/// Callers must invoke beforehand.
///
/// Next exponent in the ascending sequence. Callers must never move backwards or reuse an older exponent.
/// The canonical residue produced by 2^exponent mod divisor.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ulong ComputeNextGpu(ulong exponent)
{
AdvanceGpu(exponent);
return ReduceCurrent();
}
///
/// Advances the GPU stepping state to the next exponent and reports whether it evaluates to unity.
/// Callers must seed the stepper through or and then
/// pass exponents in strictly ascending order.
///
/// Next exponent in the ascending sequence.
/// when the canonical residue equals one.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ComputeNextGpuIsUnity(ulong exponent)
{
AdvanceGpu(exponent);
return _currentMontgomery == _montgomeryOne;
}
///
/// Seeds the stepper with a precomputed Montgomery residue produced by the caller.
/// The provided exponent becomes the new baseline and all subsequent calls must continue with greater exponents.
///
/// Exponent associated with .
/// Residue in Montgomery form produced externally for the configured divisor.
/// Outputs whether the supplied residue equals Montgomery one.
/// when the residue is accepted and cached.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryInitializeFromMontgomeryResult(ulong exponent, ulong montgomeryResult, out bool isUnity)
{
_currentMontgomery = montgomeryResult;
_previousExponent = exponent;
isUnity = _currentMontgomery == _montgomeryOne;
return true;
}
///
/// Advances the stepper using a caller-supplied Montgomery delta that represents the gap to the next exponent.
/// Callers must seed the stepper first, reuse the same divisor metadata, and continue feeding exponents in ascending order so the delta remains valid.
///
/// Next exponent in the ascending sequence.
/// Montgomery residue delta corresponding to the exponent gap.
/// Outputs whether the resulting residue equals Montgomery one.
/// when the advance succeeds.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryAdvanceWithMontgomeryDelta(ulong exponent, ulong montgomeryDelta, out bool isUnity)
{
_currentMontgomery = _currentMontgomery.MontgomeryMultiply(montgomeryDelta, _modulus, _nPrime);
_previousExponent = exponent;
isUnity = _currentMontgomery == _montgomeryOne;
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void InitializeGpuState(ulong exponent)
{
_currentMontgomery = ULongExtensions.Pow2MontgomeryModWindowedGpuKeepMontgomery(_divisor, exponent);
_previousExponent = exponent;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AdvanceGpu(ulong exponent)
{
ulong delta = exponent - _previousExponent;
ulong multiplier = ULongExtensions.Pow2MontgomeryModWindowedGpuKeepMontgomery(_divisor, delta);
_currentMontgomery = _currentMontgomery.MontgomeryMultiply(multiplier, _modulus, _nPrime);
_previousExponent = exponent;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ulong ReduceCurrent() => _currentMontgomery.MontgomeryMultiply(1UL, _modulus, _nPrime);
}