BigIntegerRng.cs

Fast Balanced Distribution BigInteger Rng

using System;
using System.Linq;
using System.Numerics;
using System.Security.Cryptography;
[Serializable]
public class BigIntegerRng : RandomNumberGenerator
{
    private int                      _bitWidth;
    private byte[]                   _buffer;
    public  RNGCryptoServiceProvider _crng;
    private BigDecimal               _dBi;
    private BigInteger               _upperLimit;
    public BigIntegerRng(int bitWidthHint)
    {
        _crng       = new RNGCryptoServiceProvider();
        _upperLimit = (BigInteger.One << bitWidthHint) - 1;
        _dBi        = new BigDecimal(1) / _upperLimit;
        _buffer     = new byte[bitWidthHint >> 3];
        _bitWidth   = bitWidthHint;
    }
    public bool OddsOnly
    {
        get;
        set;
    }
    public bool Unsigned
    {
        get;
        set;
    }
    private BigDecimal Sample(BigDecimal weight)
    {
        BigInteger Internal()
        {
            _crng.GetBytes(_buffer);
            return new BigInteger(!Unsigned ? _buffer : _buffer.Concat(new byte[] {0}).ToArray());
        }
        var        rat  = Internal() * _dBi;
        BigDecimal tens = 10;
        if (weight != 0 && weight > .9)
        {
            if (rat < weight)
            {
                var or = rat / tens + weight;
                while (or > 1)
                {
                    tens *= 10;
                    or   =  rat / tens + weight;
                }
                return or;
            }
            return rat;
        }
        return rat;
    }
    public BigInteger Next(BigInteger minValue, BigInteger maxValue)
    {
        BestBitWidth(maxValue);
        var sa = Sample(0);
        var fi = (BigDecimal) (maxValue - minValue + minValue);
        var n  = (BigInteger) (sa * fi);
        n = !OddsOnly ? n : n | 1;
        return n < minValue ? SpecialRange(minValue, maxValue) : n;
    }
    private BigInteger SpecialRange(BigInteger minValue, BigInteger maxValue)
    {
        BigInteger res;
        var        fi     = (BigDecimal) (maxValue - minValue + minValue);
        var        weight = (BigDecimal) minValue / _upperLimit;
        do
        {
            var n = (BigInteger) (Sample(weight) * fi);
            res = !OddsOnly ? n : n | 1;
        } while (res > maxValue || res < minValue);
        return res;
    }
    private void BestBitWidth(BigInteger maxValue)
    {
        var bitWidth = maxValue.GetBitWidth();
        if (_bitWidth != bitWidth)
        {
            _upperLimit = (BigInteger.One << bitWidth) - 1;
            _dBi        = new BigDecimal(1) / _upperLimit;
            _buffer     = new byte[bitWidth >> 3];
            _bitWidth   = bitWidth;
        }
    }
    public BigInteger Next(BigInteger maxValue)
    {
        return Next(0, maxValue);
    }
    public BigInteger Next()
    {
        return Next(0, _upperLimit);
    }
    public BigDecimal NextBigDecimal()
    {
        return Sample(0) * _upperLimit;
    }
    public override void GetBytes(byte[] data)
    {
        if (data == null)
            throw new ArgumentException("The buffer cannot be null.");
        _crng.GetBytes(data);
    }
    public BigInteger NextFast(int bitWidth)
    {
        if (_bitWidth != bitWidth)
        {
            _buffer   = new byte[bitWidth >> 3];
            _bitWidth = bitWidth;
        }
        _crng.GetBytes(_buffer);
        return new BigInteger(!Unsigned ? _buffer : _buffer.Concat(new byte[] {0}).ToArray());
    }
}