// MIT License - Copyright (c) 2025 wallstop // Full license text: https://github.com/wallstop/unity-helpers/blob/main/LICENSE /* IllusionFlow is a significant enhancement upon the classic XoroShiroRandom discovered by Will Stafford Parsons. Reference: https://github.com/wstaffordp/illusionflow Copyright original author: https://github.com/wstaffordp */ namespace WallstopStudios.UnityHelpers.Core.Random { using System; using System.Buffers.Binary; using System.Collections.Generic; using System.Runtime.Serialization; using System.Text.Json.Serialization; using ProtoBuf; /// /// IllusionFlow: a modern, high-performance PRNG building on Xoroshiro concepts with additional state and mixing. /// /// /// /// IllusionFlow enhances the classic Xoroshiro approach with additional state and update rules for improved /// distribution characteristics. In this package, defaults to /// to provide fast, high-quality randomness out of the box. /// /// Pros: /// /// Excellent performance; strong general-purpose statistical behavior. /// Deterministic and portable via . /// /// Cons: /// /// Not cryptographically secure. /// Newer algorithm—choose established ones if you require historical precedence. /// /// When to use: /// /// Default choice for most gameplay and procedural content needs. /// /// When not to use: /// /// Cryptographic or adversarial contexts. /// /// /// Threading: Prefer or to avoid sharing state. /// /// /// /// /// using WallstopStudios.UnityHelpers.Core.Random; /// /// IRandom rng = PRNG.Instance; // IllusionFlow by default /// int index = rng.Next(0, items.Count); /// float gaussian = rng.NextGaussian(mean: 0f, stdDev: 1f); /// /// // Deterministic snapshot /// var state = rng.InternalState; /// var replay = new IllusionFlow(state); /// /// [RandomGeneratorMetadata( RandomQuality.Excellent, "Hybridized PCG + xorshift design; upstream PractRand 64GB passes with no anomalies per author.", "Will Stafford Parsons", "" // Original repository wileylooper/illusionflow is offline )] [Serializable] [DataContract] [ProtoContract] public sealed class IllusionFlow : AbstractRandom { private const int UintByteCount = sizeof(uint) * 8; private const int StatePayloadLength = sizeof(uint); public static IllusionFlow Instance => ThreadLocalRandom.Instance; public override RandomState InternalState { get { ulong stateA = ((ulong)_a << UintByteCount) | _b; ulong stateB = ((ulong)_c << UintByteCount) | _d; BinaryPrimitives.WriteUInt32LittleEndian(_payload, _e); return BuildState(stateA, stateB, payload: _payload); } } [ProtoMember(6)] private uint _a; [ProtoMember(7)] private uint _b; [ProtoMember(8)] private uint _c; [ProtoMember(9)] private uint _d; [ProtoMember(10)] private uint _e; // Cached space for RandomState private readonly byte[] _payload = new byte[StatePayloadLength]; public IllusionFlow() : this(Guid.NewGuid()) { } public IllusionFlow(Guid guid, uint? extraSeed = null) { (uint a, uint b, uint c, uint d) = RandomUtilities.GuidToUInt32Quad(guid); _a = a; _b = b; _b = b; _c = c; _d = d; _e = extraSeed ?? unchecked((uint)guid.GetHashCode()); } [JsonConstructor] public IllusionFlow(RandomState internalState) { unchecked { _a = (uint)(internalState.State1 >> UintByteCount); _b = (uint)internalState.State1; _c = (uint)(internalState.State2 >> UintByteCount); _d = (uint)internalState.State2; uint legacyE = 0; bool hasPayload = TryReadStatePayload(internalState, out uint payloadE); bool legacyPackedE = !hasPayload && TryExtractLegacyPackedValue(internalState, out legacyE); if (hasPayload) { _e = payloadE; } else if (legacyPackedE) { _e = legacyE; } else { _e = DeriveFallbackE(internalState.State1, internalState.State2); } RandomState stateToRestore = legacyPackedE ? StripLegacyGaussian(internalState) : internalState; RestoreCommonState(stateToRestore); } } public override uint NextUint() { unchecked { uint result = _b + _e; ++_a; if (_a == 0U) { _c += _e; _d ^= _b; _b += _c; _e ^= _d; return result; } _b = ((_b << 17) | (_b >> 15)) ^ _d; _d += 1111111111U; _e = (result << 13) | (result >> 19); return result; } } public override IRandom Copy() { return new IllusionFlow(InternalState); } private static bool TryReadStatePayload(RandomState state, out uint value) { IReadOnlyList payload = state.PayloadBytes; if (payload is not { Count: >= StatePayloadLength }) { value = 0; return false; } Span buffer = stackalloc byte[StatePayloadLength]; for (int i = 0; i < StatePayloadLength; ++i) { buffer[i] = payload[i]; } value = BinaryPrimitives.ReadUInt32LittleEndian(buffer); return true; } private static bool TryExtractLegacyPackedValue(RandomState state, out uint value) { double? gaussian = state.Gaussian; if (gaussian == null) { value = 0; return false; } long bits = BitConverter.DoubleToInt64Bits(gaussian.Value); if ((bits & unchecked((long)0xFFFFFFFF00000000L)) != 0) { value = 0; return false; } value = (uint)(bits & 0xFFFFFFFFL); return true; } private static RandomState StripLegacyGaussian(RandomState state) { return new RandomState( state.State1, state.State2, gaussian: null, payload: state.PayloadBytes, bitBuffer: state.BitBuffer, bitCount: state.BitCount, byteBuffer: state.ByteBuffer, byteCount: state.ByteCount ); } private static uint DeriveFallbackE(ulong state1, ulong state2) { uint candidate = unchecked((uint)(state1 ^ state2 ^ 0xA5A5A5A5UL)); return candidate != 0 ? candidate : 0x1F123BB5U; } } }