// ----------------------------------------------------------------------------
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ----------------------------------------------------------------------------

using System;

namespace Microsoft.Singularity.Crypto.PublicKey
{
    public class Random {
        uint[] _state;
        const int _N = 8;
        void _Mix() {
            uint[] square = new uint[_N];
            ulong carry = 0;
            for (int n = 0; n < _N; n++) {
                ulong t = carry;
                carry = 0;
                for (int i = 0; i <= n; i++) {
                    int j = n - i;
                    t += (ulong)_state[i] * _state[j];
                    carry += t >> 32;
                    t = unchecked((uint)t);
                }
                square[n] = (uint)t;
                carry += t >> 32;
            }
            square[0] |= 5;
            carry = 0;
            for (int i = 0; i < _N; i++) {
                ulong t = carry + _state[i] + square[i];
                _state[i] = unchecked((uint)t);
                carry = t >> 32;
            }
        }
        public uint Next() { _Mix(); return _state[_N - 1]; }
        public uint Next(uint n) {
            if (n <= 0) {
                throw new ArgumentException();
            }
            uint mask = 1;
            while (mask < n - 1) {
                mask = mask << 1 | 1;
            }
            while (true) {
                uint next = Next() & mask;
                if (next < n) {
                    return next;
                }
            }
        }
        public uint Next(uint lo, uint hi) {
            if (lo >= hi) {
                throw new ArgumentException();
            }
            return lo + Next(hi - lo);
        }
        public Random(uint n) {
            _state = new uint[_N];
            for (int i = 0; i < _N; i++) {
                _state[i] = n;
            }
        }
        public Random() : this(1372455507) { } // Randomly selected 32-bit integer
    }
}
