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