diff --git a/src/libraries/System.Private.CoreLib/src/System/Random.cs b/src/libraries/System.Private.CoreLib/src/System/Random.cs
index 9fa1859187cbf0..35ffb4c8e2d9cf 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Random.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Random.cs
@@ -15,6 +15,8 @@ namespace System
///
public partial class Random
{
+ private const int StackallocThreshold = 256;
+
/// The underlying generator implementation.
///
/// This is separated out so that different generators can be used based on how this Random instance is constructed.
@@ -179,6 +181,298 @@ public virtual void NextBytes(byte[] buffer)
/// The array to be filled with random numbers.
public virtual void NextBytes(Span buffer) => _impl.NextBytes(buffer);
+ /// Returns a non-negative random integer of type .
+ /// The type of integer to generate.
+ ///
+ /// A value of type in the inclusive range [0, T.MaxValue].
+ ///
+ ///
+ /// Unlike , which returns an that is less than ,
+ /// NextInteger<int>() returns an in the inclusive range from zero through
+ /// and may return .
+ /// must use a two's complement representation for signed values.
+ ///
+ public T NextInteger() where T : IBinaryInteger, IMinMaxValue
+ {
+ if (T.MaxValue == T.Zero)
+ {
+ return T.Zero;
+ }
+
+ Debug.Assert(T.IsPositive(T.MaxValue));
+
+ int bitLength = T.MaxValue.GetShortestBitLength();
+ int byteCount = (bitLength + 7) >> 3;
+
+ // Compute mask for the top byte to avoid negative values for signed types
+ // and to reduce rejection rate for custom integer types.
+ int topBits = bitLength & 7;
+ byte topMask = topBits == 0 ? byte.MaxValue : (byte)((1 << topBits) - 1);
+
+ byte[]? rented = null;
+ Span bytes = byteCount <= StackallocThreshold
+ ? stackalloc byte[StackallocThreshold]
+ : rented = ArrayPool.Shared.Rent(byteCount);
+ bytes = bytes.Slice(0, byteCount);
+
+ try
+ {
+ while (true)
+ {
+ NextBytes(bytes);
+ bytes[^1] &= topMask;
+
+ T value = T.ReadLittleEndian(bytes, isUnsigned: true);
+ if (value <= T.MaxValue)
+ {
+ return value;
+ }
+ }
+ }
+ finally
+ {
+ if (rented is not null)
+ {
+ ArrayPool.Shared.Return(rented);
+ }
+ }
+ }
+
+ /// Returns a non-negative random integer that is less than the specified maximum.
+ /// The type of integer to generate.
+ /// The exclusive upper bound of the random number to be generated.
+ /// must be greater than or equal to zero.
+ ///
+ /// A value of type that is greater than or equal to zero,
+ /// and less than ; that is, the range of return values is ordinarily
+ /// [0, ). However, if equals zero, zero is returned.
+ ///
+ /// is less than zero.
+ /// must use a two's complement representation for signed values.
+ public T NextInteger(T maxValue) where T : IBinaryInteger
+ {
+ ArgumentOutOfRangeException.ThrowIfNegative(maxValue);
+
+ return NextBinaryIntegerInRange(maxValue);
+ }
+
+ /// Returns a random integer that is within a specified range.
+ /// The type of integer to generate.
+ /// The inclusive lower bound of the random number returned.
+ /// The exclusive upper bound of the random number returned.
+ /// must be greater than or equal to .
+ ///
+ /// A value of type greater than or equal to
+ /// and less than ; that is, the range of return values is ordinarily
+ /// [, ). If
+ /// equals , is returned.
+ ///
+ /// is greater than .
+ /// must use a two's complement representation for signed values.
+ public T NextInteger(T minValue, T maxValue) where T : IBinaryInteger
+ {
+ if (minValue > maxValue)
+ {
+ ThrowMinMaxValueSwapped();
+ }
+
+ T range = maxValue - minValue;
+
+ // For signed types, subtraction may overflow when the range exceeds T.MaxValue.
+ // T.IsNegative(range) detects this. Fall back to full-width generation.
+ if (T.IsNegative(range))
+ {
+ return NextBinaryIntegerFullRange(minValue, maxValue);
+ }
+
+ return NextBinaryIntegerInRange(range) + minValue;
+ }
+
+ /// Generates a random value in [T.Zero, maxExclusive) where maxExclusive is non-negative.
+ private T NextBinaryIntegerInRange(T maxExclusive) where T : IBinaryInteger
+ {
+ Debug.Assert(!T.IsNegative(maxExclusive));
+
+ // Fast paths for common types using existing optimized implementations.
+ // The JIT eliminates the dead branches when T is a known value type.
+ if (typeof(T) == typeof(sbyte) ||
+ typeof(T) == typeof(byte) ||
+ typeof(T) == typeof(short) ||
+ typeof(T) == typeof(ushort) ||
+ typeof(T) == typeof(char) ||
+ typeof(T) == typeof(int) ||
+ (typeof(T) == typeof(nint) && nint.Size == 4))
+ {
+ return T.CreateTruncating(Next(int.CreateTruncating(maxExclusive)));
+ }
+
+ if (typeof(T) == typeof(uint) ||
+ typeof(T) == typeof(nint) ||
+ typeof(T) == typeof(long) ||
+ (typeof(T) == typeof(nuint) && nint.Size == 4))
+ {
+ return T.CreateTruncating(NextInt64(long.CreateTruncating(maxExclusive)));
+ }
+
+ // We can't always use a fast path for these types, but if the maxExclusive value
+ // fits within a long, we can just generate a long and cast. The round-trip check
+ // ensures we don't silently truncate values for types larger than ulong.
+ if (typeof(T) == typeof(ulong) ||
+ (typeof(T) == typeof(nuint) && nint.Size == 8) ||
+ typeof(T) == typeof(Int128) ||
+ typeof(T) == typeof(UInt128))
+ {
+ ulong maxExclusiveUlong = ulong.CreateTruncating(maxExclusive);
+ if (maxExclusiveUlong <= (ulong)long.MaxValue &&
+ T.CreateTruncating(maxExclusiveUlong) == maxExclusive)
+ {
+ return T.CreateTruncating(NextInt64((long)maxExclusiveUlong));
+ }
+ }
+
+ // Generic fallback for large ulong, nuint, Int128, UInt128, BigInteger, etc.
+ return NextBinaryIntegerRejectionSampling(maxExclusive);
+ }
+
+ /// Generic rejection sampling for arbitrary types.
+ private T NextBinaryIntegerRejectionSampling(T maxExclusive) where T : IBinaryInteger
+ {
+ if (maxExclusive == T.Zero)
+ {
+ return T.Zero;
+ }
+
+ Debug.Assert(T.IsPositive(maxExclusive));
+
+ int bitLength = maxExclusive.GetShortestBitLength();
+ int byteCount = (bitLength + 7) >> 3;
+
+ // Compute mask for the top byte to reduce rejection rate.
+ int topBits = bitLength & 7;
+ byte topMask = topBits == 0 ? byte.MaxValue : (byte)((1 << topBits) - 1);
+
+ byte[]? rented = null;
+ Span bytes = byteCount <= StackallocThreshold
+ ? stackalloc byte[StackallocThreshold]
+ : rented = ArrayPool.Shared.Rent(byteCount);
+ bytes = bytes.Slice(0, byteCount);
+
+ try
+ {
+ while (true)
+ {
+ NextBytes(bytes);
+ bytes[^1] &= topMask;
+
+ T value = T.ReadLittleEndian(bytes, isUnsigned: true);
+ if (value < maxExclusive)
+ {
+ return value;
+ }
+ }
+ }
+ finally
+ {
+ if (rented is not null)
+ {
+ ArrayPool.Shared.Return(rented);
+ }
+ }
+ }
+
+ /// Handles the case where the range overflows for signed types by generating full-width random values.
+ private T NextBinaryIntegerFullRange(T minValue, T maxValue) where T : IBinaryInteger
+ {
+ Debug.Assert(minValue < maxValue);
+
+ // The range exceeds what T can represent as a positive value.
+ // Generate a random value across the full range of T and check bounds.
+ // Since the range > T.MaxValue, the acceptance rate is > 50%.
+ int byteCount = Math.Max(minValue.GetByteCount(), maxValue.GetByteCount());
+
+ byte[]? rented = null;
+ Span bytes = byteCount <= StackallocThreshold
+ ? stackalloc byte[StackallocThreshold]
+ : rented = ArrayPool.Shared.Rent(byteCount);
+ bytes = bytes.Slice(0, byteCount);
+
+ try
+ {
+ while (true)
+ {
+ NextBytes(bytes);
+
+ T value = T.ReadLittleEndian(bytes, isUnsigned: false);
+ if (value >= minValue && value < maxValue)
+ {
+ return value;
+ }
+ }
+ }
+ finally
+ {
+ if (rented is not null)
+ {
+ ArrayPool.Shared.Return(rented);
+ }
+ }
+ }
+
+ /// Returns a random binary floating-point number of type that is greater than or equal to 0.0, and less than 1.0.
+ /// The type of floating-point number to generate.
+ /// A binary floating-point number of type in the range [0.0, 1.0).
+ public T NextBinaryFloat() where T : IBinaryFloatingPointIeee754
+ {
+ // Fast paths for common types using existing optimized implementations.
+ if (typeof(T) == typeof(float))
+ {
+ return T.CreateTruncating(NextSingle());
+ }
+
+ if (typeof(T) == typeof(double))
+ {
+ return T.CreateTruncating(NextDouble());
+ }
+
+ if (typeof(T) == typeof(NFloat))
+ {
+ return nint.Size == 8
+ ? T.CreateTruncating(NextDouble())
+ : T.CreateTruncating(NextSingle());
+ }
+
+ // For Half, BFloat16, and other low-precision types, converting from NextSingle()
+ // can round up to 1.0. Generate the value directly using the type's significand
+ // bit length to guarantee the result is in [0.0, 1.0).
+ int significandBitLength = T.Zero.GetSignificandBitLength();
+
+ // For types with significand >= 63 bits, 1L << significandBitLength would overflow.
+ // Build the random significand using chunks of up to 62 random bits. Since T has
+ // significandBitLength bits of precision, all intermediate values are exact.
+ // Note: No built-in IEEE type reaches this path (double has the largest significand
+ // at 53 bits). This handles hypothetical custom IBinaryFloatingPointIeee754
+ // implementations with wider significands (e.g. Quad/Float128 with 113 bits).
+ if (significandBitLength >= 63)
+ {
+ T value = T.Zero;
+ int bitsRemaining = significandBitLength;
+ while (bitsRemaining > 0)
+ {
+ int chunk = Math.Min(bitsRemaining, 62);
+ Debug.Assert(chunk >= 1 && chunk <= 62);
+ long randomChunk = NextInt64(1L << chunk);
+ value = T.ScaleB(value, chunk) + T.CreateTruncating(randomChunk);
+ bitsRemaining -= chunk;
+ }
+
+ Debug.Assert(value >= T.Zero && value < T.ScaleB(T.One, significandBitLength));
+ return T.ScaleB(value, -significandBitLength);
+ }
+
+ long randomBits = NextInt64(1L << significandBitLength);
+ return T.ScaleB(T.CreateTruncating(randomBits), -significandBitLength);
+ }
+
///
/// Fills the elements of a specified span with items chosen at random from the provided set of choices.
///
diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs
index 1e205d855b5d0a..36628da51727ec 100644
--- a/src/libraries/System.Runtime/ref/System.Runtime.cs
+++ b/src/libraries/System.Runtime/ref/System.Runtime.cs
@@ -5044,12 +5044,16 @@ public void GetItems(System.ReadOnlySpan choices, System.Span destinati
public virtual int Next() { throw null; }
public virtual int Next(int maxValue) { throw null; }
public virtual int Next(int minValue, int maxValue) { throw null; }
+ public T NextBinaryFloat() where T : System.Numerics.IBinaryFloatingPointIeee754 { throw null; }
public virtual void NextBytes(byte[] buffer) { }
public virtual void NextBytes(System.Span buffer) { }
public virtual double NextDouble() { throw null; }
public virtual long NextInt64() { throw null; }
public virtual long NextInt64(long maxValue) { throw null; }
public virtual long NextInt64(long minValue, long maxValue) { throw null; }
+ public T NextInteger() where T : System.Numerics.IBinaryInteger, System.Numerics.IMinMaxValue { throw null; }
+ public T NextInteger(T maxValue) where T : System.Numerics.IBinaryInteger { throw null; }
+ public T NextInteger(T minValue, T maxValue) where T : System.Numerics.IBinaryInteger { throw null; }
public virtual float NextSingle() { throw null; }
protected virtual double Sample() { throw null; }
public void Shuffle(System.Span values) { }
diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Random.cs b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Random.cs
index 3ab2f0a090c853..6729aed2296e6f 100644
--- a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Random.cs
+++ b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Random.cs
@@ -2,9 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
+using System.Numerics;
using System.Reflection;
+using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -997,6 +1000,522 @@ public static void GetHexString_Span_ProducesExpectedItems()
}
}
+ [Theory]
+ [InlineData(false, false)]
+ [InlineData(false, true)]
+ [InlineData(true, false)]
+ [InlineData(true, true)]
+ public void NextIntegerT_InvalidArguments_Throws(bool derived, bool seeded)
+ {
+ Random r = Create(derived, seeded);
+
+ // Negative maxValue throws for all signed types.
+ Assert.Throws(() => r.NextInteger(-1));
+ Assert.Throws(() => r.NextInteger(-1));
+ Assert.Throws(() => r.NextInteger(-1));
+ Assert.Throws(() => r.NextInteger(-1));
+ Assert.Throws(() => r.NextInteger(-1));
+ Assert.Throws(() => r.NextInteger(-1));
+ Assert.Throws(() => r.NextInteger(-1));
+
+ // minValue > maxValue throws.
+ Assert.Throws(() => r.NextInteger(2, 1));
+ Assert.Throws(() => r.NextInteger(2, 1));
+ Assert.Throws(() => r.NextInteger((byte)5, (byte)3));
+ Assert.Throws(() => r.NextInteger(10u, 5u));
+ Assert.Throws(() => r.NextInteger((Int128)10, (Int128)5));
+ Assert.Throws(() => r.NextInteger(10, 5));
+ }
+
+ [Theory]
+ [InlineData(false, false)]
+ [InlineData(false, true)]
+ [InlineData(true, false)]
+ [InlineData(true, true)]
+ public void NextIntegerT_ZeroMaxValue_ReturnsZero(bool derived, bool seeded)
+ {
+ Random r = Create(derived, seeded);
+ AssertNextIntegerTZeroMaxValue(r);
+ AssertNextIntegerTZeroMaxValue(r);
+ AssertNextIntegerTZeroMaxValue(r);
+ AssertNextIntegerTZeroMaxValue(r);
+ AssertNextIntegerTZeroMaxValue(r);
+ AssertNextIntegerTZeroMaxValue(r);
+ AssertNextIntegerTZeroMaxValue(r);
+ AssertNextIntegerTZeroMaxValue(r);
+ AssertNextIntegerTZeroMaxValue(r);
+ AssertNextIntegerTZeroMaxValue(r);
+ AssertNextIntegerTZeroMaxValue(r);
+ AssertNextIntegerTZeroMaxValue(r);
+ AssertNextIntegerTZeroMaxValue(r);
+
+ static void AssertNextIntegerTZeroMaxValue(Random r) where T : IBinaryInteger
+ {
+ Assert.Equal(T.Zero, r.NextInteger(T.Zero));
+ Assert.Equal(T.Zero, r.NextInteger(T.Zero, T.Zero));
+ }
+ }
+
+ [Theory]
+ [InlineData(false, false)]
+ [InlineData(false, true)]
+ [InlineData(true, false)]
+ [InlineData(true, true)]
+ public void NextIntegerT_EqualMinMax_ReturnsMinValue(bool derived, bool seeded)
+ {
+ Random r = Create(derived, seeded);
+ AssertNextIntegerTEqualMinMax(r, (byte)42);
+ AssertNextIntegerTEqualMinMax(r, (sbyte)-10);
+ AssertNextIntegerTEqualMinMax(r, (short)1000);
+ AssertNextIntegerTEqualMinMax(r, (ushort)500);
+ AssertNextIntegerTEqualMinMax(r, 12345);
+ AssertNextIntegerTEqualMinMax(r, 99u);
+ AssertNextIntegerTEqualMinMax(r, -42L);
+ AssertNextIntegerTEqualMinMax(r, 100UL);
+ AssertNextIntegerTEqualMinMax(r, (nint)7);
+ AssertNextIntegerTEqualMinMax(r, (nuint)7);
+ AssertNextIntegerTEqualMinMax(r, (Int128)(-77));
+ AssertNextIntegerTEqualMinMax(r, (UInt128)200);
+
+ static void AssertNextIntegerTEqualMinMax(Random r, T value) where T : IBinaryInteger
+ {
+ Assert.Equal(value, r.NextInteger(value, value));
+ }
+ }
+
+ [Theory]
+ [InlineData(false, false)]
+ [InlineData(false, true)]
+ [InlineData(true, false)]
+ [InlineData(true, true)]
+ public void NextIntegerT_SingleElementRange_ReturnsMinValue(bool derived, bool seeded)
+ {
+ Random r = Create(derived, seeded);
+ AssertNextIntegerTSingleElement(r, (byte)5, (byte)6);
+ AssertNextIntegerTSingleElement(r, (sbyte)-3, (sbyte)-2);
+ AssertNextIntegerTSingleElement(r, (short)100, (short)101);
+ AssertNextIntegerTSingleElement(r, (ushort)200, (ushort)201);
+ AssertNextIntegerTSingleElement(r, 42, 43);
+ AssertNextIntegerTSingleElement(r, 42u, 43u);
+ AssertNextIntegerTSingleElement(r, -1L, 0L);
+ AssertNextIntegerTSingleElement(r, 99UL, 100UL);
+ AssertNextIntegerTSingleElement(r, (nint)10, (nint)11);
+ AssertNextIntegerTSingleElement(r, (nuint)10, (nuint)11);
+ AssertNextIntegerTSingleElement(r, (Int128)(-1), (Int128)0);
+ AssertNextIntegerTSingleElement(r, (UInt128)50, (UInt128)51);
+
+ static void AssertNextIntegerTSingleElement(Random r, T min, T max) where T : IBinaryInteger
+ {
+ for (int i = 0; i < 10; i++)
+ {
+ Assert.Equal(min, r.NextInteger(min, max));
+ }
+ }
+ }
+
+ [Theory]
+ [InlineData(false, false)]
+ [InlineData(false, true)]
+ [InlineData(true, false)]
+ [InlineData(true, true)]
+ public void NextIntegerT_AllBuiltInTypes_MaxValueInRange(bool derived, bool seeded)
+ {
+ Random r = Create(derived, seeded);
+ AssertNextIntegerTMaxValueInRange(r, (byte)100);
+ AssertNextIntegerTMaxValueInRange(r, (sbyte)50);
+ AssertNextIntegerTMaxValueInRange(r, (short)500);
+ AssertNextIntegerTMaxValueInRange(r, (ushort)500);
+ AssertNextIntegerTMaxValueInRange(r, (char)100);
+ AssertNextIntegerTMaxValueInRange(r, 1000);
+ AssertNextIntegerTMaxValueInRange(r, 1000u);
+ AssertNextIntegerTMaxValueInRange(r, 1000L);
+ AssertNextIntegerTMaxValueInRange(r, 1000UL);
+ AssertNextIntegerTMaxValueInRange(r, (nint)1000);
+ AssertNextIntegerTMaxValueInRange(r, (nuint)1000);
+ AssertNextIntegerTMaxValueInRange(r, (Int128)1000);
+ AssertNextIntegerTMaxValueInRange(r, (UInt128)1000);
+ }
+
+ [Theory]
+ [InlineData(false, false)]
+ [InlineData(false, true)]
+ [InlineData(true, false)]
+ [InlineData(true, true)]
+ public void NextIntegerT_AllBuiltInTypes_MinMaxInRange(bool derived, bool seeded)
+ {
+ Random r = Create(derived, seeded);
+ AssertNextIntegerTMinMaxInRange(r, (byte)10, (byte)200);
+ AssertNextIntegerTMinMaxInRange(r, (sbyte)-50, (sbyte)50);
+ AssertNextIntegerTMinMaxInRange(r, (short)-500, (short)500);
+ AssertNextIntegerTMinMaxInRange(r, (ushort)100, (ushort)1000);
+ AssertNextIntegerTMinMaxInRange(r, -1000, 1000);
+ AssertNextIntegerTMinMaxInRange(r, 50u, 500u);
+ AssertNextIntegerTMinMaxInRange(r, -100_000L, 100_000L);
+ AssertNextIntegerTMinMaxInRange(r, 100UL, 1000UL);
+ AssertNextIntegerTMinMaxInRange(r, (nint)(-100), (nint)100);
+ AssertNextIntegerTMinMaxInRange(r, (nuint)10, (nuint)500);
+ AssertNextIntegerTMinMaxInRange(r, (Int128)(-1000), (Int128)1000);
+ AssertNextIntegerTMinMaxInRange(r, (UInt128)50, (UInt128)500);
+ }
+
+ [Theory]
+ [InlineData(false, false)]
+ [InlineData(false, true)]
+ [InlineData(true, false)]
+ [InlineData(true, true)]
+ public void NextIntegerT_AllValuesInSmallRangeHit(bool derived, bool seeded)
+ {
+ Random r = Create(derived, seeded);
+ const int rangeSize = 5;
+ AssertAllValuesHit(r, (byte)rangeSize);
+ AssertAllValuesHit(r, (sbyte)rangeSize);
+ AssertAllValuesHit(r, (short)rangeSize);
+ AssertAllValuesHit(r, (ushort)rangeSize);
+ AssertAllValuesHit(r, rangeSize);
+ AssertAllValuesHit(r, (uint)rangeSize);
+ AssertAllValuesHit(r, (long)rangeSize);
+ AssertAllValuesHit(r, (ulong)rangeSize);
+ AssertAllValuesHit(r, (nint)rangeSize);
+ AssertAllValuesHit(r, (nuint)rangeSize);
+ AssertAllValuesHit(r, (Int128)rangeSize);
+ AssertAllValuesHit(r, (UInt128)rangeSize);
+
+ static void AssertAllValuesHit(Random r, T maxExclusive) where T : IBinaryInteger
+ {
+ HashSet seen = [];
+ for (int i = 0; i < 10_000; i++)
+ {
+ seen.Add(r.NextInteger(maxExclusive));
+ }
+
+ for (T v = T.Zero; v < maxExclusive; v++)
+ {
+ Assert.Contains(v, seen);
+ }
+
+ Assert.DoesNotContain(maxExclusive, seen);
+ }
+ }
+
+ [Theory]
+ [InlineData(false, false)]
+ [InlineData(false, true)]
+ [InlineData(true, false)]
+ [InlineData(true, true)]
+ public void NextIntegerT_Parameterless_AllTypes(bool derived, bool seeded)
+ {
+ Random r = Create(derived, seeded);
+ AssertNextIntegerTParameterless(r);
+ AssertNextIntegerTParameterless(r);
+ AssertNextIntegerTParameterless(r);
+ AssertNextIntegerTParameterless(r);
+ AssertNextIntegerTParameterless(r);
+ AssertNextIntegerTParameterless(r);
+ AssertNextIntegerTParameterless(r);
+ AssertNextIntegerTParameterless(r);
+ AssertNextIntegerTParameterless(r);
+ AssertNextIntegerTParameterless(r);
+ AssertNextIntegerTParameterless(r);
+ AssertNextIntegerTParameterless(r);
+ AssertNextIntegerTParameterless(r);
+
+ static void AssertNextIntegerTParameterless(Random r) where T : IBinaryInteger, IMinMaxValue
+ {
+ for (int i = 0; i < 100; i++)
+ {
+ T value = r.NextInteger();
+ Assert.True(value >= T.Zero, $"NextInteger<{typeof(T).Name}>() returned negative value: {value}");
+ Assert.True(value <= T.MaxValue, $"NextInteger<{typeof(T).Name}>() returned a value greater than MaxValue: {value}");
+ }
+ }
+ }
+
+ [Fact]
+ public void NextIntegerT_Parameterless_CanReturnMaxValue()
+ {
+ Random r = new MaxValueRandom();
+ Assert.Equal(byte.MaxValue, r.NextInteger());
+ Assert.Equal(sbyte.MaxValue, r.NextInteger());
+ Assert.Equal(short.MaxValue, r.NextInteger());
+ Assert.Equal(ushort.MaxValue, r.NextInteger());
+ Assert.Equal(char.MaxValue, r.NextInteger());
+ Assert.Equal(int.MaxValue, r.NextInteger());
+ Assert.Equal(uint.MaxValue, r.NextInteger());
+ Assert.Equal(long.MaxValue, r.NextInteger());
+ Assert.Equal(ulong.MaxValue, r.NextInteger());
+ Assert.Equal(nint.MaxValue, r.NextInteger());
+ Assert.Equal(nuint.MaxValue, r.NextInteger());
+ Assert.Equal(Int128.MaxValue, r.NextInteger());
+ Assert.Equal(UInt128.MaxValue, r.NextInteger());
+ }
+
+ public static IEnumerable