Created
January 12, 2021 14:05
-
-
Save mjs3339/c716d35b2dc9d383269ed177bf296560 to your computer and use it in GitHub Desktop.
A Fast Random Number Generator Based on BigInteger
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment