// MIT License - Copyright (c) 2023 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 classic, extremely fast XorShift PRNG with small state and modest quality. /// /// /// /// XorShift generators are known for their simplicity and speed. This variant operates on a 32-bit state and /// produces 32-bit outputs. It is suitable for lightweight, cosmetic randomness where maximum statistical /// rigor is not required. /// /// Pros: /// /// Very fast; tiny state footprint. /// Deterministic and easy to serialize/restore. /// /// Cons: /// /// Lower statistical quality than newer generators; can fail some modern test batteries. /// Not cryptographically secure. /// /// When to use: /// /// Effects, particles, jitter, or any light randomness in hot loops. /// Short-lived simulations where ultimate quality is not required. /// /// When not to use: /// /// Simulations or systems sensitive to subtle bias. /// Security-sensitive contexts. /// /// /// Threading: Use ThreadLocalRandom<XorShiftRandom>.Instance or to avoid shared-state across threads. /// /// /// /// /// using WallstopStudios.UnityHelpers.Core.Random; /// /// var rng = new XorShiftRandom(state: 42); /// int damage = rng.Next(10, 20); /// Vector3 pos = rng.NextVector3(-5f, 5f); // via RandomExtensions /// /// // Thread-local access for parallel systems /// var fast = XorShiftRandom.Instance; // per-thread instance /// /// [RandomGeneratorMetadata( RandomQuality.Fair, "Classic 32-bit xorshift; known to fail portions of TestU01 and PractRand, acceptable for lightweight effects only.", "Marsaglia 2003", "https://doi.org/10.18637/jss.v008.i14" )] [Serializable] [DataContract] [ProtoContract] public sealed class XorShiftRandom : AbstractRandom { public static XorShiftRandom Instance => ThreadLocalRandom.Instance; public override RandomState InternalState => BuildState(_state); private const uint DefaultState = 2463534242U; [ProtoMember(6)] private uint _state; private static uint NormalizeState(uint state) { return state != 0 ? state : DefaultState; } public XorShiftRandom() : this(Guid.NewGuid()) { } public XorShiftRandom(int state) { _state = NormalizeState(unchecked((uint)state)); } public XorShiftRandom(Guid seed) { _state = NormalizeState(unchecked((uint)seed.GetHashCode())); } [JsonConstructor] public XorShiftRandom(RandomState internalState) { _state = NormalizeState(unchecked((uint)internalState.State1)); RestoreCommonState(internalState); } public override uint NextUint() { _state = NormalizeState(_state); _state ^= _state << 13; _state ^= _state >> 17; _state ^= _state << 5; return _state; } public override IRandom Copy() { return new XorShiftRandom(InternalState); } } }