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