using System;
using System.Numerics;

namespace LibHac.Tests
{
    /// <summary>
    /// Simple, full-cycle PRNG for use in tests.
    /// </summary>
    public class FullCycleRandom
    {
        private int _state;
        private int _mult;
        private int _inc;
        private int _and;
        private int _max;

        public FullCycleRandom(int period, int seed)
        {
            // Avoid exponential growth pattern when initializing with a 0 seed
            seed ^= 0x55555555;
            _max = period - 1;
            int order = BitOperations.Log2((uint)period - 1) + 1;

            // There isn't any deep reasoning behind the choice of the number of bits
            // in the seed used for initializing each parameter
            int multSeedBits = Math.Max(order >> 1, 2);
            int multSeedMask = (1 << multSeedBits) - 1;
            int multSeed = seed & multSeedMask;
            _mult = (multSeed << 2) | 5;

            int incSeedBits = Math.Max(order >> 2, 2);
            int incSeedMask = (1 << incSeedBits) - 1;
            int incSeed = (seed >> multSeedBits) & incSeedMask;
            _inc = incSeed | 1;

            int stateSeedBits = order;
            int stateSeedMask = (1 << stateSeedBits) - 1;
            int stateSeed = (seed >> multSeedBits + incSeedBits) & stateSeedMask;
            _state = stateSeed;

            _and = (1 << order) - 1;
        }

        public int Next()
        {
            do
            {
                _state = (_state * _mult + _inc) & _and;
            } while (_state > _max);

            return _state;
        }
    }
}