// 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}}}"; } } }