namespace UnityHelpers.Core.Random
{
using System;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
///
/// Implementation based off of the reference PCG Random, found here: https://www.pcg-random.org/index.html
///
[Serializable]
[DataContract]
public sealed class PcgRandom
: AbstractRandom,
IEquatable,
IComparable,
IComparable
{
public static PcgRandom Instance => ThreadLocalRandom.Instance;
public override RandomState InternalState => new(_state, _increment, _cachedGaussian);
internal readonly ulong _increment;
internal ulong _state;
public PcgRandom()
: this(Guid.NewGuid()) { }
public PcgRandom(Guid guid)
{
byte[] guidArray = guid.ToByteArray();
_state = BitConverter.ToUInt64(guidArray, 0);
_increment = BitConverter.ToUInt64(guidArray, sizeof(ulong));
}
[JsonConstructor]
public PcgRandom(RandomState internalState)
{
_state = internalState.State1;
_increment = internalState.State2;
_cachedGaussian = internalState.Gaussian;
}
public PcgRandom(ulong increment, ulong state)
{
_increment = increment;
_state = state;
}
public PcgRandom(long seed)
{
// Start with a nice prime
_increment = 6554638469UL;
_state = unchecked((ulong)seed);
_increment = NextUlong();
}
public override uint NextUint()
{
unchecked
{
ulong oldState = _state;
_state = oldState * 6364136223846793005UL + _increment;
uint xorShifted = (uint)(((oldState >> 18) ^ oldState) >> 27);
int rot = (int)(oldState >> 59);
return (xorShifted >> rot) | (xorShifted << (-rot & 31));
}
}
public bool Equals(PcgRandom other)
{
if (ReferenceEquals(other, null))
{
return false;
}
// ReSharper disable once CompareOfFloatsByEqualityOperator
return _increment == other._increment
&& _state == other._state
&& _cachedGaussian == other._cachedGaussian;
}
public override bool Equals(object obj)
{
return Equals(obj as PcgRandom);
}
public int CompareTo(PcgRandom other)
{
if (ReferenceEquals(other, null))
{
return -1;
}
if (_increment == other._increment)
{
if (_state == other._state)
{
return 0;
}
if (_state < other._state)
{
return -1;
}
return 1;
}
if (_increment < other._increment)
{
return -1;
}
if (_cachedGaussian.HasValue != other._cachedGaussian.HasValue)
{
return _cachedGaussian.HasValue ? -1 : 1;
}
if (!_cachedGaussian.HasValue)
{
return 0;
}
// ReSharper disable once PossibleInvalidOperationException
return _cachedGaussian.Value.CompareTo(other._cachedGaussian.Value);
}
public int CompareTo(object obj)
{
return CompareTo(obj as PcgRandom);
}
public override int GetHashCode()
{
return _increment.GetHashCode();
}
public override string ToString()
{
return $"{{\"Increment\": {_increment}, \"State\": {_state}}}";
}
public override IRandom Copy()
{
return new PcgRandom(InternalState);
}
}
}