< Summary

Information
Class: AmbientServices.Pseudorandom
Assembly: AmbientServices
File(s): /home/runner/work/AmbientServices/AmbientServices/AmbientServices/Types/Pseudorandom.cs
Tag: 332_35464845198
Line coverage
100%
Covered lines: 159
Uncovered lines: 0
Coverable lines: 159
Total lines: 593
Line coverage: 100%
Branch coverage
99%
Covered branches: 115
Total branches: 116
Branch coverage: 99.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
get_NextGlobalSeed()100%11100%
get_Next()100%11100%
.ctor()100%11100%
.ctor(...)100%11100%
.ctor(...)100%11100%
Clone()100%11100%
.ctor(...)100%22100%
get_NextUInt64()100%11100%
get_NextInt64Signed()100%11100%
get_NextInt64()100%11100%
NextInt64Ranged(...)100%11100%
NextInt64Ranged(...)100%44100%
get_NextUInt32()100%11100%
get_NextInt32Signed()100%11100%
get_NextInt32()100%11100%
NextInt32Ranged(...)100%11100%
NextInt32Ranged(...)100%44100%
NextUInt32Ranged(...)100%44100%
NextInt32SignedRanged(...)100%44100%
get_NextUInt32UsuallySmall()100%11100%
get_NextInt32UsuallySmall()100%11100%
get_NextInt32SignedUsuallySmall()100%11100%
NextUInt32RangedUsuallySmall(...)100%22100%
NextInt32SignedRangedUsuallySmall(...)100%88100%
NextInt32RangedUsuallySmall(...)100%11100%
NextUInt64Ranged(...)100%44100%
NextInt64SignedRanged(...)100%44100%
get_NextUInt64UsuallySmall()100%11100%
get_NextInt64UsuallySmall()100%11100%
get_NextInt64SignedUsuallySmall()100%11100%
NextUInt64RangedUsuallySmall(...)100%22100%
NextInt64SignedRangedUsuallySmall(...)100%88100%
NextInt64RangedUsuallySmall(...)100%11100%
get_NextSignMultiplier()100%11100%
get_NextBoolean()100%11100%
NextNewBytes(...)100%11100%
NextBytes(...)100%2020100%
NextBytes(...)100%2020100%
get_NextDecimal()100%11100%
get_NextDouble()100%11100%
get_NextGuid()100%11100%
NextEnum(...)92.86%1414100%
NextEnum<TEnum>()100%11100%
GetHashCode()100%11100%
Equals(...)100%22100%
Equals(...)100%22100%
op_Equality(...)100%66100%
op_Inequality(...)100%66100%

File(s)

/home/runner/work/AmbientServices/AmbientServices/AmbientServices/Types/Pseudorandom.cs

