using System; using System.Numerics; namespace PerfectNumbers.Core; internal struct CycleRemainderStepper { private readonly ulong _cycleLength; private ulong _previousPrime; private ulong _currentRemainder; private bool _hasState; public CycleRemainderStepper(ulong cycleLength) { if (cycleLength == 0UL) { throw new ArgumentOutOfRangeException(nameof(cycleLength), "Cycle length must be non-zero."); } _cycleLength = cycleLength; _previousPrime = 0UL; _currentRemainder = 0UL; _hasState = false; } public void Reset() { _previousPrime = 0UL; _currentRemainder = 0UL; _hasState = false; } public ulong Initialize(ulong prime) { ulong cycleLength = _cycleLength; ulong remainder = prime; if (remainder >= cycleLength) { // TODO: Swap this `%` for the shared divisor-cycle remainder helper so initialization reuses the cached // cycle deltas benchmarked faster than on-the-fly modulo work. remainder %= cycleLength; } _previousPrime = prime; _currentRemainder = remainder; _hasState = true; return remainder; } public ulong ComputeNext(ulong prime) { if (!_hasState) { throw new InvalidOperationException("CycleRemainderStepper must be initialized before computing."); } if (prime <= _previousPrime) { throw new ArgumentOutOfRangeException(nameof(prime), "Primes must be processed in strictly increasing order."); } ulong cycleLength = _cycleLength; ulong delta = prime - _previousPrime; _previousPrime = prime; if (ulong.MaxValue - _currentRemainder < delta) { UInt128 extended = (UInt128)_currentRemainder + delta; // TODO: Replace this `%` with the UInt128-aware cycle reducer so large deltas use the cached subtraction // ladder instead of falling back to the slower modulo implementation. _currentRemainder = (ulong)(extended % cycleLength); return _currentRemainder; } _currentRemainder += delta; if (_currentRemainder >= cycleLength) { _currentRemainder -= cycleLength; if (_currentRemainder >= cycleLength) { // TODO: Route this `%` through the shared divisor-cycle helper so repeated wrap-arounds avoid // modulo operations and match the benchmarked fast path. _currentRemainder %= cycleLength; } } return _currentRemainder; } public void CaptureState(out ulong previousPrime, out ulong previousRemainder, out bool hasState) { previousPrime = _previousPrime; previousRemainder = _currentRemainder; hasState = _hasState; } }