< Summary

Information
Class: FixedMathSharp.Utility.DeterministicRandom
Assembly: FixedMathSharp
File(s): /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Utility/DeterministicRandom.cs
Line coverage
97%
Covered lines: 93
Uncovered lines: 2
Coverable lines: 95
Total lines: 211
Line coverage: 97.8%
Branch coverage
85%
Covered branches: 17
Total branches: 20
Branch coverage: 85%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)50%4483.33%
FromWorldFeature(...)100%11100%
NextU64()100%11100%
Next()100%11100%
Next(...)100%22100%
Next(...)100%22100%
NextDouble()100%11100%
NextBytes(...)100%66100%
NextFixed6401()100%11100%
NextFixed64(...)100%22100%
NextFixed64(...)100%22100%
NextBounded(...)50%2288.88%
RotL(...)100%11100%
SplitMix64(...)100%11100%
Mix64(...)100%11100%

File(s)

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Utility/DeterministicRandom.cs

#LineLine coverage
 1using System;
 2using System.Runtime.CompilerServices;
 3
 4namespace FixedMathSharp.Utility
 5{
 6    /// <summary>
 7    /// Fast, seedable, deterministic RNG suitable for lockstep sims and map gen.
 8    /// Uses xoroshiro128++ with splitmix64 seeding. No allocations, no time/GUID.
 9    /// </summary>
 10    public struct DeterministicRandom
 11    {
 12        // xoroshiro128++ state
 13        private ulong _s0;
 14        private ulong _s1;
 15
 16        #region Construction / Seeding
 17
 18        public DeterministicRandom(ulong seed)
 2619        {
 20            // Expand a single seed into two 64-bit state words via splitmix64.
 2621            _s0 = SplitMix64(ref seed);
 2622            _s1 = SplitMix64(ref seed);
 23
 24            // xoroshiro requires non-zero state; repair pathological seed.
 2625            if (_s0 == 0UL && _s1 == 0UL)
 026                _s1 = 0x9E3779B97F4A7C15UL;
 2627        }
 28
 29        /// <summary>
 30        /// Create a stream deterministically
 31        /// Derived from (worldSeed, featureKey[,index]).
 32        /// </summary>
 33        public static DeterministicRandom FromWorldFeature(ulong worldSeed, ulong featureKey, ulong index = 0)
 434        {
 35            // Simple reversible mix (swap for a stronger mix if required).
 436            ulong seed = Mix64(worldSeed, featureKey);
 437            seed = Mix64(seed, index);
 438            return new DeterministicRandom(seed);
 439        }
 40
 41        #endregion
 42
 43        #region Core PRNG
 44
 45        /// <summary>
 46        /// xoroshiro128++ next 64 bits.
 47        /// </summary>
 48        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 49        public ulong NextU64()
 5188350        {
 10376651            ulong s0 = _s0, s1 = _s1;
 5188352            ulong result = RotL(s0 + s1, 17) + s0;
 53
 5188354            s1 ^= s0;
 5188355            _s0 = RotL(s0, 49) ^ s1 ^ (s1 << 21); // a,b
 5188356            _s1 = RotL(s1, 28);                   // c
 57
 5188358            return result;
 5188359        }
 60
 61        /// <summary>
 62        /// Next non-negative Int32 in [0, int.MaxValue].
 63        /// </summary>
 64        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 65        public int Next()
 100466        {
 67            // Take high bits for better quality; mask to 31 bits non-negative.
 100468            return (int)(NextU64() >> 33);
 100469        }
 70
 71        /// <summary>
 72        /// Unbiased int in [0, maxExclusive).
 73        /// </summary>
 74        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 75        public int Next(int maxExclusive)
 3021476        {
 3021477            return maxExclusive <= 0
 3021478                ? throw new ArgumentOutOfRangeException(nameof(maxExclusive))
 3021479                : (int)NextBounded((uint)maxExclusive);
 3021280        }
 81
 82        /// <summary>
 83        /// Unbiased int in [min, maxExclusive).
 84        /// </summary>
 85        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 86        public int Next(int minInclusive, int maxExclusive)
 410187        {
 410188            if (minInclusive >= maxExclusive)
 389                throw new ArgumentException("min >= max");
 409890            uint range = (uint)(maxExclusive - minInclusive);
 409891            return minInclusive + (int)NextBounded(range);
 409892        }
 93
 94        /// <summary>
 95        /// Double in [0,1).
 96        /// </summary>
 97        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 98        public double NextDouble()
 410099        {
 100            // 53 random bits -> [0,1)
 4100101            return (NextU64() >> 11) * (1.0 / (1UL << 53));
 4100102        }
 103
 104        /// <summary>
 105        /// Fill span with random bytes.
 106        /// </summary>
 107        public void NextBytes(Span<byte> buffer)
 13108        {
 13109            int i = 0;
 34110            while (i + 8 <= buffer.Length)
 21111            {
 21112                ulong v = NextU64();
 21113                Unsafe.WriteUnaligned(ref buffer[i], v);
 21114                i += 8;
 21115            }
 13116            if (i < buffer.Length)
 10117            {
 10118                ulong v = NextU64();
 47119                while (i < buffer.Length)
 37120                {
 37121                    buffer[i++] = (byte)v;
 37122                    v >>= 8;
 37123                }
 10124            }
 13125        }
 126
 127        #endregion
 128
 129        #region Fixed64 helpers
 130
 131        /// <summary>
 132        /// Random Fixed64 in [0,1).
 133        /// </summary>
 134        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 135        public Fixed64 NextFixed6401()
 4096136        {
 137            // Produce a raw value in [0, One.m_rawValue)
 4096138            ulong rawOne = (ulong)Fixed64.One.m_rawValue;
 4096139            ulong r = NextBounded(rawOne);
 4096140            return Fixed64.FromRaw((long)r);
 4096141        }
 142
 143        /// <summary>
 144        /// Random Fixed64 in [0, maxExclusive).
 145        /// </summary>
 146        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 147        public Fixed64 NextFixed64(Fixed64 maxExclusive)
 4098148        {
 4098149            if (maxExclusive <= Fixed64.Zero)
 2150                throw new ArgumentOutOfRangeException(nameof(maxExclusive), "max must be > 0");
 4096151            ulong rawMax = (ulong)maxExclusive.m_rawValue;
 4096152            ulong r = NextBounded(rawMax);
 4096153            return Fixed64.FromRaw((long)r);
 4096154        }
 155
 156        /// <summary>
 157        /// Random Fixed64 in [minInclusive, maxExclusive).
 158        /// </summary>
 159        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 160        public Fixed64 NextFixed64(Fixed64 minInclusive, Fixed64 maxExclusive)
 4100161        {
 4100162            if (minInclusive >= maxExclusive)
 2163                throw new ArgumentException("min >= max");
 4098164            ulong span = (ulong)(maxExclusive.m_rawValue - minInclusive.m_rawValue);
 4098165            ulong r = NextBounded(span);
 4098166            return Fixed64.FromRaw((long)r + minInclusive.m_rawValue);
 4098167        }
 168
 169        #endregion
 170
 171        #region Internals: unbiased range, splitmix64, mixing, rotations
 172
 173        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 174        private ulong NextBounded(ulong bound)
 46600175        {
 176            // Rejection to avoid modulo bias.
 177            // threshold = 2^64 % bound, but expressed as (-bound) % bound
 46600178            ulong threshold = unchecked((ulong)-(long)bound) % bound;
 46600179            while (true)
 46600180            {
 46600181                ulong r = NextU64();
 46600182                if (r >= threshold)
 46600183                    return r % bound;
 0184            }
 46600185        }
 186
 187        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 155649188        private static ulong RotL(ulong x, int k) => (x << k) | (x >> (64 - k));
 189
 190        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 191        private static ulong SplitMix64(ref ulong state)
 52192        {
 52193            ulong z = (state += 0x9E3779B97F4A7C15UL);
 52194            z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9UL;
 52195            z = (z ^ (z >> 27)) * 0x94D049BB133111EBUL;
 52196            return z ^ (z >> 31);
 52197        }
 198
 199        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 200        private static ulong Mix64(ulong a, ulong b)
 8201        {
 202            // Simple reversible mix (variant of splitmix finalizer).
 8203            ulong x = a ^ (b + 0x9E3779B97F4A7C15UL);
 8204            x = (x ^ (x >> 30)) * 0xBF58476D1CE4E5B9UL;
 8205            x = (x ^ (x >> 27)) * 0x94D049BB133111EBUL;
 8206            return x ^ (x >> 31);
 8207        }
 208
 209        #endregion
 210    }
 211}