64 Bit RNG using RngJitterSource.cs
Updated: Dec-26,2020
Example Code:
Add a 256x256 panel to a form.
var lrng = new Random64(65536, 256, 2);
var g = panel1.CreateGraphics();
var bm = new Bitmap(256, 256);
var buf = lrng.GetNextBoolArrayLimit(65536);
var ptr = 0;
for (var y = 0; y < 256; y++)
for (var x = 0; x < 256; x++)
{
var r = buf[ptr++];
if (r)
{
bm.SetPixel(x, y, Color.White);
}
else
{
bm.SetPixel(x, y, Color.Black);
}
}
g.DrawImageUnscaled(bm, 0, 0);
using System;
using System.Security.Cryptography;
[Serializable]
public class Random64 : RandomNumberGenerator
{
private byte[] _buffer;
public RngJitterSource _crng;
private double _dBi;
private ulong UpperLimit = ulong.MaxValue;
public Random64()
{
SetDataUse = 8;
_crng = new RngJitterSource();
}
public Random64(int cacheSize)
{
SetDataUse = 8;
_crng = new RngJitterSource(cacheSize * 8, 256, 256, 4);
}
public Random64(int cacheSize, int seedSize)
{
SetDataUse = 8;
_crng = new RngJitterSource(cacheSize * 8, seedSize, 256, 4);
}
public Random64(int cacheSize, int seedSize, int dataSize, int sha3Size = 256, int sha3Rounds = 4)
{
SetDataUse = dataSize;
_crng = new RngJitterSource(cacheSize * dataSize, seedSize, sha3Size, sha3Rounds);
}
public int SetDataUse
{
get => _buffer.Length;
set
{
var v = value;
if (v < 1 || v > 8 || v == 3 || v == 5 || v == 6 || v == 7)
throw new ArgumentException($"Value {v} must be either 1 or 2 or 4 or 8");
switch (v)
{
case 1:
_dBi = 1.0D / byte.MaxValue;
UpperLimit = byte.MaxValue;
_buffer = new byte[1];
break;
case 2:
_dBi = 1.0D / ushort.MaxValue;
UpperLimit = ushort.MaxValue;
_buffer = new byte[2];
break;
case 4:
_dBi = 1.0D / uint.MaxValue;
UpperLimit = uint.MaxValue;
_buffer = new byte[4];
break;
case 8:
_dBi = 1.0D / ulong.MaxValue;
UpperLimit = ulong.MaxValue;
_buffer = new byte[8];
break;
default:
_dBi = 1.0D / ulong.MaxValue;
UpperLimit = ulong.MaxValue;
_buffer = new byte[8];
break;
}
}
}
public bool OddsOnly
{
get;
set;
}
private double Sample()
{
ulong Internal()
{
_crng.GetBytes(_buffer);
return BufferToLong(_buffer);
}
return Internal() * _dBi;
}
public ulong Next(ulong minValue, ulong maxValue)
{
var sa = Sample();
var fi = (double) (maxValue - minValue + minValue);
var n = (ulong) (sa * fi);
n = !OddsOnly ? n : n | 1;
if (n < minValue)
return Next(minValue, maxValue);
return n;
}
public ulong Next(ulong maxValue)
{
return Next(0, maxValue);
}
public ulong Next()
{
return Next(0, UpperLimit);
}
public unsafe double NextDouble()
{
var buf = new byte[8];
GetBytes(buf);
fixed (byte* ptr = buf)
{
return *(ulong*) ptr * _dBi * ulong.MaxValue;
}
}
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 >= 32 && c <= 127)
ca[ptr++] = (char) c;
} while (ptr < size);
return ca;
}
public char[] GetNextCharArrayX(int size)
{
var xbc = new byte[2];
var ca = new char[size];
var ptr = 0;
do
{
_crng.GetBytes(xbc);
ca[ptr++] = Convert.ToChar(xbc.ToShort());
} while (ptr < size);
return ca;
}
public byte[] GetNextByteArray(int size)
{
var ba = new byte[size];
_crng.GetBytes(ba);
return ba;
}
public bool[] GetNextBoolArrayLimit(int size)
{
var ba = new bool[size];
const uint ll = uint.MaxValue >> 1;
for (var i = 0; i < size; ++i)
ba[i] = Next(0, uint.MaxValue) > ll;
return ba;
}
public byte[] GetNextByteArrayLimit(int size, ulong minValue, ulong maxValue)
{
var ba = new byte[size];
for (var i = 0; i < size; ++i)
ba[i] = (byte) Next(minValue, maxValue);
return ba;
}
public ushort[] GetNextUShortArrayLimit(int size, ulong minValue, ulong maxValue)
{
var ba = new ushort[size];
for (var i = 0; i < size; ++i)
ba[i] = (ushort) Next(minValue, maxValue);
return ba;
}
public uint[] GetNextUIntArrayLimit(int size, ulong minValue, ulong maxValue)
{
var ba = new uint[size];
for (var i = 0; i < size; ++i)
ba[i] = (uint) Next(minValue, maxValue);
return ba;
}
public ulong[] GetNextULongArrayLimit(int size, ulong minValue, ulong maxValue)
{
var ba = new ulong[size];
for (var i = 0; i < size; ++i)
ba[i] = Next(minValue, maxValue);
return ba;
}
public string GetRandomString(int minLen, int maxLen)
{
if (minLen == maxLen)
return new string(GetNextCharArray(minLen));
return new string(GetNextCharArray((int) Next((ulong) minLen, (ulong) maxLen)));
}
public override void GetBytes(byte[] data)
{
if (data == null)
throw new ArgumentException("The buffer cannot be null.");
_crng.GetBytes(data);
}
public void NextBytes(byte[] buffer)
{
if (buffer == null)
throw new ArgumentNullException("The buffer cannot be null.");
for (var index = 0; index < buffer.Length; ++index)
buffer[index] = (byte) (Sample() * byte.MaxValue + 1);
}
public override void GetNonZeroBytes(byte[] buffer)
{
if (buffer == null)
throw new ArgumentNullException("The buffer cannot be null.");
var index = 0;
do
{
var v = (byte) (Sample() * byte.MaxValue + 1);
if (v > 0)
{
buffer[index] = v;
index++;
}
} while (index < buffer.Length);
}
private unsafe ulong BufferToLong(byte[] buffer)
{
var len = buffer.Length;
if (len < 1 || len > 8)
throw new ArgumentException($"The array length {len} must be between 1 and 8");
fixed (byte* Ptr = &buffer[0])
{
switch (len)
{
case 1:
return *Ptr;
case 2:
return (uint) (*Ptr | (Ptr[1] << 8));
case 3:
return (uint) (*Ptr | (Ptr[1] << 8) | (Ptr[2] << 16));
case 4:
return (uint) (*Ptr | (Ptr[1] << 8) | (Ptr[2] << 16) | (Ptr[3] << 24));
case 5:
return (uint) (*Ptr | (Ptr[1] << 8) | (Ptr[2] << 16) | (Ptr[3] << 24)) | ((ulong) Ptr[4] << 32);
case 6:
return (uint) (*Ptr | (Ptr[1] << 8) | (Ptr[2] << 16) | (Ptr[3] << 24)) | ((ulong) (Ptr[4] | (Ptr[5] << 8)) << 32);
case 7:
return (uint) (*Ptr | (Ptr[1] << 8) | (Ptr[2] << 16) | (Ptr[3] << 24)) | ((ulong) (Ptr[4] | (Ptr[5] << 8) | (Ptr[6] << 16)) << 32);
case 8:
return (uint) (*Ptr | (Ptr[1] << 8) | (Ptr[2] << 16) | (Ptr[3] << 24)) | ((ulong) (Ptr[4] | (Ptr[5] << 8) | (Ptr[6] << 16) | (Ptr[7] << 24)) << 32);
default:
return 0;
}
}
}
}