// MIT License - Copyright (c) 2024 wallstop
// Full license text: https://github.com/wallstop/unity-helpers/blob/main/LICENSE
namespace WallstopStudios.UnityHelpers.Core.Random
{
using System;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using ProtoBuf;
///
/// A fast 64-bit SplitMix generator often used as a high-quality seeding/mixing PRNG.
///
///
///
/// SplitMix64 is widely used to quickly generate well-distributed 64-bit values and as a seed source for
/// other generators. In this implementation, 32-bit outputs are produced from the mixed 64-bit state.
///
/// Pros:
///
/// - Very fast; great as a hash/mixer and for seed generation.
/// - Deterministic, portable, and simple.
///
/// Cons:
///
/// - Not cryptographically secure.
/// - If you require 64-bit outputs, prefer consuming the full 64-bit mixed value.
///
/// When to use:
///
/// - Producing seeds for other PRNGs; quick hash-like mixing; gameplay randomness.
///
/// When not to use:
///
/// - Security-sensitive scenarios.
///
///
///
///
/// using WallstopStudios.UnityHelpers.Core.Random;
///
/// var seedSource = new SplitMix64(123UL);
/// // Use to seed another RNG
/// var seeded = new XoroShiroRandom(seedSource.NextUlong(), seedSource.NextUlong());
/// int v = seeded.Next(0, 10);
///
///
[RandomGeneratorMetadata(
RandomQuality.VeryGood,
"Well-known SplitMix64 mixer; passes TestU01 BigCrush and PractRand up to large data sizes in literature.",
"Vigna 2014",
"https://prng.di.unimi.it/splitmix64.c"
)]
[Serializable]
[DataContract]
[ProtoContract]
public sealed class SplitMix64
: AbstractRandom,
IEquatable,
IComparable,
IComparable
{
public static SplitMix64 Instance => ThreadLocalRandom.Instance;
public override RandomState InternalState => BuildState(_state);
[ProtoMember(6)]
internal ulong _state;
public SplitMix64()
: this(Guid.NewGuid()) { }
public SplitMix64(Guid guid)
: this(RandomUtilities.GuidToUInt64Pair(guid).First) { }
public SplitMix64(ulong seed)
{
_state = seed;
}
[JsonConstructor]
public SplitMix64(RandomState internalState)
{
_state = internalState.State1;
RestoreCommonState(internalState);
}
public override uint NextUint()
{
unchecked
{
_state += 0x9E3779B97F4A7C15UL;
ulong z = _state;
z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9UL;
z = (z ^ (z >> 27)) * 0x94D049BB133111EBUL;
z ^= z >> 31;
return (uint)z;
}
}
public override IRandom Copy()
{
return new SplitMix64(InternalState);
}
public bool Equals(SplitMix64 other)
{
if (other == null)
{
return false;
}
return _state == other._state;
}
public int CompareTo(object obj)
{
return CompareTo(obj as SplitMix64);
}
public int CompareTo(SplitMix64 other)
{
if (other == null)
{
return -1;
}
return _state.CompareTo(other._state);
}
public override int GetHashCode()
{
return _state.GetHashCode();
}
public override string ToString()
{
return $"{{\"State\": {_state}}}";
}
}
}