#LineLine coverage
 1using System;
 2using System.Reflection;
 3
 4namespace AmbientServices;
 5
 6/// <summary>
 7/// A class containing a random seed from which pseudorandom data of various types can be generated.
 8/// </summary>
 9/// <remarks>
 10/// <pitch>Reproducible pseudorandom data of many shapes (integers of every width, ranged values, skewed usually-small v
 11/// <pledge>
 12/// Determinism is the contract: two instances with the same seed making the same sequence of calls produce identical va
 13/// Every value produced advances the seed; ranged forms return values in [inclusive lower, exclusive upper) and throw w
 14/// Instances are mutable and not thread-safe; share streams across threads by giving each thread its own instance or se
 15/// </pledge>
 16/// <plan>
 17/// The generator multiplies the incrementing seed by a large prime and fully reverses the bit order (log-n swap cascade
 18/// Seed generation for <see cref="Next"/> mixes <see cref="Environment.TickCount"/>, an <see cref="AmbientStopwatch"/>'
 19/// </plan>
 20/// <priority>
 21/// 1. Reproducibility over unpredictability: the entire state is one seed, a clone continues its source's stream exactl
 22/// 2. Even distribution under small divisors over generator speed: values are produced by multiplying the seed by a lar
 23/// 3. Cheap instances over thread safety: state is a single 64-bit seed with no synchronization, so instances are trivi
 24/// </priority>
 25/// Data generated using <see cref="Pseudorandom"/> should be more random than that generated by the system's <see cref=
 26/// Additionally, instances constructed using <see cref="Pseudorandom.Pseudorandom(bool)"/> with true as a parameter or 
 27/// The random data generated from any particular seed is consistent, ie. given the same seed and sequence of property a
 28/// This class should *not* ever be used for generating encryption keys of any type for production use.
 29/// </remarks>
 30public class Pseudorandom : IEquatable<Pseudorandom>
 31{
 232    private static readonly long _startTickCount = Environment.TickCount;
 233    private static readonly AmbientStopwatch _stopwatch = AmbientStopwatch.StartNew();  // use this stopwatch to get see
 34    private static long _rotator;      // interlocked
 35
 36    /// <summary>
 37    /// Gets a thread-safe non-repeating seed to initialize a <see cref="Pseudorandom"/>, attempting to avoid using the 
 38    /// Uses timing information including the high frequency performance counter from <see cref="System.Diagnostics.Stop
 39    /// </summary>
 240    private static ulong NextGlobalSeed => (ulong)((_startTickCount + _stopwatch.ElapsedTicks) ^ System.Threading.Interl
 41    /// <summary>
 42    /// Gets a new <see cref="Pseudorandom"/> using a thread-safe seed generator.
 43    /// Getting even a single random number through this property is very efficient.
 44    /// </summary>
 245    public static Pseudorandom Next => new((int)NextGlobalSeed);
 46
 47    private ulong _seed;
 48
 49    /// <summary>
 50    /// Constructs a pseudorandom with a zero seed.
 51    /// </summary>
 252    public Pseudorandom()
 53    {
 254        _seed = 0;
 255    }
 56    /// <summary>
 57    /// Constructs a pseudorandom with the specified seed value.
 58    /// </summary>
 59    /// <param name="seed">The seed to use.</param>
 260    public Pseudorandom(long seed)
 61    {
 262        _seed = (ulong)seed;
 263    }
 64    /// <summary>
 65    /// Constructs a pseudorandom with the specified seed value.
 66    /// </summary>
 67    /// <param name="seed">The seed to use.</param>
 68    [CLSCompliant(false)]
 269    public Pseudorandom(ulong seed)
 70    {
 271        _seed = seed;
 272    }
 73    /// <summary>
 74    /// Clones this <see cref="Pseudorandom"/> such that identical calls on the clone will return the same pseudorandom 
 75    /// </summary>
 76    /// <returns>A new <see cref="Pseudorandom"/> in the same state as this one.</returns>
 77    public Pseudorandom Clone()
 78    {
 279        return new Pseudorandom(_seed);
 80    }
 81    /// <summary>
 82    /// Constructs a pseudorandom with a seed generated from the system time combined with a global rotating number.
 83    /// </summary>
 84    /// <param name="generateSeed">Whether or not to create a seed using the current system time combined with a global 
 285    public Pseudorandom(bool generateSeed)
 86    {
 287        _seed = generateSeed ? (uint)NextGlobalSeed : 0;
 288    }
 89    /// <summary>
 90    /// Gets the next <see cref="ulong"/> based on the current seed.  Values will be roughly evenly distributed across a
 91    /// </summary>
 92    [CLSCompliant(false)]
 93    public ulong NextUInt64
 94    {
 95        get
 96        {
 97            unchecked
 98            {
 299                ulong x = ++_seed * 1_111_111_111_111_111_111UL;        // note that this is a prime number (but not a m
 2100                x = (((x & 0xaaaaaaaaaaaaaaaa) >> 1) | ((x & 0x5555555555555555) << 1));
 2101                x = (((x & 0xcccccccccccccccc) >> 2) | ((x & 0x3333333333333333) << 2));
 2102                x = (((x & 0xf0f0f0f0f0f0f0f0) >> 4) | ((x & 0x0f0f0f0f0f0f0f0f) << 4));
 2103                x = (((x & 0xff00ff00ff00ff00) >> 8) | ((x & 0x00ff00ff00ff00ff) << 8));
 2104                x = (((x & 0xffff0000ffff0000) >> 16)| ((x & 0x0000ffff0000ffff) << 16));
 2105                return ((x >> 32) | (x << 32));
 106            }
 107        }
 108    }
 109    /// <summary>
 110    /// Gets the next <see cref="long"/> based on the current seed.  Note that this may return a negative number.  Value
 111    /// </summary>
 2112    public long NextInt64Signed => (long)NextUInt64 * NextSignMultiplier;
 113    /// <summary>
 114    /// Gets the next positive <see cref="long"/> based on the current seed.  Values will be roughly evenly distributed 
 115    /// </summary>
 2116    public long NextInt64 => (long)(NextUInt64 & 0x7fffffffffffffff);
 117    /// <summary>
 118    /// Gets the next positive <see cref="long"/> based on the current seed.  Values will be roughly evenly distributed 
 119    /// </summary>
 120    /// <param name="maxValue">The possible maximum value (exclusive).</param>
 121    /// <returns>A random value between zero (inclusive) and <paramref name="maxValue"/> (exclusive).</returns>
 122    public long NextInt64Ranged(long maxValue)
 123    {
 2124        return (long)((NextUInt64 & 0x7fffffffffffffffUL) % (ulong)maxValue);
 125    }
 126    /// <summary>
 127    /// Gets a random <see cref="long"/> in the specified range.  Values will be roughly evenly distributed across all v
 128    /// </summary>
 129    /// <param name="lowerLimit">The lower limit for the number, inclusive.</param>
 130    /// <param name="upperLimit">The upper limit for the number, exclusive.</param>
 131    /// <returns>A pseudorandom number between <paramref name="lowerLimit"/> (inclusive) and <paramref name="upperLimit"
 132    public long NextInt64Ranged(long lowerLimit, long upperLimit)
 133    {
 2134        if (upperLimit < lowerLimit) throw new ArgumentException("The upper limit must be higher than the lower limit!",
 2135        ulong difference = (ulong)(upperLimit - lowerLimit);
 2136        return (difference == 0) ? lowerLimit : (lowerLimit + (long)(NextUInt64 % difference));
 137    }
 138    /// <summary>
 139    /// Gets the next <see cref="uint"/> based on the current seed.  Values will be roughly evenly distributed across al
 140    /// </summary>
 141    [CLSCompliant(false)]
 142    public uint NextUInt32
 143    {
 144        get
 145        {
 146            unchecked
 147            {
 2148                uint x = (uint)(++_seed * 777_767_777);     // note that this is a prime number (but not a mersenne prim
 2149                x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
 2150                x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
 2151                x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
 2152                x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
 2153                return ((x >> 16) | (x << 16));
 154            }
 155        }
 156    }
 157    /// <summary>
 158    /// Gets the next <see cref="int"/> based on the current seed.  Note that this may return a negative number.  Values
 159    /// </summary>
 2160    public int NextInt32Signed => (int)NextUInt32 * NextSignMultiplier;
 161    /// <summary>
 162    /// Gets the next positive <see cref="int"/> based on the current seed.  Values will be roughly evenly distributed a
 163    /// </summary>
 2164    public int NextInt32 => (int)(NextUInt32 & 0x7fffffff);
 165    /// <summary>
 166    /// Gets the next positive <see cref="int"/> based on the current seed.  Values will be roughly evenly distributed a
 167    /// </summary>
 168    /// <param name="maxValue">The possible maximum value (exclusive).</param>
 169    /// <returns>The selected value.</returns>
 170    public int NextInt32Ranged(int maxValue)
 171    {
 2172        return (int)((NextUInt32 & 0x7fffffff) % maxValue);
 173    }
 174    /// <summary>
 175    /// Gets a random <see cref="int"/> in the specified range.  Values will be roughly evenly distributed across all va
 176    /// </summary>
 177    /// <param name="lowerLimit">The lower limit for the number, inclusive.</param>
 178    /// <param name="upperLimit">The upper limit for the number, exclusive.</param>
 179    /// <returns>A pseudorandom number between <paramref name="lowerLimit"/> (inclusive) and <paramref name="upperLimit"
 180    public int NextInt32Ranged(int lowerLimit, int upperLimit)
 181    {
 2182        if (upperLimit < lowerLimit) throw new ArgumentException("The upper limit must be higher than the lower limit!",
 2183        uint difference = (uint)(upperLimit - lowerLimit);
 2184        return (difference == 0) ? lowerLimit : (lowerLimit + (int)(NextUInt32 % difference));
 185    }
 186
 187
 188
 189
 190    /// <summary>
 191    /// Gets a random <see cref="uint"/> in the specified range.  Values will be roughly evenly distributed across all v
 192    /// </summary>
 193    /// <param name="lowerLimit">The lower limit for the number, inclusive.</param>
 194    /// <param name="upperLimit">The upper limit for the number, exclusive.</param>
 195    /// <returns>A pseudorandom number between <paramref name="lowerLimit"/> (inclusive) and <paramref name="upperLimit"
 196    [CLSCompliant(false)]
 197    public uint NextUInt32Ranged(uint lowerLimit, uint upperLimit)
 198    {
 2199        if (upperLimit < lowerLimit) throw new ArgumentException("The upper limit must be higher than the lower limit!",
 2200        uint difference = upperLimit - lowerLimit;
 2201        return (difference == 0) ? lowerLimit : (lowerLimit + NextUInt32 % difference);
 202    }
 203    /// <summary>
 204    /// Gets a random signed <see cref="int"/> in the specified range.  Values will be roughly evenly distributed across
 205    /// </summary>
 206    /// <param name="lowerLimit">The lower limit for the number, inclusive.</param>
 207    /// <param name="upperLimit">The upper limit for the number, exclusive.</param>
 208    /// <returns>A pseudorandom number between <paramref name="lowerLimit"/> (inclusive) and <paramref name="upperLimit"
 209    public int NextInt32SignedRanged(int lowerLimit, int upperLimit)
 210    {
 2211        if (upperLimit < lowerLimit) throw new ArgumentException("The upper limit must be higher than the lower limit!",
 2212        uint difference = (uint)(upperLimit - lowerLimit);
 2213        return (difference == 0) ? lowerLimit : (lowerLimit + (int)(NextUInt32 % difference));
 214    }
 215    /// <summary>
 216    /// Gets a random <see cref="uint"/> that is usually small.  All <see cref="uint"/> values are possible, but smaller
 217    /// </summary>
 218    [CLSCompliant(false)]
 2219    public uint NextUInt32UsuallySmall => NextUInt32RangedUsuallySmall(uint.MaxValue);
 220    /// <summary>
 221    /// Gets a random <see cref="int"/> that is usually small.  All non-negative <see cref="int"/> values are possible, 
 222    /// </summary>
 2223    public int NextInt32UsuallySmall => NextInt32RangedUsuallySmall(int.MaxValue);
 224    /// <summary>
 225    /// Gets a random signed <see cref="int"/> that is usually small.  All <see cref="int"/> values are possible, but va
 226    /// </summary>
 2227    public int NextInt32SignedUsuallySmall => NextInt32SignedRangedUsuallySmall(int.MinValue, int.MaxValue, 8);
 228    /// <summary>
 229    /// Gets a random <see cref="uint"/> that is usually small.  All <see cref="uint"/> values up to <paramref name="upp
 230    /// </summary>
 231    /// <param name="upperLimit">The upper limit (exclusive).</param>
 232    /// <param name="iterations">The number of iterations (more iterations means more preference for the small numbers).
 233    /// <returns>A random <see cref="uint"/> that is usually small.</returns>
 234    [CLSCompliant(false)]
 235    public uint NextUInt32RangedUsuallySmall(uint upperLimit, int iterations = 8)
 236    {
 2237        uint baseAdjuster = NextUInt32;
 2238        uint maxDivisor = (1U << iterations);
 2239        uint divisor = (iterations > 31) ? int.MaxValue : (1U + (baseAdjuster % maxDivisor));
 2240        ushort downShift = (ushort)(baseAdjuster % (iterations));
 2241        uint baseNumber = NextUInt32Ranged(0, upperLimit >> downShift);
 2242        return baseNumber / divisor;
 243    }
 244    /// <summary>
 245    /// Gets a random <see cref="int"/> that is usually small.  All <see cref="int"/> values between <paramref name="low
 246    /// </summary>
 247    /// <param name="lowerLimit">The lower limit (inclusive).</param>
 248    /// <param name="upperLimit">The upper limit (exclusive).</param>
 249    /// <param name="iterations">The number of iterations (more iterations means more preference for the small numbers).
 250    /// <returns>A random <see cref="int"/> that is usually nearer to zero.</returns>
 251    public int NextInt32SignedRangedUsuallySmall(int lowerLimit, int upperLimit, int iterations = 8)
 252    {
 2253        if (upperLimit < lowerLimit) throw new ArgumentException("The upper limit must be higher than the lower limit!",
 254        // always positive?
 2255        if (lowerLimit >= 0)
 256        {
 2257            return (int)(lowerLimit + NextUInt32RangedUsuallySmall((uint)(upperLimit - lowerLimit), iterations));
 258        }
 259        // always negative?
 2260        if (upperLimit <= 0)
 261        {
 2262            return (int)(upperLimit - 1 - NextUInt32RangedUsuallySmall((uint)(lowerLimit - upperLimit), iterations));
 263        }
 2264        uint range = (uint)(upperLimit - lowerLimit);
 265        // pick a positive?
 2266        if ((NextUInt32 * NextUInt32) % range < upperLimit)
 267        {
 2268            return (int)NextUInt32RangedUsuallySmall((uint)upperLimit, iterations);
 269        }
 270        else // negative
 271        {
 2272            return -(int)NextUInt32RangedUsuallySmall((uint)(-lowerLimit), iterations) - 1;
 273        }
 274    }
 275    /// <summary>
 276    /// Gets a random <see cref="int"/> that is usually small.  All non-negative <see cref="int"/> values up to <paramre
 277    /// </summary>
 278    /// <param name="upperLimit">The upper limit (exclusive).</param>
 279    /// <param name="iterations">The number of iterations (more iterations means more preference for the small numbers).
 280    /// <returns>A random <see cref="int"/> between zero and <paramref name="upperLimit"/> (exclusive), that is usually 
 281    public int NextInt32RangedUsuallySmall(int upperLimit, int iterations = 8)
 282    {
 2283        return (int)NextUInt32RangedUsuallySmall((uint)upperLimit, iterations);
 284    }
 285    /// <summary>
 286    /// Gets a random <see cref="ulong"/> in the specified range.  Values will be roughly evenly distributed across all 
 287    /// </summary>
 288    /// <param name="lowerLimit">The lower limit for the number, inclusive.</param>
 289    /// <param name="upperLimit">The upper limit for the number, exclusive.</param>
 290    /// <returns>A pseudorandom number between <paramref name="lowerLimit"/> (inclusive) and <paramref name="upperLimit"
 291    [CLSCompliant(false)]
 292    public ulong NextUInt64Ranged(ulong lowerLimit, ulong upperLimit)
 293    {
 2294        if (upperLimit < lowerLimit) throw new ArgumentException("The upper limit must be higher than the lower limit!",
 2295        ulong difference = upperLimit - lowerLimit;
 2296        return (difference == 0) ? lowerLimit : (lowerLimit + NextUInt64 % difference);
 297    }
 298    /// <summary>
 299    /// Gets a random signed <see cref="long"/> in the specified range.  Values will be roughly evenly distributed acros
 300    /// </summary>
 301    /// <param name="lowerLimit">The lower limit for the number, inclusive.</param>
 302    /// <param name="upperLimit">The upper limit for the number, exclusive.</param>
 303    /// <returns>A pseudorandom number between <paramref name="lowerLimit"/> (inclusive) and <paramref name="upperLimit"
 304    public long NextInt64SignedRanged(long lowerLimit, long upperLimit)
 305    {
 2306        if (upperLimit < lowerLimit) throw new ArgumentException("The upper limit must be higher than the lower limit!",
 2307        ulong difference = (ulong)(upperLimit - lowerLimit);
 2308        return (difference == 0) ? lowerLimit : (lowerLimit + (long)(NextUInt64 % difference));
 309    }
 310    /// <summary>
 311    /// Gets a random <see cref="ulong"/> that is usually small.  All <see cref="ulong"/> values are possible, but small
 312    /// </summary>
 313    [CLSCompliant(false)]
 2314    public ulong NextUInt64UsuallySmall => NextUInt64RangedUsuallySmall(ulong.MaxValue);
 315    /// <summary>
 316    /// Gets a random <see cref="long"/> that is usually small.  All non-negative <see cref="long"/> values are possible
 317    /// </summary>
 2318    public long NextInt64UsuallySmall => NextInt64RangedUsuallySmall(long.MaxValue);
 319    /// <summary>
 320    /// Gets a random <see cref="long"/> that is usually small.  All <see cref="long"/> values are possible, but values 
 321    /// </summary>
 2322    public long NextInt64SignedUsuallySmall => NextInt64SignedRangedUsuallySmall(long.MinValue, long.MaxValue);
 323    /// <summary>
 324    /// Gets a random <see cref="ulong"/> that is usually small.  All <see cref="ulong"/> values up to <paramref name="u
 325    /// </summary>
 326    /// <param name="upperLimit">The upper limit (exclusive).</param>
 327    /// <param name="iterations">The number of iterations (more iterations means more preference for the small numbers).
 328    /// <returns>A random <see cref="ulong"/> that is usually small.</returns>
 329    [CLSCompliant(false)]
 330    public ulong NextUInt64RangedUsuallySmall(ulong upperLimit, int iterations = 20)
 331    {
 2332        ulong baseAdjuster = NextUInt64;
 2333        uint maxDivisor = (1U << iterations);
 2334        ulong divisor = (iterations > 63) ? long.MaxValue : (1U + (baseAdjuster % maxDivisor));
 2335        ushort downShift = (ushort)(baseAdjuster % (uint)iterations);
 2336        ulong baseNumber = NextUInt64Ranged(0, upperLimit >> downShift);
 2337        return baseNumber / divisor;
 338    }
 339    /// <summary>
 340    /// Gets a random <see cref="long"/> that is usually small.  All <see cref="int"/> values between <paramref name="lo
 341    /// </summary>
 342    /// <param name="lowerLimit">The lower limit (inclusive).</param>
 343    /// <param name="upperLimit">The upper limit (exclusive).</param>
 344    /// <param name="iterations">The number of iterations (more iterations means more preference for the small numbers).
 345    /// <returns>A random <see cref="long"/> that is usually nearer to zero.</returns>
 346    public long NextInt64SignedRangedUsuallySmall(long lowerLimit, long upperLimit, int iterations = 20)
 347    {
 2348        if (upperLimit < lowerLimit) throw new ArgumentException("The upper limit must be higher than the lower limit!",
 349        // always positive?
 2350        if (lowerLimit >= 0)
 351        {
 2352            return lowerLimit + (long)NextUInt64RangedUsuallySmall((ulong)(upperLimit - lowerLimit), iterations);
 353        }
 354        // always negative?
 2355        if (upperLimit <= 0)
 356        {
 2357            return upperLimit - 1 - (long)NextUInt64RangedUsuallySmall((ulong)(lowerLimit - upperLimit), iterations);
 358        }
 2359        ulong range = (ulong)(upperLimit - lowerLimit);
 360        // pick a positive?
 2361        if ((NextUInt64 * NextUInt64) % range < (ulong)upperLimit)
 362        {
 2363            return (long)NextUInt64RangedUsuallySmall((ulong)upperLimit, iterations);
 364        }
 365        else // negative
 366        {
 2367            return -(long)NextUInt64RangedUsuallySmall((ulong)(-lowerLimit), iterations) - 1;
 368        }
 369    }
 370    /// <summary>
 371    /// Gets a random <see cref="long"/> that is usually small.  All non-negative <see cref="long"/> values up to <param
 372    /// </summary>
 373    /// <param name="upperLimit">The upper limit (exclusive).</param>
 374    /// <param name="iterations">The number of iterations (more iterations means more preference for the small numbers).
 375    /// <returns>A random <see cref="long"/> between zero and <paramref name="upperLimit"/> (exclusive), that is usually
 376    public long NextInt64RangedUsuallySmall(long upperLimit, int iterations = 20)
 377    {
 2378        return (long)NextUInt64RangedUsuallySmall((ulong)upperLimit, iterations);
 379    }
 380
 381
 382    /// <summary>
 383    /// Gets a random sign multiplier.  Values returned should be 1 roughly 50% of the time and -1 roughly 50% of the ti
 384    /// </summary>
 2385    public int NextSignMultiplier => (2 * (int)NextUInt32Ranged(0, 2)) - 1;
 386    /// <summary>
 387    /// Gets a random boolean, either true or false, with a roughly even distribution between those values.
 388    /// </summary>
 2389    public bool NextBoolean => NextUInt32Ranged(0, 2) == 0;
 390    /// <summary>
 391    /// Returns an array of the specified length populated with random values.
 392    /// </summary>
 393    /// <param name="length">The number of random bytes to put in the returned array.</param>
 394    /// <returns>An array of bytes of the specified length populated with random byte values.</returns>
 395    public byte[] NextNewBytes(int length)
 396    {
 2397        byte[] bytes = new byte[length];
 2398        NextBytes(bytes);
 2399        return bytes;
 400    }
 401    /// <summary>
 402    /// Populates an existing byte array with random values.
 403    /// </summary>
 404    /// <param name="target">The target array of bytes.</param>
 405    /// <param name="offset">The offset in the byte array to begin filling with random bytes.</param>
 406    /// <param name="length">The number of random bytes to put in the returned array.</param>
 407    /// <returns>An array of bytes of the specified length populated with random byte values.</returns>
 408    public void NextBytes(byte[] target, int offset = 0, int length = -1)
 409    {
 410#if NET5_0_OR_GREATER
 2411        ArgumentNullException.ThrowIfNull(target);
 412#else
 413        if (target is null) throw new ArgumentNullException(nameof(target));
 414#endif
 2415        int index = 0;
 416        ulong rawData;
 2417        for (int endOffset = (length < 0 || offset + length > target.Length) ? target.Length : (offset + length); offset
 418        {
 2419            rawData = NextUInt32;
 2420            target[offset] = (byte)(rawData & 0xff);
 2421            if (offset + 1 < endOffset) target[offset + 1] = (byte)((rawData & 0xff00) >> 8);
 2422            if (offset + 2 < endOffset) target[offset + 2] = (byte)((rawData & 0xff0000) >> 16);
 2423            if (offset + 3 < endOffset) target[offset + 3] = (byte)((rawData & 0xff000000) >> 24);
 2424            if (offset + 4 < endOffset) target[offset + 4] = (byte)((rawData & 0xff00000000) >> 32);
 2425            if (offset + 5 < endOffset) target[offset + 5] = (byte)((rawData & 0xff0000000000) >> 40);
 2426            if (offset + 6 < endOffset) target[offset + 6] = (byte)((rawData & 0xff000000000000) >> 48);
 2427            if (offset + 7 < endOffset) target[offset + 7] = (byte)((rawData & 0xff00000000000000) >> 56);
 428        }
 2429    }
 430    /// <summary>
 431    /// Populates an existing byte span with random values.
 432    /// </summary>
 433    /// <param name="target">The target Span of bytes.</param>
 434    /// <param name="offset">The offset in the byte array to begin filling with random bytes.</param>
 435    /// <param name="length">The number of random bytes to put in the returned array.</param>
 436    /// <returns>An array of bytes of the specified length populated with random byte values.</returns>
 437    public void NextBytes(Span<byte> target, int offset = 0, int length = -1)
 438    {
 2439        int index = 0;
 440        ulong rawData;
 2441        for (int endOffset = (length < 0 || offset + length > target.Length) ? target.Length : (offset + length); offset
 442        {
 2443            rawData = NextUInt32;
 2444            target[offset] = (byte)(rawData & 0xff);
 2445            if (offset + 1 < endOffset) target[offset + 1] = (byte)((rawData & 0xff00) >> 8);
 2446            if (offset + 2 < endOffset) target[offset + 2] = (byte)((rawData & 0xff0000) >> 16);
 2447            if (offset + 3 < endOffset) target[offset + 3] = (byte)((rawData & 0xff000000) >> 24);
 2448            if (offset + 4 < endOffset) target[offset + 4] = (byte)((rawData & 0xff00000000) >> 32);
 2449            if (offset + 5 < endOffset) target[offset + 5] = (byte)((rawData & 0xff0000000000) >> 40);
 2450            if (offset + 6 < endOffset) target[offset + 6] = (byte)((rawData & 0xff000000000000) >> 48);
 2451            if (offset + 7 < endOffset) target[offset + 7] = (byte)((rawData & 0xff00000000000000) >> 56);
 452        }
 2453    }
 454
 455    private const int DecimalMaxScalePlusOne = 29;
 456    /// <summary>
 457    /// Gets a <see cref="decimal"/> with a random value.  All possible values should be roughly evenly distributed.
 458    /// </summary>
 2459    public decimal NextDecimal => new(NextInt32Signed, NextInt32Signed, NextInt32Signed, NextBoolean, (byte)(NextUInt32 
 460    /// <summary>
 461    /// Gets a <see cref="double"/> with a random value.  All possible values should be roughly evenly distributed, incl
 462    /// </summary>
 2463    public double NextDouble => BitConverter.Int64BitsToDouble(NextInt64Signed);
 464    /// <summary>
 465    /// Gets a <see cref="Guid"/> with a random value.  All possible values should be roughly evenly distributed.
 466    /// Note that these "GUID"s should *not* be used publicly where GUIDs are expected to have certain characteristics t
 467    /// These GUIDs should only be used for testing.
 468    /// </summary>
 469    public Guid NextGuid
 470    {
 471        get
 472        {
 2473            byte[] bytes = NextNewBytes(16);
 2474            return new Guid(bytes);
 475        }
 476    }
 477    /// <summary>
 478    /// Gets a random value for an enum of the specified type.  All possible values should be roughly evenly distributed
 479    /// If the specified enum type is marked with <see cref="FlagsAttribute"/>, a random set of possible values are comb
 480    /// </summary>
 481    /// <param name="type">The type for the enum a random value is to be selected for.  Must be a non-null enum type.</p
 482    /// <returns>A random value for that enum.</returns>
 483    public object NextEnum(Type type)
 484    {
 485#if NET5_0_OR_GREATER
 2486        ArgumentNullException.ThrowIfNull(type);
 487#else
 488        if (type is null) throw new ArgumentNullException(nameof(type));
 489#endif
 2490        if (!(typeof(System.Enum)).IsAssignableFrom(type)) throw new ArgumentException("The type must be an enum type!",
 2491        FieldInfo[] enumValues = type.GetFields(BindingFlags.Public | BindingFlags.Static);
 492        // is this a flags type?
 2493        if (type.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0)
 494        {
 495            // build a randomized sequential array
 2496            int[] enumValueSelectorIndex = new int[enumValues.Length];
 2497            for (int value = 0; value < enumValues.Length; ++value)
 498            {
 2499                enumValueSelectorIndex[value] = value;
 500            }
 2501            for (int value = 0; value < enumValues.Length * 2; ++value)
 502            {
 2503                int pick1 = value % enumValues.Length;
 2504                int pick2 = NextInt32 % enumValues.Length;
 2505                int temp = enumValueSelectorIndex[pick1];
 2506                enumValueSelectorIndex[pick1] = enumValueSelectorIndex[pick2];
 2507                enumValueSelectorIndex[pick2] = temp;
 508            }
 509            // build a string containing random flag values
 2510            string enumStringValue = string.Empty;
 2511            int values = NextInt32 % enumValues.Length;
 512            // no values specified--use default value (0)
 2513            if (values == 0)
 514            {
 2515                return 0;
 516            }
 2517            for (int value = 0; value < values; ++value)
 518            {
 519                // Coverage note: the optimizer seems to insert a huge amount of code here with at least one branch, des
 2520                enumStringValue = "," + enumValues[enumValueSelectorIndex[value]].GetValue(type);
 521            }
 522            // have the CLR parse that value and give us a typed enum back
 523#if NET6_0_OR_GREATER
 2524            return Enum.Parse(type, enumStringValue.AsSpan(1));
 525#else
 526            return Enum.Parse(type, enumStringValue.Substring(1));
 527#endif
 528        }
 2529        return enumValues[NextUInt32 % enumValues.Length].GetValue(type)!;  // enum field values better not be null!
 530    }
 531    /// <summary>
 532    /// Gets a random typed enum value for a specific enum type.  Values will be roughly evenly distributed across all p
 533    /// If the specified enum type is marked with <see cref="FlagsAttribute"/>, a random set of possible values are comb
 534    /// </summary>
 535    /// <typeparam name="TEnum">The type of enum to get a random value for.</typeparam>
 536    /// <returns>A random value for that enum.</returns>
 537    public TEnum NextEnum<TEnum>()
 538    {
 2539        return (TEnum)NextEnum(typeof(TEnum));
 540    }
 541    /// <summary>
 542    /// Gets a 32-bit hash code for this instance.
 543    /// </summary>
 544    /// <returns>A 32-bit hash code for this instance.</returns>
 545    public override int GetHashCode()
 546    {
 2547        return _seed.GetHashCode();
 548    }
 549    /// <summary>
 550    /// Checks to see if the specified object is logically equal to this one.
 551    /// </summary>
 552    /// <param name="obj">The object to compare to.</param>
 553    /// <returns>true if <paramref name="obj"/> is logically equal to this instance, false if it is not.</returns>
 554    public override bool Equals(object? obj)
 555    {
 2556        if (obj is not Pseudorandom) return false;
 2557        return Equals((Pseudorandom)obj);
 558    }
 559    /// <summary>
 560    /// Checks to see if the specified Pseudorandom is logically equal to this one.
 561    /// </summary>
 562    /// <param name="other">The Pseudorandom to compare to.</param>
 563    /// <returns>true if <paramref name="other"/> is logically equal to this instance, false if it is not.</returns>
 564    public bool Equals(Pseudorandom? other)
 565    {
 2566        if (other is null) return false;
 2567        return _seed.Equals(other._seed);
 568    }
 569    /// <summary>
 570    /// Checks to see if two Pseudorandoms are logically equal.
 571    /// </summary>
 572    /// <param name="a">The first Pseudorandom to compare.</param>
 573    /// <param name="b">The second Pseudorandom to compare.</param>
 574    /// <returns>true if the Pseudorandoms are the same, otherwise false.</returns>
 575    public static bool operator ==(Pseudorandom? a, Pseudorandom? b)
 576    {
 2577        if (ReferenceEquals(a, b)) return true;
 2578        if (a is null || b is null) return false;
 2579        return a._seed == b._seed;
 580    }
 581    /// <summary>
 582    /// Checks to see if two Pseudorandoms are logically not equal.
 583    /// </summary>
 584    /// <param name="a">The first Pseudorandom to compare.</param>
 585    /// <param name="b">The second Pseudorandom to compare.</param>
 586    /// <returns>true if the Pseudorandoms are logically not equal, otherwise false.</returns>
 587    public static bool operator !=(Pseudorandom? a, Pseudorandom? b)
 588    {
 2589        if (ReferenceEquals(a, b)) return false;
 2590        if (a is null || b is null) return true;
 2591        return a._seed != b._seed;
 592    }
 593}