using System; using System.Runtime.CompilerServices; namespace MoonTools.ECS; /// /// This class implements the well equidistributed long-period linear pseudorandom number generator. /// Code taken from Chris Lomont: http://lomont.org/papers/2008/Lomont_PRNG_2008.pdf /// public class Random { public const int STATE_BYTE_COUNT = 68; // 16 state ints + 1 index int uint[] State = new uint[16]; uint Index = 0; uint Seed; /// /// Initializes the RNG with an arbitrary seed. /// public Random() { Init((uint) Environment.TickCount); } /// /// Initializes the RNG with a given seed. /// public void Init(uint seed) { Seed = seed; uint s = seed; for (int i = 0; i < 16; i++) { s = (((s * 214013 + 2531011) >> 16) & 0x7fffffff) | 0; State[i] = ~ ~s; //i ; } Index = 0; } /// /// Returns the seed that was used to initialize the RNG. /// public uint GetSeed() { return Seed; } /// /// Returns the entire state of the RNG as a string. /// public string PrintState() { var s = ""; for (var i = 0; i < 16; i += 1) { s += State[i]; } s += Index; return s; } /// /// Saves the entire state of the RNG to a Span. /// /// Must be a span of at least STATE_BYTE_COUNT bytes. /// Thrown if the byte span is too short. public unsafe void SaveState(Span bytes) { #if DEBUG if (bytes.Length < STATE_BYTE_COUNT) { throw new ArgumentException("Byte span too short!"); } #endif fixed (byte* ptr = bytes) { var offset = 0; for (var i = 0; i < 16; i += 1) { Unsafe.Write(ptr + offset, State[i]); offset += 4; } Unsafe.Write(ptr + offset, Index); } } /// /// Loads the entire state of the RNG from a Span. /// /// Must be a span of at least STATE_BYTE_COUNT bytes. /// Thrown if the byte span is too short. public unsafe void LoadState(Span bytes) { #if DEBUG if (bytes.Length < STATE_BYTE_COUNT) { throw new ArgumentException("Byte span too short!"); } #endif fixed (byte* ptr = bytes) { var offset = 0; for (var i = 0; i < 16; i += 1) { State[i] = Unsafe.Read(ptr + offset); offset += 4; } Index = Unsafe.Read(ptr + offset); } } private uint NextInternal() { uint a, b, c, d; a = State[Index]; c = State[(Index+13)&15]; b = a^c^(a<<16)^(c<<15); c = State[(Index+9)&15]; c ^= (c>>11); a = State[Index] = b^c; d = (uint) (a ^((a<<5)&0xDA442D24UL)); Index = (Index + 15)&15; a = State[Index]; State[Index] = a^b^d^(a<<2)^(b<<18)^(c<<28); return State[Index]; } /// /// Returns a non-negative signed integer. /// public int Next() { return (int) (NextInternal() >>> 1); // unsigned bitshift right to get rid of signed bit } /// /// Returns a non-negative signed integer less than max. /// public int Next(int max) { return (int) (((double) Next()) * max / int.MaxValue); } /// /// Returns a signed integer greater than or equal to min and less than max. /// public int Next(int min, int max) { var diff = max - min; var next = Next(diff); return min + next; } /// /// Returns a non-negative signed 64 bit integer. /// public long NextInt64() { long next = NextInternal(); next <<= 32; next |= NextInternal(); next >>>= 1; return next; } /// /// Returns a non-negative signed 64 bit integer less than max. /// public long NextInt64(long max) { var next = NextInt64(); return (long) (((double) next) * max / long.MaxValue); } /// /// Returns a non-negative signed 64 bit integer greater than or equal to min and less than max. /// public long NextInt64(long min, long max) { var diff = max - min; var next = NextInt64(diff); return min + next; } /// /// Returns a single-precision floating point value between 0 and 1. /// public float NextSingle() { var n = NextInternal(); return ((float) n) / uint.MaxValue; } /// /// Returns a double-precision floating point value between 0 and 1. /// public double NextDouble() { var n = NextInternal(); return ((double) n) / uint.MaxValue; } }