using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Security.Cryptography;
namespace DominionBase.Utilities
{
public static class Shuffler
{
private static DominionRandom provider;
///
/// Performs an in-place shuffle of the list provided using strong crypto to (virtually) guarantee a randomized shuffling
///
/// Type of object to be shuffled (unimportant for this method)
/// List of items to be shuffled
public static void Shuffle(this IList list)
{
Contract.Requires(list != null, "list cannot be null");
if (provider == null)
provider = new DominionRandom();
var n = list.Count;
while (n > 1)
{
n--;
var k = provider.Next(n + 1);
var value = list[k];
list[k] = list[n];
list[n] = value;
}
}
///
/// Chooses a random item from the list provided, using strong crypto to (virtually) guarantee a natural distribution
///
/// Type of object to be chosen at random
/// List of items to choose from
/// An item from the list, chosen at random
public static T Choose(this IList list)
{
Contract.Requires(list != null, "list cannot be null");
if (provider == null)
provider = new DominionRandom();
return list[provider.Next(list.Count)];
}
}
public class DominionRandom
{
private const int BufferSize = 1024; // must be a multiple of 4
private readonly byte[] RandomBuffer;
private int BufferOffset;
private readonly RNGCryptoServiceProvider rng;
public DominionRandom()
{
RandomBuffer = new byte[BufferSize];
rng = new RNGCryptoServiceProvider();
BufferOffset = RandomBuffer.Length;
}
private void FillBuffer()
{
rng.GetBytes(RandomBuffer);
BufferOffset = 0;
}
public int Next()
{
if (BufferOffset >= RandomBuffer.Length)
{
FillBuffer();
}
var val = BitConverter.ToInt32(RandomBuffer, BufferOffset) & 0x7fffffff;
BufferOffset += sizeof(int);
return val;
}
public int Next(int maxValue)
{
return Next() % maxValue;
}
public int Next(int minValue, int maxValue)
{
Contract.Requires(maxValue >= minValue, "maxValue >= minValue");
var range = maxValue - minValue;
return minValue + Next(range);
}
public double NextDouble()
{
var val = Next();
return (double)val / int.MaxValue;
}
public void GetBytes(byte[] buff)
{
rng.GetBytes(buff);
}
}
public static class Gaussian
{
private static bool _HaveNextNextGaussian;
private static double _NextNextGaussian;
private static Random _Random;
private static readonly object _RNGLock = new object();
///
/// Returns a random number with a Gaussian distribution with a mean of 0 and a standard deviation of 1
///
/// A random number with a Gaussian distribution with a mean of 0 and a standard deviation of 1
public static double NextGaussian()
{
return NextGaussian(null);
}
///
/// Returns a random number with a Gaussian distribution with a mean of 0 and a standard deviation of 1
///
/// The Random object to use instead of the internal one (e.g. if you need to control the seed)
/// A random number with a Gaussian distribution with a mean of 0 and a standard deviation of 1
public static double NextGaussian(Random rng)
{
if (_HaveNextNextGaussian)
{
_HaveNextNextGaussian = false;
return _NextNextGaussian;
}
if (_Random == null && rng == null)
_Random = new Random();
double v1, v2, s;
do
{
if (rng == null)
{
v1 = 2.0 * _Random.NextDouble() - 1; // between -1.0 and 1.0
v2 = 2.0 * _Random.NextDouble() - 1; // between -1.0 and 1.0
}
else
{
lock (_RNGLock)
{
v1 = 2.0 * rng.NextDouble() - 1; // between -1.0 and 1.0
v2 = 2.0 * rng.NextDouble() - 1; // between -1.0 and 1.0
}
}
s = v1 * v1 + v2 * v2;
} while (s >= 1 || Math.Abs(s) < 0.001);
var multiplier = Math.Sqrt(-2 * Math.Log(s) / s);
_NextNextGaussian = v2 * multiplier;
_HaveNextNextGaussian = true;
return v1 * multiplier;
}
}
}