I took the basic structure from this link and wrote the following class.
Can you review the code?
Can you find a silent bug?
using System;
namespace Lib
{
/// <summary>
/// A PCG-based random number generator that is interchangeable with System.Random.
/// </summary>
public sealed class Pcg32Rng : Random
{
public ulong Seed { get; private set; }
public ulong Increment { get; }
/// <summary>
/// Initializes a new instance of the <see cref="Pcg32Rng"/> class with a default seed.
/// </summary>
public Pcg32Rng() : this((ulong)Environment.TickCount) { }
/// <summary>
/// Initializes a new instance of the <see cref="Pcg32Rng"/> class with a specified seed and stream ID.
/// </summary>
/// <param name="seed">State initializer (a.k.a. seed).</param>
/// <param name="streamID">Sequence selection constant (a.k.a. stream ID). Defaults to 0.</param>
public Pcg32Rng(ulong seed, ulong streamID = 0)
{
Seed = 0ul;
Increment = (streamID << 1) | 1ul;
PCG32();
Seed += seed;
PCG32();
}
public Pcg32Rng(ulong seed, ulong increment, bool dummy)
{
Seed = seed;
Increment = increment;
}
/// <summary>
/// Generates a uniformly distributed 32-bit unsigned integer.
/// </summary>
/// <returns>A uniformly distributed 32-bit unsigned integer.</returns>
private uint PCG32()
{
ulong oldState = Seed;
Seed = unchecked(Seed * 6364136223846793005ul + Increment);
uint xorshifted = (uint)(((oldState >> 18) ^ oldState) >> 27);
int rot = (int)(oldState >> 59);
return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
}
/// <summary>
/// Generates a uniformly distributed 32-bit unsigned integer less than the specified bound.
/// </summary>
/// <param name="bound">Exclusive upper bound.</param>
/// <returns>A uniformly distributed 32-bit unsigned integer strictly less than <paramref name="bound"/>.</returns>
private uint PCG32(uint bound)
{
uint threshold = ((uint)-bound) % bound;
while (true)
{
uint r = PCG32();
if (r >= threshold)
return r % bound;
}
}
/// <inheritdoc />
public override int Next()
{
return Next(int.MaxValue);
}
/// <inheritdoc />
public override int Next(int maxValue)
{
if (maxValue <= 0)
throw new ArgumentOutOfRangeException(nameof(maxValue), "maxValue must be greater than 0.");
return (int)PCG32((uint)maxValue);
}
/// <inheritdoc />
public override int Next(int minValue, int maxValue)
{
if (minValue > maxValue)
throw new ArgumentOutOfRangeException(nameof(minValue), "minValue must be less than or equal to maxValue.");
long range = (long)maxValue - minValue;
return (int)(minValue + PCG32((uint)range));
}
/// <inheritdoc />
protected override double Sample()
{
// Generate a random double in the range [0, 1).
return PCG32() * (1.0 / uint.MaxValue);
}
/// <inheritdoc />
public override double NextDouble()
{
return Sample();
}
/// <inheritdoc />
public override void NextBytes(byte[] buffer)
{
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
for (int i = 0; i < buffer.Length; i++)
{
buffer[i] = (byte)PCG32();
}
}
}
}