namespace StellaOps.TestKit.Deterministic; /// /// Provides deterministic random number generation for testing. /// Uses a fixed seed to ensure reproducible random sequences. /// /// /// Usage: /// /// var random = new DeterministicRandom(seed: 42); /// var value1 = random.Next(); // Same value every time with seed 42 /// var value2 = random.NextDouble(); // Deterministic sequence /// /// // For property-based testing with FsCheck /// var gen = DeterministicRandom.CreateGen(seed: 42); /// /// public sealed class DeterministicRandom { private readonly System.Random _random; private readonly int _seed; /// /// Creates a new deterministic random number generator with the specified seed. /// /// The seed value. Same seed always produces same sequence. public DeterministicRandom(int seed) { _seed = seed; _random = new System.Random(seed); } /// /// Gets the seed used for this random number generator. /// public int Seed => _seed; /// /// Returns a non-negative random integer. /// public int Next() => _random.Next(); /// /// Returns a non-negative random integer less than the specified maximum. /// public int Next(int maxValue) => _random.Next(maxValue); /// /// Returns a random integer within the specified range. /// public int Next(int minValue, int maxValue) => _random.Next(minValue, maxValue); /// /// Returns a random floating-point number between 0.0 and 1.0. /// public double NextDouble() => _random.NextDouble(); /// /// Fills the elements of the specified array with random bytes. /// public void NextBytes(byte[] buffer) => _random.NextBytes(buffer); /// /// Fills the elements of the specified span with random bytes. /// public void NextBytes(Span buffer) => _random.NextBytes(buffer); /// /// Creates a new deterministic Random instance with the specified seed. /// Useful for integration with code that expects System.Random. /// public static System.Random CreateRandom(int seed) => new(seed); /// /// Generates a deterministic GUID based on the seed. /// public Guid NextGuid() { var bytes = new byte[16]; _random.NextBytes(bytes); return new Guid(bytes); } /// /// Generates a deterministic string of the specified length using alphanumeric characters. /// public string NextString(int length) { const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; var result = new char[length]; for (int i = 0; i < length; i++) { result[i] = chars[_random.Next(chars.Length)]; } return new string(result); } /// /// Selects a random element from the specified array. /// public T NextElement(T[] array) { if (array == null || array.Length == 0) { throw new ArgumentException("Array cannot be null or empty", nameof(array)); } return array[_random.Next(array.Length)]; } /// /// Shuffles an array in-place using the Fisher-Yates algorithm (deterministic). /// public void Shuffle(T[] array) { if (array == null) { throw new ArgumentNullException(nameof(array)); } for (int i = array.Length - 1; i > 0; i--) { int j = _random.Next(i + 1); (array[i], array[j]) = (array[j], array[i]); } } }