A Fast Random Number Generator Based on BigInteger
using System; using System.Numerics; using System.Security.Cryptography; public struct RandomX { private readonly RNGCryptoServiceProvider _crng; private int _maxByteWidth; private int _bitWidth; public RandomX(int bitWidth) { MaxValue = BigIntegerHelper.GetMaxValueBitWidth(bitWidth); _maxByteWidth = bitWidth >> 3; OddsOnly = false; Unsigned = false; _crng = new RNGCryptoServiceProvider(); _bitWidth = bitWidth; } public bool OddsOnly; public bool Unsigned; public int BitWidth { get => _bitWidth; set { _bitWidth = value; MaxValue = BigIntegerHelper.GetMaxValueBitWidth(_bitWidth); _maxByteWidth = _bitWidth >> 3; } } public BigInteger MaxValue; public bool NextBool() { return Sample() < .5; } public char NextChar(char maxValue) { if (maxValue < 0) throw new ArgumentException("maxValue must be greater than zero."); if (!Unsigned) Unsigned = true; if (_bitWidth < 16) BitWidth = 16; return (char) (Sample() * maxValue); } public char NextChar(char minValue, char maxValue) { if (!Unsigned) Unsigned = true; if (_bitWidth < 16) BitWidth = 16; return (char) Next(minValue, maxValue); } public sbyte NextInt8() { if (Unsigned) Unsigned = false; if (_bitWidth < 8) BitWidth = 8; return (sbyte) Internal(); } public sbyte NextInt8(sbyte maxValue) { if (maxValue < 0) throw new ArgumentException("maxValue must be greater than zero."); if (Unsigned) Unsigned = false; if (_bitWidth < 8) BitWidth = 8; return (sbyte) (Sample() * maxValue); } public sbyte NextInt8(sbyte minValue, sbyte maxValue) { if (minValue > maxValue) throw new ArgumentException("maxValue must be greater than or equal to minValue"); if (Unsigned) Unsigned = false; if (_bitWidth < 8) BitWidth = 8; return (sbyte) Next(minValue, maxValue); } public byte NextUInt8() { if (!Unsigned) Unsigned = true; if (_bitWidth < 8) BitWidth = 8; var n = Internal(); return (byte) n; } public byte NextUInt8(byte maxValue) { if (!Unsigned) Unsigned = true; if (_bitWidth < 8) BitWidth = 8; return (byte) (Sample() * maxValue); } public byte NextUInt8(byte minValue, byte maxValue) { if (minValue > maxValue) throw new ArgumentException("maxValue must be greater than or equal to minValue"); if (!Unsigned) Unsigned = true; if (_bitWidth < 8) BitWidth = 8; return (byte) Next(minValue, maxValue); } public short NextInt16() { if (_bitWidth < 16) BitWidth = 16; if (Unsigned) Unsigned = false; return (short) Internal(); } public short NextInt16(short maxValue) { if (maxValue < 0) throw new ArgumentException("maxValue must be greater than zero."); if (Unsigned) Unsigned = false; if (_bitWidth < 16) BitWidth = 16; return (short) (Sample() * maxValue); } public short NextInt16(short minValue, short maxValue) { if (minValue > maxValue) throw new ArgumentException("maxValue must be greater than or equal to minValue"); if (Unsigned) Unsigned = false; if (_bitWidth < 16) BitWidth = 16; return (short) Next(minValue, maxValue); } public ushort NextUInt16() { if (!Unsigned) Unsigned = true; if (_bitWidth < 16) BitWidth = 16; return (ushort) Internal(); } public ushort NextUInt16(ushort maxValue) { if (!Unsigned) Unsigned = true; if (_bitWidth < 16) BitWidth = 16; return (ushort) (Sample() * maxValue); } public ushort NextUInt16(ushort minValue, ushort maxValue) { if (minValue > maxValue) throw new ArgumentException("maxValue must be greater than or equal to minValue"); if (!Unsigned) Unsigned = true; if (_bitWidth < 16) BitWidth = 16; return (ushort) Next(minValue, maxValue); } public int NextInt24() { BitWidth = 24; if (Unsigned) Unsigned = false; return (int) Internal(); } public int NextInt24(int maxValue) { if (maxValue < 0) throw new ArgumentException("maxValue must be greater than zero."); if (Unsigned) Unsigned = false; BitWidth = 24; return (int) (Sample() * maxValue); } public int NextInt24(int minValue, int maxValue) { if (minValue > maxValue) throw new ArgumentException("maxValue must be greater than or equal to minValue"); if (Unsigned) Unsigned = false; BitWidth = 24; return (int) Next(minValue, maxValue); } public uint NextUInt24() { if (!Unsigned) Unsigned = true; BitWidth = 24; return (uint) Internal(); } public uint NextUInt24(uint maxValue) { if (!Unsigned) Unsigned = true; BitWidth = 24; return (uint) (Sample() * maxValue); } public uint NextUInt24(uint minValue, uint maxValue) { if (minValue > maxValue) throw new ArgumentException("maxValue must be greater than or equal to minValue"); if (!Unsigned) Unsigned = true; BitWidth = 24; return (uint) Next(minValue, maxValue); } public uint NextUInt32() { if (!Unsigned) Unsigned = true; if (_bitWidth < 32) BitWidth = 32; return (uint) Internal(); } public uint NextUInt32(uint maxValue) { if (!Unsigned) Unsigned = true; if (_bitWidth < 32) BitWidth = 32; return (uint) (Sample() * maxValue); } public uint NextUInt32(uint minValue, uint maxValue) { if (minValue > maxValue) throw new ArgumentException("maxValue must be greater than or equal to minValue"); if (!Unsigned) Unsigned = true; if (_bitWidth < 32) BitWidth = 32; return (uint) Next(minValue, maxValue); } public int NextInt32() { if (Unsigned) Unsigned = false; if (_bitWidth < 32) BitWidth = 32; return (int) Internal(); } public int NextInt32(int maxValue) { if (maxValue < 0) throw new ArgumentException("maxValue must be greater than zero."); if (Unsigned) Unsigned = false; if (_bitWidth < 32) BitWidth = 32; return (int) (Sample() * maxValue); } public int NextInt32(int minValue, int maxValue) { if (minValue > maxValue) throw new ArgumentException("maxValue must be greater than or equal to minValue"); if (Unsigned) Unsigned = false; if (_bitWidth < 32) BitWidth = 32; return (int) Next(minValue, maxValue); } public long NextUInt40() { if (!Unsigned) Unsigned = true; BitWidth = 40; return (long) Internal(); } public long NextUInt40(long maxValue) { if (!Unsigned) Unsigned = true; BitWidth = 40; return (long) (Sample() * maxValue); } public long NextUInt40(long minValue, long maxValue) { if (minValue > maxValue) throw new ArgumentException("maxValue must be greater than or equal to minValue"); if (!Unsigned) Unsigned = true; BitWidth = 40; return (long) Next(minValue, maxValue); } public long NextInt40() { if (Unsigned) Unsigned = false; BitWidth = 40; return (long) Internal(); } public long NextInt40(long maxValue) { if (maxValue < 0) throw new ArgumentException("maxValue must be greater than zero."); if (Unsigned) Unsigned = false; BitWidth = 40; return (long) (Sample() * maxValue); } public long NextInt40(long minValue, long maxValue) { if (minValue > maxValue) throw new ArgumentException("maxValue must be greater than or equal to minValue"); if (Unsigned) Unsigned = false; BitWidth = 40; return (long) Next(minValue, maxValue); } public ulong NextUInt48() { if (!Unsigned) Unsigned = true; BitWidth = 48; return (ulong) Internal(); } public ulong NextUInt48(ulong maxValue) { if (!Unsigned) Unsigned = true; BitWidth = 48; return (ulong) (Sample() * maxValue); } public ulong NextUInt48(ulong minValue, ulong maxValue) { if (minValue > maxValue) throw new ArgumentException("maxValue must be greater than or equal to minValue"); if (!Unsigned) Unsigned = true; BitWidth = 48; return (ulong) Next(minValue, maxValue); } public long NextInt48() { if (Unsigned) Unsigned = false; BitWidth = 48; return (long) Internal(); } public long NextInt48(long maxValue) { if (maxValue < 0) throw new ArgumentException("maxValue must be greater than zero."); if (Unsigned) Unsigned = false; BitWidth = 48; return (long) (Sample() * maxValue); } public ulong NextUInt56() { if (!Unsigned) Unsigned = true; BitWidth = 56; return (ulong) Internal(); } public ulong NextUInt56(ulong maxValue) { if (!Unsigned) Unsigned = true; BitWidth = 56; return (ulong) (Sample() * maxValue); } public ulong NextUInt56(ulong minValue, ulong maxValue) { if (minValue > maxValue) throw new ArgumentException("maxValue must be greater than or equal to minValue"); if (!Unsigned) Unsigned = true; BitWidth = 56; return (uint) Next(minValue, maxValue); } public long NextInt56() { if (Unsigned) Unsigned = false; BitWidth = 56; return (long) Internal(); } public long NextInt56(long maxValue) { if (maxValue < 0) throw new ArgumentException("maxValue must be greater than zero."); if (Unsigned) Unsigned = false; BitWidth = 56; return (long) (Sample() * maxValue); } public long NextInt56(long minValue, long maxValue) { if (minValue > maxValue) throw new ArgumentException("maxValue must be greater than or equal to minValue"); if (Unsigned) Unsigned = false; BitWidth = 48; return (long) Next(minValue, maxValue); } public long NextInt64() { if (Unsigned) Unsigned = false; if (_bitWidth < 64) BitWidth = 64; return (long) Internal(); } public long NextInt64(long maxValue) { if (maxValue < 0) throw new ArgumentException("maxValue must be greater than zero."); if (Unsigned) Unsigned = false; if (_bitWidth < 64) BitWidth = 64; return (long) (Sample() * maxValue); } public long NextInt64(long minValue, long maxValue) { if (minValue > maxValue) throw new ArgumentException("maxValue must be greater than or equal to minValue"); if (Unsigned) Unsigned = false; if (_bitWidth < 64) BitWidth = 64; return (long) Next(minValue, maxValue); } public ulong NextUInt64() { if (!Unsigned) Unsigned = true; if (_bitWidth < 64) BitWidth = 64; return (ulong) Internal(); } public ulong NextUInt64(ulong maxValue) { if (!Unsigned) Unsigned = true; if (_bitWidth < 64) BitWidth = 64; return (ulong) (Sample() * maxValue); } public ulong NextUInt64(ulong minValue, ulong maxValue) { if (minValue > maxValue) throw new ArgumentException("maxValue must be greater than or equal to minValue"); if (!Unsigned) Unsigned = true; if (_bitWidth < 64) BitWidth = 64; return (ulong) Next(minValue, maxValue); } public BigInteger Next() { return Internal(); } public BigInteger Next(BigInteger minValue, BigInteger maxValue) { if (minValue > maxValue) throw new ArgumentException("maxValue must be greater than or equal to minValue"); return (BigInteger) (Sample() * (maxValue - minValue)) + minValue; } public UInt512 Next(UInt512 minValue, UInt512 maxValue) { if (minValue > maxValue) throw new ArgumentException("maxValue must be greater than or equal to minValue"); var s = Sample(); var f = (BigDecimal) ((BigInteger) maxValue - (BigInteger) minValue); return (BigInteger) (s * f); } public BigInteger Next(BigInteger maxValue) { if (maxValue < 0) throw new ArgumentException("maxValue must be greater than zero."); return (BigInteger) (Sample() * maxValue); } public unsafe double NextDouble() { var buf = new byte[8]; GetBytes(buf); fixed (byte* ptr = buf) { return *(ulong*) ptr * (1.0 / ulong.MaxValue) * ulong.MaxValue; } } public BigRational NextBigRational() { return new BigRational(Internal(), Internal()); } public decimal NextDecimal() { return new decimal(NextInt32(), NextInt32(), NextInt32(), NextBool(), NextUInt8(255)); } public BigDecimal NextBigDecimal() { return Sample(); } public byte[] GetNextByteArray(int size) { var ba = new byte[size]; _crng.GetBytes(ba); return ba; } public char[] GetNextCharArray(int size) { var xbc = new byte[1]; var ca = new char[size]; var ptr = 0; do { _crng.GetBytes(xbc); var c = xbc[0]; if (c >= 0x20 && c <= 0x7F) ca[ptr++] = (char) c; } while (ptr < size); return ca; } public char NextChar() { var xbc = new byte[1]; while (true) { _crng.GetBytes(xbc); var c = xbc[0]; if (c >= 0x20 && c <= 0x7F) return (char) c; } } public string GetRandomString(int minLen, int maxLen) { return minLen == maxLen ? new string(GetNextCharArray(minLen)) : new string(GetNextCharArray((int) NextUInt32((uint) minLen, (uint) maxLen))); } private BigDecimal Sample() { var i = Internal(); var s = i * (BigDecimal.One / MaxValue); if (s.Sign == -1) s = s * -1; if (s.IsZero) throw new Exception("Sample is zero."); return s; } public void GetBytes(byte[] data) { if (data == null) throw new ArgumentException("The buffer cannot be null."); _crng.GetBytes(data); } private BigInteger Internal() { if (Unsigned) { var buffer = new byte[_maxByteWidth + 1]; _crng.GetBytes(buffer); buffer[_maxByteWidth] = 0; var n = new BigInteger(buffer); return !OddsOnly ? n : n | 1; } else { var buffer = new byte[_maxByteWidth]; _crng.GetBytes(buffer); var n = new BigInteger(buffer); return !OddsOnly ? n : n | 1; } } }