From 5cac3cada4a9b30bf0f56fd4fe939c966d6aef20 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 27 Apr 2026 13:10:31 -0400 Subject: [PATCH] Add generic Random numeric APIs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/System/Random.cs | 294 ++++++ .../System.Runtime/ref/System.Runtime.cs | 4 + .../System/Random.cs | 957 ++++++++++++++++++ 3 files changed, 1255 insertions(+) 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 NextIntegerT_SignedOverflowRange_MemberData() => + from derived in new[] { false, true } + from seeded in new[] { false, true } + select new object[] { derived, seeded }; + + [Theory] + [MemberData(nameof(NextIntegerT_SignedOverflowRange_MemberData))] + public void NextIntegerT_SignedOverflow_FullRange(bool derived, bool seeded) + { + Random r = Create(derived, seeded); + + // These ranges exceed T.MaxValue, triggering the NextBinaryIntegerFullRange path. + AssertNextIntegerTMinMaxInRange(r, sbyte.MinValue, sbyte.MaxValue); + AssertNextIntegerTMinMaxInRange(r, short.MinValue, short.MaxValue); + AssertNextIntegerTMinMaxInRange(r, int.MinValue, int.MaxValue); + AssertNextIntegerTMinMaxInRange(r, long.MinValue, long.MaxValue); + AssertNextIntegerTMinMaxInRange(r, nint.MinValue, nint.MaxValue); + AssertNextIntegerTMinMaxInRange(r, Int128.MinValue, Int128.MaxValue); + + // Ranges that cross zero with large span. + AssertNextIntegerTMinMaxInRange(r, int.MinValue, 0); + AssertNextIntegerTMinMaxInRange(r, -1, int.MaxValue); + AssertNextIntegerTMinMaxInRange(r, long.MinValue, 0L); + AssertNextIntegerTMinMaxInRange(r, -1L, long.MaxValue); + } + + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public void NextIntegerT_LargeUnsignedValues(bool derived, bool seeded) + { + Random r = Create(derived, seeded); + + // ulong values beyond long.MaxValue. + for (int i = 0; i < 100; i++) + { + ulong value = r.NextInteger(ulong.MaxValue); + Assert.True(value < ulong.MaxValue); + } + + // Full uint range. + AssertNextIntegerTMaxValueInRange(r, uint.MaxValue); + + // UInt128 large range. + AssertNextIntegerTMinMaxInRange(r, (UInt128)0, UInt128.MaxValue); + + // nuint on the current platform. + AssertNextIntegerTMaxValueInRange(r, nuint.MaxValue); + + // Int128/UInt128 values that exceed ulong.MaxValue - these must bypass the + // ulong fast path and use rejection sampling. + UInt128 largeUInt128Max = ((UInt128)ulong.MaxValue << 1) + 5; + AssertNextIntegerTMaxValueInRange(r, largeUInt128Max); + + Int128 largeInt128Max = (Int128)ulong.MaxValue + 100; + AssertNextIntegerTMaxValueInRange(r, largeInt128Max); + + // UInt128 maxExclusive = 2^64 (exactly one more than ulong.MaxValue) + // This previously truncated to 0 via ulong.CreateTruncating. + UInt128 twoTo64 = (UInt128)ulong.MaxValue + 1; + for (int i = 0; i < 100; i++) + { + UInt128 value = r.NextInteger(twoTo64); + Assert.True(value < twoTo64, $"NextInteger({twoTo64}) returned {value}"); + } + + // Int128 maxExclusive that truncates to a small value when cast to ulong + Int128 tricky = ((Int128)1 << 64) + 5; + for (int i = 0; i < 100; i++) + { + Int128 value = r.NextInteger(tricky); + Assert.True(value >= Int128.Zero && value < tricky, $"NextInteger({tricky}) returned {value}"); + } + } + + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public void NextIntegerT_BigInteger_InRange(bool derived, bool seeded) + { + Random r = Create(derived, seeded); + + AssertNextIntegerTMaxValueInRange(r, new BigInteger(1_000)); + AssertNextIntegerTMinMaxInRange(r, new BigInteger(-1_000), new BigInteger(1_000)); + + BigInteger largeMax = BigInteger.One << 3_000; + AssertNextIntegerTMaxValueInRange(r, largeMax, iterations: 10); + AssertNextIntegerTMinMaxInRange(r, -largeMax, largeMax, iterations: 10); + } + + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public void NextIntegerT_NegativeRanges(bool derived, bool seeded) + { + Random r = Create(derived, seeded); + AssertNextIntegerTMinMaxInRange(r, (sbyte)-100, (sbyte)-10); + AssertNextIntegerTMinMaxInRange(r, (short)-1000, (short)-1); + AssertNextIntegerTMinMaxInRange(r, -1_000_000, -1); + AssertNextIntegerTMinMaxInRange(r, -1_000_000_000L, -1L); + AssertNextIntegerTMinMaxInRange(r, (Int128)(-1000), (Int128)(-1)); + } + + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public void NextBinaryFloatT_AllTypes_InRange(bool derived, bool seeded) + { + Random r = Create(derived, seeded); + AssertNextBinaryFloatInRange(r); + AssertNextBinaryFloatInRange(r); + AssertNextBinaryFloatInRange(r); + AssertNextBinaryFloatInRange(r); + AssertNextBinaryFloatInRange(r); + + static void AssertNextBinaryFloatInRange(Random r) where T : IBinaryFloatingPointIeee754 + { + for (int i = 0; i < 1000; i++) + { + T value = r.NextBinaryFloat(); + Assert.True(value >= T.Zero, $"NextBinaryFloat<{typeof(T).Name}>() returned {value}, expected >= 0"); + Assert.True(value < T.One, $"NextBinaryFloat<{typeof(T).Name}>() returned {value}, expected < 1"); + } + } + } + + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public void NextBinaryFloatT_ProducesVariedValues(bool derived, bool seeded) + { + Random r = Create(derived, seeded); + AssertNextBinaryFloatVaried(r); + AssertNextBinaryFloatVaried(r); + AssertNextBinaryFloatVaried(r); + AssertNextBinaryFloatVaried(r); + AssertNextBinaryFloatVaried(r); + + static void AssertNextBinaryFloatVaried(Random r) where T : IBinaryFloatingPointIeee754 + { + HashSet seen = []; + for (int i = 0; i < 100; i++) + { + seen.Add(r.NextBinaryFloat()); + } + + Assert.True(seen.Count > 50, $"NextBinaryFloat<{typeof(T).Name}>() produced only {seen.Count} distinct values in 100 calls"); + } + } + + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public void NextIntegerT_CustomReferenceType(bool derived, bool seeded) + { + Random r = Create(derived, seeded); + + AssertNextIntegerTMaxValueInRange>(r, 10); + AssertNextIntegerTMinMaxInRange>(r, -5, 5); + } + + [Fact] + public void NextIntegerT_CustomReferenceType_ParameterlessCanReturnMaxValue() + { + Random r = new MaxValueRandom(); + + Assert.Equal(BinaryIntegerReference.MaxValue, r.NextInteger>()); + } + + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public void NextBinaryFloatT_CustomReferenceType(bool derived, bool seeded) + { + Random r = Create(derived, seeded); + HashSet> seen = []; + string typeName = nameof(BinaryFloatingPointIeee754Reference); + + for (int i = 0; i < 100; i++) + { + BinaryFloatingPointIeee754Reference value = r.NextBinaryFloat>(); + Assert.True(value >= BinaryFloatingPointIeee754Reference.Zero, $"NextBinaryFloat<{typeName}>() returned {value}, expected >= 0"); + Assert.True(value < BinaryFloatingPointIeee754Reference.One, $"NextBinaryFloat<{typeName}>() returned {value}, expected < 1"); + seen.Add(value); + } + + Assert.True(seen.Count > 50, $"NextBinaryFloat<{typeName}>() produced only {seen.Count} distinct values in 100 calls"); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void NextIntegerT_DerivedType_DispatchesThroughVirtuals(bool seeded) + { + // NextInteger() routes through Next(int), NextInt64(long), or NextBytes(Span), + // all of which are virtual. For a derived Random, these must reach the derived + // type's overrides so that subclass behavior (e.g. custom RNG) is preserved. + // SubRandom.Next() sets NextCalled; the compat path routes Next(int)/NextInt64/ + // NextSingle/NextDouble through Sample(), which sets SampleCalled. + + SubRandom r; + + // Integer types that hit Next(int) -> Sample() + r = seeded ? new SubRandom(42) : new SubRandom(); + r.NextInteger(42); + Assert.True(r.SampleCalled, "NextInteger should dispatch through Sample on derived type"); + + // Integer types that hit NextInt64(long) -> Sample() + r = seeded ? new SubRandom(42) : new SubRandom(); + r.NextInteger(42L); + Assert.True(r.SampleCalled, "NextInteger should dispatch through Sample on derived type"); + + // Large types that hit NextBytes -> Next() -> NextCalled + r = seeded ? new SubRandom(42) : new SubRandom(); + r.NextInteger(UInt128.MaxValue); + Assert.True(r.NextCalled, "NextInteger should dispatch through Next on derived type"); + + // NextBinaryFloat -> NextSingle() -> Sample() + r = seeded ? new SubRandom(42) : new SubRandom(); + r.NextBinaryFloat(); + Assert.True(r.SampleCalled, "NextBinaryFloat should dispatch through Sample on derived type"); + + // NextBinaryFloat -> NextDouble() -> Sample() + r = seeded ? new SubRandom(42) : new SubRandom(); + r.NextBinaryFloat(); + Assert.True(r.SampleCalled, "NextBinaryFloat should dispatch through Sample on derived type"); + + // NextBinaryFloat -> NextInt64() -> Sample() + r = seeded ? new SubRandom(42) : new SubRandom(); + r.NextBinaryFloat(); + Assert.True(r.SampleCalled, "NextBinaryFloat should dispatch through Sample on derived type"); + } + + private static void AssertNextIntegerTMaxValueInRange(Random r, T maxExclusive, int iterations = 100) where T : IBinaryInteger + { + for (int i = 0; i < iterations; i++) + { + T value = r.NextInteger(maxExclusive); + Assert.True(!T.IsNegative(value), $"NextInteger<{typeof(T).Name}>({maxExclusive}) returned negative: {value}"); + Assert.True(value < maxExclusive, $"NextInteger<{typeof(T).Name}>({maxExclusive}) returned {value}, expected < {maxExclusive}"); + } + } + + private static void AssertNextIntegerTMinMaxInRange(Random r, T min, T max, int iterations = 100) where T : IBinaryInteger + { + for (int i = 0; i < iterations; i++) + { + T value = r.NextInteger(min, max); + Assert.True(value >= min, $"NextInteger<{typeof(T).Name}>({min}, {max}) returned {value}, expected >= {min}"); + Assert.True(value < max, $"NextInteger<{typeof(T).Name}>({min}, {max}) returned {value}, expected < {max}"); + } + } + private static Random Create(bool derived, bool seeded) => (derived, seeded) switch { @@ -1027,5 +1546,443 @@ public override int Next() return base.Next(); } } + + private sealed class BinaryIntegerReference : IBinaryInteger>, IMinMaxValue> + where T : IBinaryInteger, IMinMaxValue + { + public BinaryIntegerReference(T value) => Value = value; + + public T Value { get; } + + public static implicit operator BinaryIntegerReference(T value) => new(value); + public static implicit operator T(BinaryIntegerReference value) => value.Value; + + public static BinaryIntegerReference AdditiveIdentity => T.AdditiveIdentity; + public static BinaryIntegerReference MaxValue => T.MaxValue; + public static BinaryIntegerReference MinValue => T.MinValue; + public static BinaryIntegerReference MultiplicativeIdentity => T.MultiplicativeIdentity; + public static BinaryIntegerReference One => T.One; + public static int Radix => T.Radix; + public static BinaryIntegerReference Zero => T.Zero; + + public static BinaryIntegerReference Abs(BinaryIntegerReference value) => T.Abs(value); + public static bool IsCanonical(BinaryIntegerReference value) => T.IsCanonical(value); + public static bool IsComplexNumber(BinaryIntegerReference value) => T.IsComplexNumber(value); + public static bool IsEvenInteger(BinaryIntegerReference value) => T.IsEvenInteger(value); + public static bool IsFinite(BinaryIntegerReference value) => T.IsFinite(value); + public static bool IsImaginaryNumber(BinaryIntegerReference value) => T.IsImaginaryNumber(value); + public static bool IsInfinity(BinaryIntegerReference value) => T.IsInfinity(value); + public static bool IsInteger(BinaryIntegerReference value) => T.IsInteger(value); + public static bool IsNaN(BinaryIntegerReference value) => T.IsNaN(value); + public static bool IsNegative(BinaryIntegerReference value) => T.IsNegative(value); + public static bool IsNegativeInfinity(BinaryIntegerReference value) => T.IsNegativeInfinity(value); + public static bool IsNormal(BinaryIntegerReference value) => T.IsNormal(value); + public static bool IsOddInteger(BinaryIntegerReference value) => T.IsOddInteger(value); + public static bool IsPositive(BinaryIntegerReference value) => T.IsPositive(value); + public static bool IsPositiveInfinity(BinaryIntegerReference value) => T.IsPositiveInfinity(value); + public static bool IsPow2(BinaryIntegerReference value) => T.IsPow2(value); + public static bool IsRealNumber(BinaryIntegerReference value) => T.IsRealNumber(value); + public static bool IsSubnormal(BinaryIntegerReference value) => T.IsSubnormal(value); + public static bool IsZero(BinaryIntegerReference value) => T.IsZero(value); + public static BinaryIntegerReference Log2(BinaryIntegerReference value) => T.Log2(value); + public static BinaryIntegerReference MaxMagnitude(BinaryIntegerReference x, BinaryIntegerReference y) => T.MaxMagnitude(x, y); + public static BinaryIntegerReference MaxMagnitudeNumber(BinaryIntegerReference x, BinaryIntegerReference y) => T.MaxMagnitudeNumber(x, y); + public static BinaryIntegerReference MinMagnitude(BinaryIntegerReference x, BinaryIntegerReference y) => T.MinMagnitude(x, y); + public static BinaryIntegerReference MinMagnitudeNumber(BinaryIntegerReference x, BinaryIntegerReference y) => T.MinMagnitudeNumber(x, y); + public static BinaryIntegerReference Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) => T.Parse(s, style, provider); + public static BinaryIntegerReference Parse(string s, NumberStyles style, IFormatProvider? provider) => T.Parse(s, style, provider); + public static BinaryIntegerReference Parse(ReadOnlySpan s, IFormatProvider? provider) => T.Parse(s, provider); + public static BinaryIntegerReference Parse(string s, IFormatProvider? provider) => T.Parse(s, provider); + public static BinaryIntegerReference PopCount(BinaryIntegerReference value) => T.PopCount(value); + public static BinaryIntegerReference TrailingZeroCount(BinaryIntegerReference value) => T.TrailingZeroCount(value); + + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out BinaryIntegerReference result) + { + bool succeeded = T.TryParse(s, style, provider, out T actualResult); + result = actualResult; + return succeeded; + } + + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out BinaryIntegerReference result) + { + bool succeeded = T.TryParse(s, style, provider, out T actualResult); + result = actualResult; + return succeeded; + } + + public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, [MaybeNullWhen(false)] out BinaryIntegerReference result) + { + bool succeeded = T.TryParse(s, provider, out T actualResult); + result = actualResult; + return succeeded; + } + + public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out BinaryIntegerReference result) + { + bool succeeded = T.TryParse(s, provider, out T actualResult); + result = actualResult; + return succeeded; + } + + public static bool TryReadBigEndian(ReadOnlySpan source, bool isUnsigned, out BinaryIntegerReference value) + { + bool succeeded = T.TryReadBigEndian(source, isUnsigned, out T actualValue); + value = actualValue; + return succeeded; + } + + public static bool TryReadLittleEndian(ReadOnlySpan source, bool isUnsigned, out BinaryIntegerReference value) + { + bool succeeded = T.TryReadLittleEndian(source, isUnsigned, out T actualValue); + value = actualValue; + return succeeded; + } + + public int CompareTo(object? obj) + { + if (obj is not BinaryIntegerReference other) + { + return obj is null ? 1 : throw new ArgumentException(); + } + + return CompareTo(other); + } + + public int CompareTo(BinaryIntegerReference? other) => other is null ? 1 : Value.CompareTo(other.Value); + public override bool Equals([NotNullWhen(true)] object? obj) => obj is BinaryIntegerReference other && Equals(other); + public bool Equals(BinaryIntegerReference? other) => other is not null && Value.Equals(other.Value); + public int GetByteCount() => Value.GetByteCount(); + public override int GetHashCode() => Value.GetHashCode(); + public int GetShortestBitLength() => Value.GetShortestBitLength(); + public override string ToString() => Value.ToString()!; + public string ToString(string? format, IFormatProvider? formatProvider) => Value.ToString(format, formatProvider); + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) => Value.TryFormat(destination, out charsWritten, format, provider); + public bool TryWriteBigEndian(Span destination, out int bytesWritten) => Value.TryWriteBigEndian(destination, out bytesWritten); + public bool TryWriteLittleEndian(Span destination, out int bytesWritten) => Value.TryWriteLittleEndian(destination, out bytesWritten); + + static bool INumberBase>.TryConvertFromChecked(TOther value, out BinaryIntegerReference result) + { + if (typeof(TOther) == typeof(T)) + { + result = (T)(object)value; + return true; + } + + bool succeeded = T.TryConvertFromChecked(value, out T actualResult); + + if (!succeeded) + { + succeeded = TOther.TryConvertToChecked(value, out actualResult); + } + + result = actualResult; + return succeeded; + } + + static bool INumberBase>.TryConvertFromSaturating(TOther value, out BinaryIntegerReference result) + { + if (typeof(TOther) == typeof(T)) + { + result = (T)(object)value; + return true; + } + + bool succeeded = T.TryConvertFromSaturating(value, out T actualResult); + + if (!succeeded) + { + succeeded = TOther.TryConvertToSaturating(value, out actualResult); + } + + result = actualResult; + return succeeded; + } + + static bool INumberBase>.TryConvertFromTruncating(TOther value, out BinaryIntegerReference result) + { + if (typeof(TOther) == typeof(T)) + { + result = (T)(object)value; + return true; + } + + bool succeeded = T.TryConvertFromTruncating(value, out T actualResult); + + if (!succeeded) + { + succeeded = TOther.TryConvertToTruncating(value, out actualResult); + } + + result = actualResult; + return succeeded; + } + + static bool INumberBase>.TryConvertToChecked(BinaryIntegerReference value, out TOther result) => T.TryConvertToChecked(value.Value, out result); + static bool INumberBase>.TryConvertToSaturating(BinaryIntegerReference value, out TOther result) => T.TryConvertToSaturating(value.Value, out result); + static bool INumberBase>.TryConvertToTruncating(BinaryIntegerReference value, out TOther result) => T.TryConvertToTruncating(value.Value, out result); + + public static BinaryIntegerReference operator +(BinaryIntegerReference value) => +value.Value; + public static BinaryIntegerReference operator +(BinaryIntegerReference left, BinaryIntegerReference right) => left.Value + right.Value; + public static BinaryIntegerReference operator -(BinaryIntegerReference value) => -value.Value; + public static BinaryIntegerReference operator -(BinaryIntegerReference left, BinaryIntegerReference right) => left.Value - right.Value; + public static BinaryIntegerReference operator ~(BinaryIntegerReference value) => ~value.Value; + public static BinaryIntegerReference operator ++(BinaryIntegerReference value) => value.Value + T.One; + public static BinaryIntegerReference operator --(BinaryIntegerReference value) => value.Value - T.One; + public static BinaryIntegerReference operator *(BinaryIntegerReference left, BinaryIntegerReference right) => left.Value * right.Value; + public static BinaryIntegerReference operator /(BinaryIntegerReference left, BinaryIntegerReference right) => left.Value / right.Value; + public static BinaryIntegerReference operator %(BinaryIntegerReference left, BinaryIntegerReference right) => left.Value % right.Value; + public static BinaryIntegerReference operator &(BinaryIntegerReference left, BinaryIntegerReference right) => left.Value & right.Value; + public static BinaryIntegerReference operator |(BinaryIntegerReference left, BinaryIntegerReference right) => left.Value | right.Value; + public static BinaryIntegerReference operator ^(BinaryIntegerReference left, BinaryIntegerReference right) => left.Value ^ right.Value; + public static BinaryIntegerReference operator <<(BinaryIntegerReference value, int shiftAmount) => value.Value << shiftAmount; + public static BinaryIntegerReference operator >>(BinaryIntegerReference value, int shiftAmount) => value.Value >> shiftAmount; + public static bool operator ==(BinaryIntegerReference? left, BinaryIntegerReference? right) => left is null ? right is null : left.Equals(right); + public static bool operator !=(BinaryIntegerReference? left, BinaryIntegerReference? right) => !(left == right); + public static bool operator <(BinaryIntegerReference left, BinaryIntegerReference right) => left.Value < right.Value; + public static bool operator >(BinaryIntegerReference left, BinaryIntegerReference right) => left.Value > right.Value; + public static bool operator <=(BinaryIntegerReference left, BinaryIntegerReference right) => left.Value <= right.Value; + public static bool operator >=(BinaryIntegerReference left, BinaryIntegerReference right) => left.Value >= right.Value; + public static BinaryIntegerReference operator >>>(BinaryIntegerReference value, int shiftAmount) => value.Value >>> shiftAmount; + } + + private sealed class BinaryFloatingPointIeee754Reference : IBinaryFloatingPointIeee754> + where T : IBinaryFloatingPointIeee754 + { + public BinaryFloatingPointIeee754Reference(T value) => Value = value; + + public T Value { get; } + + public static implicit operator BinaryFloatingPointIeee754Reference(T value) => new(value); + public static implicit operator T(BinaryFloatingPointIeee754Reference value) => value.Value; + + public static BinaryFloatingPointIeee754Reference AdditiveIdentity => T.AdditiveIdentity; + public static BinaryFloatingPointIeee754Reference E => T.E; + public static BinaryFloatingPointIeee754Reference Epsilon => T.Epsilon; + public static BinaryFloatingPointIeee754Reference MultiplicativeIdentity => T.MultiplicativeIdentity; + public static BinaryFloatingPointIeee754Reference NaN => T.NaN; + public static BinaryFloatingPointIeee754Reference NegativeInfinity => T.NegativeInfinity; + public static BinaryFloatingPointIeee754Reference NegativeOne => T.NegativeOne; + public static BinaryFloatingPointIeee754Reference NegativeZero => T.NegativeZero; + public static BinaryFloatingPointIeee754Reference One => T.One; + public static BinaryFloatingPointIeee754Reference Pi => T.Pi; + public static BinaryFloatingPointIeee754Reference PositiveInfinity => T.PositiveInfinity; + public static int Radix => T.Radix; + public static BinaryFloatingPointIeee754Reference Tau => T.Tau; + public static BinaryFloatingPointIeee754Reference Zero => T.Zero; + + public static BinaryFloatingPointIeee754Reference Abs(BinaryFloatingPointIeee754Reference value) => T.Abs(value); + public static BinaryFloatingPointIeee754Reference Acos(BinaryFloatingPointIeee754Reference x) => T.Acos(x); + public static BinaryFloatingPointIeee754Reference Acosh(BinaryFloatingPointIeee754Reference x) => T.Acosh(x); + public static BinaryFloatingPointIeee754Reference AcosPi(BinaryFloatingPointIeee754Reference x) => T.AcosPi(x); + public static BinaryFloatingPointIeee754Reference Asin(BinaryFloatingPointIeee754Reference x) => T.Asin(x); + public static BinaryFloatingPointIeee754Reference Asinh(BinaryFloatingPointIeee754Reference x) => T.Asinh(x); + public static BinaryFloatingPointIeee754Reference AsinPi(BinaryFloatingPointIeee754Reference x) => T.AsinPi(x); + public static BinaryFloatingPointIeee754Reference Atan(BinaryFloatingPointIeee754Reference x) => T.Atan(x); + public static BinaryFloatingPointIeee754Reference Atan2(BinaryFloatingPointIeee754Reference y, BinaryFloatingPointIeee754Reference x) => T.Atan2(y, x); + public static BinaryFloatingPointIeee754Reference Atan2Pi(BinaryFloatingPointIeee754Reference y, BinaryFloatingPointIeee754Reference x) => T.Atan2Pi(y, x); + public static BinaryFloatingPointIeee754Reference Atanh(BinaryFloatingPointIeee754Reference x) => T.Atanh(x); + public static BinaryFloatingPointIeee754Reference AtanPi(BinaryFloatingPointIeee754Reference x) => T.AtanPi(x); + public static BinaryFloatingPointIeee754Reference BitDecrement(BinaryFloatingPointIeee754Reference x) => T.BitDecrement(x); + public static BinaryFloatingPointIeee754Reference BitIncrement(BinaryFloatingPointIeee754Reference x) => T.BitIncrement(x); + public static BinaryFloatingPointIeee754Reference Cbrt(BinaryFloatingPointIeee754Reference x) => T.Cbrt(x); + public static BinaryFloatingPointIeee754Reference Cos(BinaryFloatingPointIeee754Reference x) => T.Cos(x); + public static BinaryFloatingPointIeee754Reference Cosh(BinaryFloatingPointIeee754Reference x) => T.Cosh(x); + public static BinaryFloatingPointIeee754Reference CosPi(BinaryFloatingPointIeee754Reference x) => T.CosPi(x); + public static BinaryFloatingPointIeee754Reference Exp(BinaryFloatingPointIeee754Reference x) => T.Exp(x); + public static BinaryFloatingPointIeee754Reference Exp2(BinaryFloatingPointIeee754Reference x) => T.Exp2(x); + public static BinaryFloatingPointIeee754Reference Exp10(BinaryFloatingPointIeee754Reference x) => T.Exp10(x); + public static BinaryFloatingPointIeee754Reference FusedMultiplyAdd(BinaryFloatingPointIeee754Reference left, BinaryFloatingPointIeee754Reference right, BinaryFloatingPointIeee754Reference addend) => T.FusedMultiplyAdd(left, right, addend); + public static BinaryFloatingPointIeee754Reference Hypot(BinaryFloatingPointIeee754Reference x, BinaryFloatingPointIeee754Reference y) => T.Hypot(x, y); + public static BinaryFloatingPointIeee754Reference Ieee754Remainder(BinaryFloatingPointIeee754Reference left, BinaryFloatingPointIeee754Reference right) => T.Ieee754Remainder(left, right); + public static int ILogB(BinaryFloatingPointIeee754Reference x) => T.ILogB(x); + public static bool IsCanonical(BinaryFloatingPointIeee754Reference value) => T.IsCanonical(value); + public static bool IsComplexNumber(BinaryFloatingPointIeee754Reference value) => T.IsComplexNumber(value); + public static bool IsEvenInteger(BinaryFloatingPointIeee754Reference value) => T.IsEvenInteger(value); + public static bool IsFinite(BinaryFloatingPointIeee754Reference value) => T.IsFinite(value); + public static bool IsImaginaryNumber(BinaryFloatingPointIeee754Reference value) => T.IsImaginaryNumber(value); + public static bool IsInfinity(BinaryFloatingPointIeee754Reference value) => T.IsInfinity(value); + public static bool IsInteger(BinaryFloatingPointIeee754Reference value) => T.IsInteger(value); + public static bool IsNaN(BinaryFloatingPointIeee754Reference value) => T.IsNaN(value); + public static bool IsNegative(BinaryFloatingPointIeee754Reference value) => T.IsNegative(value); + public static bool IsNegativeInfinity(BinaryFloatingPointIeee754Reference value) => T.IsNegativeInfinity(value); + public static bool IsNormal(BinaryFloatingPointIeee754Reference value) => T.IsNormal(value); + public static bool IsOddInteger(BinaryFloatingPointIeee754Reference value) => T.IsOddInteger(value); + public static bool IsPositive(BinaryFloatingPointIeee754Reference value) => T.IsPositive(value); + public static bool IsPositiveInfinity(BinaryFloatingPointIeee754Reference value) => T.IsPositiveInfinity(value); + public static bool IsPow2(BinaryFloatingPointIeee754Reference value) => T.IsPow2(value); + public static bool IsRealNumber(BinaryFloatingPointIeee754Reference value) => T.IsRealNumber(value); + public static bool IsSubnormal(BinaryFloatingPointIeee754Reference value) => T.IsSubnormal(value); + public static bool IsZero(BinaryFloatingPointIeee754Reference value) => T.IsZero(value); + public static BinaryFloatingPointIeee754Reference Log(BinaryFloatingPointIeee754Reference x) => T.Log(x); + public static BinaryFloatingPointIeee754Reference Log(BinaryFloatingPointIeee754Reference x, BinaryFloatingPointIeee754Reference newBase) => T.Log(x, newBase); + public static BinaryFloatingPointIeee754Reference Log2(BinaryFloatingPointIeee754Reference x) => BinaryLog2(x.Value); + public static BinaryFloatingPointIeee754Reference Log10(BinaryFloatingPointIeee754Reference x) => T.Log10(x); + public static BinaryFloatingPointIeee754Reference MaxMagnitude(BinaryFloatingPointIeee754Reference x, BinaryFloatingPointIeee754Reference y) => T.MaxMagnitude(x, y); + public static BinaryFloatingPointIeee754Reference MaxMagnitudeNumber(BinaryFloatingPointIeee754Reference x, BinaryFloatingPointIeee754Reference y) => T.MaxMagnitudeNumber(x, y); + public static BinaryFloatingPointIeee754Reference MinMagnitude(BinaryFloatingPointIeee754Reference x, BinaryFloatingPointIeee754Reference y) => T.MinMagnitude(x, y); + public static BinaryFloatingPointIeee754Reference MinMagnitudeNumber(BinaryFloatingPointIeee754Reference x, BinaryFloatingPointIeee754Reference y) => T.MinMagnitudeNumber(x, y); + public static BinaryFloatingPointIeee754Reference Pow(BinaryFloatingPointIeee754Reference x, BinaryFloatingPointIeee754Reference y) => T.Pow(x, y); + public static BinaryFloatingPointIeee754Reference Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) => T.Parse(s, style, provider); + public static BinaryFloatingPointIeee754Reference Parse(string s, NumberStyles style, IFormatProvider? provider) => T.Parse(s, style, provider); + public static BinaryFloatingPointIeee754Reference Parse(ReadOnlySpan s, IFormatProvider? provider) => T.Parse(s, provider); + public static BinaryFloatingPointIeee754Reference Parse(string s, IFormatProvider? provider) => T.Parse(s, provider); + public static BinaryFloatingPointIeee754Reference RootN(BinaryFloatingPointIeee754Reference x, int n) => T.RootN(x, n); + public static BinaryFloatingPointIeee754Reference Round(BinaryFloatingPointIeee754Reference x, int digits, MidpointRounding mode) => T.Round(x, digits, mode); + public static BinaryFloatingPointIeee754Reference ScaleB(BinaryFloatingPointIeee754Reference x, int n) => T.ScaleB(x, n); + public static BinaryFloatingPointIeee754Reference Sin(BinaryFloatingPointIeee754Reference x) => T.Sin(x); + public static (BinaryFloatingPointIeee754Reference Sin, BinaryFloatingPointIeee754Reference Cos) SinCos(BinaryFloatingPointIeee754Reference x) => T.SinCos(x); + public static (BinaryFloatingPointIeee754Reference SinPi, BinaryFloatingPointIeee754Reference CosPi) SinCosPi(BinaryFloatingPointIeee754Reference x) => T.SinCosPi(x); + public static BinaryFloatingPointIeee754Reference Sinh(BinaryFloatingPointIeee754Reference x) => T.Sinh(x); + public static BinaryFloatingPointIeee754Reference SinPi(BinaryFloatingPointIeee754Reference x) => T.SinPi(x); + public static BinaryFloatingPointIeee754Reference Sqrt(BinaryFloatingPointIeee754Reference x) => T.Sqrt(x); + public static BinaryFloatingPointIeee754Reference Tan(BinaryFloatingPointIeee754Reference x) => T.Tan(x); + public static BinaryFloatingPointIeee754Reference Tanh(BinaryFloatingPointIeee754Reference x) => T.Tanh(x); + public static BinaryFloatingPointIeee754Reference TanPi(BinaryFloatingPointIeee754Reference x) => T.TanPi(x); + + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out BinaryFloatingPointIeee754Reference result) + { + bool succeeded = T.TryParse(s, style, provider, out T actualResult); + result = actualResult; + return succeeded; + } + + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out BinaryFloatingPointIeee754Reference result) + { + bool succeeded = T.TryParse(s, style, provider, out T actualResult); + result = actualResult; + return succeeded; + } + + public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, [MaybeNullWhen(false)] out BinaryFloatingPointIeee754Reference result) + { + bool succeeded = T.TryParse(s, provider, out T actualResult); + result = actualResult; + return succeeded; + } + + public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out BinaryFloatingPointIeee754Reference result) + { + bool succeeded = T.TryParse(s, provider, out T actualResult); + result = actualResult; + return succeeded; + } + + public int CompareTo(object? obj) + { + if (obj is not BinaryFloatingPointIeee754Reference other) + { + return obj is null ? 1 : throw new ArgumentException(); + } + + return CompareTo(other); + } + + public int CompareTo(BinaryFloatingPointIeee754Reference? other) => other is null ? 1 : Value.CompareTo(other.Value); + public override bool Equals([NotNullWhen(true)] object? obj) => obj is BinaryFloatingPointIeee754Reference other && Equals(other); + public bool Equals(BinaryFloatingPointIeee754Reference? other) => other is not null && Value.Equals(other.Value); + public int GetExponentByteCount() => Value.GetExponentByteCount(); + public int GetExponentShortestBitLength() => Value.GetExponentShortestBitLength(); + public override int GetHashCode() => Value.GetHashCode(); + public int GetSignificandBitLength() => Value.GetSignificandBitLength(); + public int GetSignificandByteCount() => Value.GetSignificandByteCount(); + public override string ToString() => Value.ToString()!; + public string ToString(string? format, IFormatProvider? formatProvider) => Value.ToString(format, formatProvider); + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) => Value.TryFormat(destination, out charsWritten, format, provider); + public bool TryWriteExponentBigEndian(Span destination, out int bytesWritten) => Value.TryWriteExponentBigEndian(destination, out bytesWritten); + public bool TryWriteExponentLittleEndian(Span destination, out int bytesWritten) => Value.TryWriteExponentLittleEndian(destination, out bytesWritten); + public bool TryWriteSignificandBigEndian(Span destination, out int bytesWritten) => Value.TryWriteSignificandBigEndian(destination, out bytesWritten); + public bool TryWriteSignificandLittleEndian(Span destination, out int bytesWritten) => Value.TryWriteSignificandLittleEndian(destination, out bytesWritten); + + static bool INumberBase>.TryConvertFromChecked(TOther value, out BinaryFloatingPointIeee754Reference result) + { + if (typeof(TOther) == typeof(T)) + { + result = (T)(object)value; + return true; + } + + bool succeeded = T.TryConvertFromChecked(value, out T actualResult); + + if (!succeeded) + { + succeeded = TOther.TryConvertToChecked(value, out actualResult); + } + + result = actualResult; + return succeeded; + } + + static bool INumberBase>.TryConvertFromSaturating(TOther value, out BinaryFloatingPointIeee754Reference result) + { + if (typeof(TOther) == typeof(T)) + { + result = (T)(object)value; + return true; + } + + bool succeeded = T.TryConvertFromSaturating(value, out T actualResult); + + if (!succeeded) + { + succeeded = TOther.TryConvertToSaturating(value, out actualResult); + } + + result = actualResult; + return succeeded; + } + + static bool INumberBase>.TryConvertFromTruncating(TOther value, out BinaryFloatingPointIeee754Reference result) + { + if (typeof(TOther) == typeof(T)) + { + result = (T)(object)value; + return true; + } + + bool succeeded = T.TryConvertFromTruncating(value, out T actualResult); + + if (!succeeded) + { + succeeded = TOther.TryConvertToTruncating(value, out actualResult); + } + + result = actualResult; + return succeeded; + } + + static bool INumberBase>.TryConvertToChecked(BinaryFloatingPointIeee754Reference value, out TOther result) => T.TryConvertToChecked(value.Value, out result); + static bool INumberBase>.TryConvertToSaturating(BinaryFloatingPointIeee754Reference value, out TOther result) => T.TryConvertToSaturating(value.Value, out result); + static bool INumberBase>.TryConvertToTruncating(BinaryFloatingPointIeee754Reference value, out TOther result) => T.TryConvertToTruncating(value.Value, out result); + + public static BinaryFloatingPointIeee754Reference operator +(BinaryFloatingPointIeee754Reference value) => +value.Value; + public static BinaryFloatingPointIeee754Reference operator +(BinaryFloatingPointIeee754Reference left, BinaryFloatingPointIeee754Reference right) => left.Value + right.Value; + public static BinaryFloatingPointIeee754Reference operator -(BinaryFloatingPointIeee754Reference value) => -value.Value; + public static BinaryFloatingPointIeee754Reference operator -(BinaryFloatingPointIeee754Reference left, BinaryFloatingPointIeee754Reference right) => left.Value - right.Value; + public static BinaryFloatingPointIeee754Reference operator ~(BinaryFloatingPointIeee754Reference value) => ~value.Value; + public static BinaryFloatingPointIeee754Reference operator ++(BinaryFloatingPointIeee754Reference value) => value.Value + T.One; + public static BinaryFloatingPointIeee754Reference operator --(BinaryFloatingPointIeee754Reference value) => value.Value - T.One; + public static BinaryFloatingPointIeee754Reference operator *(BinaryFloatingPointIeee754Reference left, BinaryFloatingPointIeee754Reference right) => left.Value * right.Value; + public static BinaryFloatingPointIeee754Reference operator /(BinaryFloatingPointIeee754Reference left, BinaryFloatingPointIeee754Reference right) => left.Value / right.Value; + public static BinaryFloatingPointIeee754Reference operator %(BinaryFloatingPointIeee754Reference left, BinaryFloatingPointIeee754Reference right) => left.Value % right.Value; + public static BinaryFloatingPointIeee754Reference operator &(BinaryFloatingPointIeee754Reference left, BinaryFloatingPointIeee754Reference right) => left.Value & right.Value; + public static BinaryFloatingPointIeee754Reference operator |(BinaryFloatingPointIeee754Reference left, BinaryFloatingPointIeee754Reference right) => left.Value | right.Value; + public static BinaryFloatingPointIeee754Reference operator ^(BinaryFloatingPointIeee754Reference left, BinaryFloatingPointIeee754Reference right) => left.Value ^ right.Value; + public static bool operator ==(BinaryFloatingPointIeee754Reference? left, BinaryFloatingPointIeee754Reference? right) => left is null ? right is null : left.Equals(right); + public static bool operator !=(BinaryFloatingPointIeee754Reference? left, BinaryFloatingPointIeee754Reference? right) => !(left == right); + public static bool operator <(BinaryFloatingPointIeee754Reference left, BinaryFloatingPointIeee754Reference right) => left.Value < right.Value; + public static bool operator >(BinaryFloatingPointIeee754Reference left, BinaryFloatingPointIeee754Reference right) => left.Value > right.Value; + public static bool operator <=(BinaryFloatingPointIeee754Reference left, BinaryFloatingPointIeee754Reference right) => left.Value <= right.Value; + public static bool operator >=(BinaryFloatingPointIeee754Reference left, BinaryFloatingPointIeee754Reference right) => left.Value >= right.Value; + + private static TNumber BinaryLog2(TNumber value) where TNumber : IBinaryNumber => TNumber.Log2(value); + } + + private sealed class MaxValueRandom : Random + { + public override void NextBytes(Span buffer) + { + buffer.Fill(byte.MaxValue); + } + } } }