diff --git a/source/MonoGame.Extended/Math/FastRandom.cs b/source/MonoGame.Extended/Math/FastRandom.cs
index 472518b6..dd0184c0 100644
--- a/source/MonoGame.Extended/Math/FastRandom.cs
+++ b/source/MonoGame.Extended/Math/FastRandom.cs
@@ -1,127 +1,342 @@
using System;
+using System.Runtime.CompilerServices;
using Microsoft.Xna.Framework;
namespace MonoGame.Extended
{
+
///
- /// A random number generator that uses a fast algorithm to generate random values.
- /// The speed comes at the price of true 'randomness' though, there are noticeable
- /// patterns & it compares quite unfavourably to other algorithms in that respect.
- /// It's a good choice in situations where speed is more desirable than a
- /// good random distribution, and a poor choice when random distribution is important.
+ /// Represents a pseudo-random number generator using a linear congruential generator algorithm.
///
+ ///
+ ///
+ /// This implementation uses the same constants as Microsoft Visual C++ rand() function:
+ ///
+ /// a=214013
+ /// c=2531011
+ /// m=2^31
+ ///
+ ///
+ /// It provides high performance and speed, but comes at the price of having lower statistical quality, or true
+ /// 'randomness' compared to modern algorithms. The algorithm is deterministic based on the initial seed
+ /// value, making it suitable for reproducible sequences.
+ ///
+ ///
+ /// Note: This pseudo-random number generator exhibits noticeable patterns and should not be used for
+ /// cryptographic purposes or when a high-quality random distribution is critical. Consider using
+ /// for better statistical properties.
+ ///
+ ///
public class FastRandom
{
- private int _state;
+ private readonly IFastRandomImpl _impl;
- public FastRandom()
- : this(1)
+ ///
+ /// Provides a thread-safe instance that may be used concurrently from any thread.
+ ///
+ public static FastRandom Shared { get; } = new FastRandom(new ThreadSafeFastRandomImpl());
+
+ ///
+ /// Initializes a new instance of the class using the default seed value.
+ ///
+ public FastRandom() : this(1)
{
+
}
+ ///
+ /// Initializes a new instance of the class using the specified seed value.
+ ///
+ /// A number used to calculate a starting value for the pseudo-random number sequence.
public FastRandom(int seed)
{
- if (seed < 1)
- throw new ArgumentOutOfRangeException(nameof(seed), "seed must be greater than zero");
+ _impl = new LinearCongruentialGeneratorImpl(seed);
+ }
- _state = seed;
+ private FastRandom(IFastRandomImpl impl)
+ {
+ ArgumentNullException.ThrowIfNull(impl);
+ _impl = impl;
}
///
- /// Gets the next random integer value.
+ /// Returns a non-negative random integer.
///
- /// A random positive integer.
+ /// A 32-bit signed integer that is greater than or equal to 0 and less than 32768.
public int Next()
{
- _state = 214013*_state + 2531011;
- return (_state >> 16) & 0x7FFF;
+ return _impl.Next();
}
///
- /// Gets the next random integer value which is greater than zero and less than or equal to
- /// the specified maxmimum value.
+ /// Returns a non-negative random integer that is less than or equal to the specified maximum.
///
- /// The maximum random integer value to return.
- /// A random integer value between zero and the specified maximum value.
+ /// The inclusive upper bound of the random number to be generated.
+ ///
+ /// A 32-bit signed integer that is greater than or equal to 0 and less than or equal to .
+ ///
public int Next(int max)
{
- return (int) (max*NextSingle() + 0.5f);
+ return _impl.Next(max);
}
///
- /// Gets the next random integer between the specified minimum and maximum values.
+ /// Returns a random integer that is within a specified range.
///
- /// The inclusive minimum value.
- /// The inclusive maximum value.
+ /// The inclusive lower bound of the random number returned.
+ /// The inclusive upper bound of the random number returned.
+ ///
+ /// A 32-bit signed integer that is greater than or equal to and less than or equal to
+ /// .
+ ///
public int Next(int min, int max)
{
- return (int) ((max - min)*NextSingle() + 0.5f) + min;
+ return _impl.Next(min, max);
}
///
- /// Gets the next random integer between the specified range of values.
+ /// Returns a random integer that is within a specified range.
///
- /// A range representing the inclusive minimum and maximum values.
- /// A random integer between the specified minumum and maximum values.
+ ///
+ /// A range representing the inclusive lower and upper bound of the random number to return.
+ ///
+ ///
+ /// A 32-bit signed integer that is greater than or equal to the and less than or
+ /// equal to the value of .
+ ///
public int Next(Range range)
{
- return Next(range.Min, range.Max);
+ return _impl.Next(range);
}
///
- /// Gets the next random single value.
+ /// Returns a random floating-point number that is greater than or equal to 0.0 and less than 1.0.
///
- /// A random single value between 0 and 1.
+ ///
+ /// A single-precision floating point number that is greater than or equal to 0.0 and less than 1.0.
+ ///
public float NextSingle()
{
- return Next()/(float) short.MaxValue;
+ return _impl.NextSingle();
}
///
- /// Gets the next random single value which is greater than zero and less than or equal to
- /// the specified maxmimum value.
+ /// Returns a random floating-point number that is greater than or equal to 0.0 and less than the
+ /// specified maximum.
///
- /// The maximum random single value to return.
- /// A random single value between zero and the specified maximum value.
+ /// The exclusive upper bond of the random number generated.
+ ///
+ /// A single precision floating-point number that is greater than or equal to 0.0 and less than
+ /// .
+ ///
public float NextSingle(float max)
{
- return max*NextSingle();
+ return _impl.NextSingle(max);
}
///
- /// Gets the next random single value between the specified minimum and maximum values.
+ /// Returns a random floating-point number that is within a specified range.
///
- /// The inclusive minimum value.
- /// The inclusive maximum value.
- /// A random single value between the specified minimum and maximum values.
+ /// The inclusive lower bound of the random number returned.
+ /// The exclusive upper bound of the random number returned.
+ ///
+ /// A single-precision floating point number that is greater than or equal to and
+ /// less than .
+ ///
public float NextSingle(float min, float max)
{
- return (max - min)*NextSingle() + min;
+ return _impl.NextSingle(min, max);
}
///
- /// Gets the next random single value between the specified range of values.
+ /// Returns a random floating-point number that is within a specified range.
///
- /// A range representing the inclusive minimum and maximum values.
- /// A random single value between the specified minimum and maximum values.
+ ///
+ /// A range representing the inclusive lower and exclusive upper bound of the random number returned.
+ ///
+ ///
+ /// A single-precision floating point number that is greater than or equal to the
+ /// and less than the value of
+ ///
public float NextSingle(Range range)
{
- return NextSingle(range.Min, range.Max);
+ return _impl.NextSingle(range);
}
///
- /// Gets the next random angle value.
+ /// Returns a random angle between -π and π.
///
- /// A random angle value.
+ ///
+ /// A random angle value in radians.
+ ///
public float NextAngle()
{
- return NextSingle(-MathHelper.Pi, MathHelper.Pi);
+ return _impl.NextAngle();
}
+ ///
+ /// Gets a random unit vector.
+ ///
+ /// When this method returns, contains a unit vector with a random direction.
public void NextUnitVector(out Vector2 vector)
{
- var angle = NextAngle();
- vector = new Vector2((float) Math.Cos(angle), (float) Math.Sin(angle));
+ _impl.NextUnitVector(out vector);
}
+
+ #region IFastRandomImplementation
+ private interface IFastRandomImpl
+ {
+ int Next();
+ int Next(int max);
+ int Next(int min, int max);
+ int Next(Range range);
+ float NextSingle();
+ float NextSingle(float max);
+ float NextSingle(float min, float max);
+ float NextSingle(Range range);
+ float NextAngle();
+ void NextUnitVector(out Vector2 vector);
+ }
+ #endregion
+
+ #region Linear Congruential Generator
+ private sealed class LinearCongruentialGeneratorImpl : IFastRandomImpl
+ {
+ private const int MULTIPLIER = 214013;
+ private const int INCREMENT = 2531011;
+
+ private int _state;
+
+ public LinearCongruentialGeneratorImpl() : this(1)
+ {
+
+ }
+
+ public LinearCongruentialGeneratorImpl(int seed)
+ {
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(seed);
+ _state = seed;
+ }
+
+ public int Next()
+ {
+ _state = MULTIPLIER * _state + INCREMENT;
+ return (_state >> 16) & 0x7FFF;
+ }
+
+ public int Next(int max)
+ {
+ return (int)(max * NextSingle() + 0.5f);
+ }
+
+ public int Next(int min, int max)
+ {
+ return (int)((max - min) * NextSingle() + 0.5f) + min;
+ }
+
+ public int Next(Range range)
+ {
+ return Next(range.Min, range.Max);
+ }
+
+ public float NextSingle()
+ {
+ return Next() / (float)short.MaxValue;
+ }
+
+ public float NextSingle(float max)
+ {
+ return max * NextSingle();
+ }
+
+ public float NextSingle(float min, float max)
+ {
+ return (max - min) * NextSingle() + min;
+ }
+
+ public float NextSingle(Range range)
+ {
+ return NextSingle(range.Min, range.Max);
+ }
+
+ public float NextAngle()
+ {
+ return NextSingle(-MathHelper.Pi, MathHelper.Pi);
+ }
+
+ public void NextUnitVector(out Vector2 vector)
+ {
+ float angle = NextAngle();
+ vector.X = MathF.Cos(angle);
+ vector.Y = MathF.Sin(angle);
+ }
+ }
+ #endregion
+
+ #region ThreadSafeImpl
+ private sealed class ThreadSafeFastRandomImpl : IFastRandomImpl
+ {
+ [ThreadStatic]
+ private static LinearCongruentialGeneratorImpl t_random;
+
+ private static LinearCongruentialGeneratorImpl LocalRandom => t_random ?? Create();
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static LinearCongruentialGeneratorImpl Create()
+ {
+ t_random = new LinearCongruentialGeneratorImpl();
+ return t_random;
+ }
+
+ public int Next()
+ {
+ return LocalRandom.Next();
+ }
+
+ public int Next(int max)
+ {
+ return LocalRandom.Next(max);
+ }
+
+ public int Next(int min, int max)
+ {
+ return LocalRandom.Next(min, max);
+ }
+
+ public int Next(Range range)
+ {
+ return LocalRandom.Next(range.Min, range.Max);
+ }
+ public float NextSingle()
+ {
+ return LocalRandom.NextSingle();
+ }
+
+ public float NextSingle(float max)
+ {
+ return LocalRandom.NextSingle(max);
+ }
+
+ public float NextSingle(float min, float max)
+ {
+ return LocalRandom.NextSingle(min, max);
+ }
+
+ public float NextSingle(Range range)
+ {
+ return LocalRandom.NextSingle(range.Min, range.Max);
+ }
+
+ public float NextAngle()
+ {
+ return LocalRandom.NextAngle();
+ }
+
+ public void NextUnitVector(out Vector2 vector)
+ {
+ LocalRandom.NextUnitVector(out vector);
+ }
+ }
+ #endregion
}
-}
\ No newline at end of file
+}