using System.Runtime.CompilerServices; using PerfectNumbers.Core; 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 ulong _modulus; private readonly ReadOnlyGpuUInt128 _modulusWide; public ulong PreviousExponent; private GpuUInt128 _currentResidue; [MethodImpl(MethodImplOptions.AggressiveInlining)] public ExponentRemainderStepperGpu(in GpuDivisorPartialData divisor) { ulong modulus = divisor.Modulus; _modulus = modulus; _modulusWide = divisor.ModulusWide; PreviousExponent = 0UL; _currentResidue = new GpuUInt128(1UL); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MatchesDivisor(in GpuDivisorPartialData divisor) { return _modulus == divisor.Modulus; } /// /// Clears the cached 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; _currentResidue = new GpuUInt128(1UL); } /// /// 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) { InitializeStateGpu(exponent); return _currentResidue.Low; } /// /// 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 residue equal to one. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool InitializeIsUnityGpu(ulong exponent) { InitializeStateGpu(exponent); return _currentResidue.Low == 1UL; } /// /// 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) { AdvanceStateGpu(exponent); return _currentResidue.Low; } /// /// 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 ComputeNextIsUnityGpu(ulong exponent) { AdvanceStateGpu(exponent); return _currentResidue.Low == 1UL; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void InitializeStateGpu(ulong exponent) { ulong residue = ULongExtensions.Pow2ModWindowedGpuKernel(exponent, _modulus); _currentResidue = new GpuUInt128(residue); PreviousExponent = exponent; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void AdvanceStateGpu(ulong exponent) { ulong delta = exponent - PreviousExponent; ulong multiplier = ULongExtensions.Pow2ModWindowedGpuKernel(delta, _modulus); _currentResidue.MulMod(multiplier, _modulusWide); PreviousExponent = exponent; } }