Random64.cs

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;
            }
        }
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *