// 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.CompilerServices; using System.Runtime.Serialization; using System.Text.Json.Serialization; using Extension; using Helper; using ProtoBuf; /// /// A member of the ROMU family (RomuDuo) emphasizing high speed and good statistical quality on modern CPUs. /// /// /// /// RomuDuo maintains two 64-bit state variables and uses rotations and multiplies to evolve the state. It is /// competitive with Xoroshiro-style generators in speed while exhibiting strong distribution for general use. /// /// Pros: /// /// Very fast; excellent for real-time usage. /// Good statistical behavior for non-crypto applications. /// Deterministic and reproducible across platforms. /// /// Cons: /// /// Not cryptographically secure. /// Relatively newer family; choose proven options if organizational policy requires long-term validation. /// /// When to use: /// /// Gameplay RNG, procedural content generation, fast Monte Carlo sampling. /// /// When not to use: /// /// Security/adversarial contexts. /// /// /// /// /// using WallstopStudios.UnityHelpers.Core.Random; /// /// var rng = new RomuDuo(Guid.NewGuid()); /// var point = rng.NextVector3InSphere(5f); // via RandomExtensions /// double normal = rng.NextGaussian(0.0, 1.0); /// /// [RandomGeneratorMetadata( RandomQuality.VeryGood, "ROMU family member (RomuDuo); authors report strong BigCrush results with minor low-bit weaknesses in some rotations.", "Markus & Crow 2019", "" // romu-random.org SSL certificate expired; see archived versions )] [Serializable] [DataContract] [ProtoContract] public sealed class RomuDuo : AbstractRandom, IEquatable, IComparable, IComparable { public static RomuDuo Instance => ThreadLocalRandom.Instance; public override RandomState InternalState => BuildState(_x, _y); [ProtoMember(6)] internal ulong _x; [ProtoMember(7)] internal ulong _y; private void EnsureNonZeroState() { if ((_x | _y) == 0) { _x = 0xD3833E804F4C574BUL; _y = 0x94D049BB133111EBUL; } } public RomuDuo() : this(Guid.NewGuid()) { } public RomuDuo(Guid guid) { (ulong a, ulong b) = RandomUtilities.GuidToUInt64Pair(guid); _x = a; _y = b; EnsureNonZeroState(); } public RomuDuo(ulong seedX, ulong seedY) { _x = seedX; _y = seedY; EnsureNonZeroState(); } [JsonConstructor] public RomuDuo(RandomState internalState) { _x = internalState.State1; _y = internalState.State2; RestoreCommonState(internalState); EnsureNonZeroState(); } public override uint NextUint() { unchecked { EnsureNonZeroState(); ulong xp = _x; _x = 15241094284759029579UL * _y; _y = Rol64(_y, 27) + xp; return (uint)xp; } } public override IRandom Copy() { return new RomuDuo(InternalState); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong Rol64(ulong x, int k) { return (x << k) | (x >> (64 - k)); } public override bool Equals(object obj) { return Equals(obj as RomuDuo); } public bool Equals(RomuDuo other) { if (other == null) { return false; } return _x == other._x && _y == other._y; } public override int GetHashCode() { return Objects.HashCode(_x, _y); } public override string ToString() { return this.ToJson(); } public int CompareTo(object obj) { return CompareTo(obj as RomuDuo); } public int CompareTo(RomuDuo other) { if (other == null) { return -1; } int comparison = _x.CompareTo(other._x); if (comparison != 0) { return comparison; } return _y.CompareTo(other._y); } } }