# HG changeset patch
# User Michael Johnson
# Date 1605989384 21600
# Sat Nov 21 14:09:44 2020 0600
# Branch genumerics
# Node ID 4362a433c3564cf4eb640541ad4fdc785d94061a
# Parent 54883176dc196e908f388413722d4df0a1324ed8
Prototype using Genumerics library for Uniform distributions
Genumerics looks like it could be a very useful library, but it's not in a
usable state for us right now. Primarily, it doesn't have a way of specifying
whether to use checked or unchecked operations. Right now, we often want
unchecked, but for some operations (for example, Number.Convert<,>), it isn't
possible to use without throwing an exception. Another issue is that the
performance vs direct UniformInt32 leaves a lot to be desired.
diff git a/src/Benchmarks/UniformDists.cs b/src/Benchmarks/UniformDists.cs
 a/src/Benchmarks/UniformDists.cs
+++ b/src/Benchmarks/UniformDists.cs
@@ 13,14 +13,16 @@
private readonly StepRng _rng;
 private readonly UniformSByte _uniformSByte;
 private readonly UniformInt16 _uniformInt16;
 private readonly UniformInt32 _uniformInt32;
 private readonly UniformInt64 _uniformInt64;
 private readonly UniformByte _uniformByte;
 private readonly UniformUInt16 _uniformUInt16;
 private readonly UniformUInt32 _uniformUInt32;
 private readonly UniformUInt64 _uniformUInt64;
+ private readonly UniformInt _uniformSByte;
+ private readonly UniformInt _uniformInt16;
+ private readonly UniformInt _uniformInt32;
+ private readonly UniformInt32 _uniformInt32Original;
+ private readonly UniformInt _uniformInt64;
+ private readonly UniformInt64 _uniformInt64Original;
+ private readonly UniformInt _uniformByte;
+ private readonly UniformInt _uniformUInt16;
+ private readonly UniformInt _uniformUInt32;
+ private readonly UniformInt _uniformUInt64;
private readonly UniformFloat _uniformSingle;
private readonly UniformFloat _uniformDouble;
@@ 38,7 +40,9 @@
_uniformByte = Uniform.New((Byte)LowerBound, (Byte)UpperBound);
_uniformUInt16 = Uniform.New((UInt16)LowerBound, (UInt16)UpperBound);
_uniformUInt32 = Uniform.New((UInt32)LowerBound, (UInt32)UpperBound);
+ _uniformInt32Original = UniformInt32.Create(LowerBound, UpperBound);
_uniformUInt64 = Uniform.New((UInt64)LowerBound, (UInt64)UpperBound);
+ _uniformInt64Original = UniformInt64.Create(LowerBound, UpperBound);
_uniformSingle = Uniform.New((Single)LowerBound, (Single)UpperBound);
_uniformDouble = Uniform.New((Double)LowerBound, (Double)UpperBound);
@@ 74,6 +78,15 @@
}
[Benchmark]
+ public Int32 SampleInt32Original()
+ {
+ Int32 sum = 0;
+ for (Int32 i = 0; i < Iterations; i++)
+ sum = unchecked(sum + _uniformInt32Original.Sample(_rng));
+ return sum;
+ }
+
+ [Benchmark]
public Int64 SampleInt64()
{
Int64 sum = 0;
@@ 83,6 +96,15 @@
}
[Benchmark]
+ public Int64 SampleInt64Original()
+ {
+ Int64 sum = 0;
+ for (Int32 i = 0; i < Iterations; i++)
+ sum = unchecked(sum + _uniformInt64Original.Sample(_rng));
+ return sum;
+ }
+
+ [Benchmark]
public UInt32 SampleByte()
{
UInt32 sum = 0;
diff git a/src/RandN/Distributions/Uniform.cs b/src/RandN/Distributions/Uniform.cs
 a/src/RandN/Distributions/Uniform.cs
+++ b/src/RandN/Distributions/Uniform.cs
@@ 21,174 +21,6 @@
///
/// Thrown when is greater than or equal to .
///
 public static UniformSByte New(SByte low, SByte high) => UniformSByte.Create(low, high);

 ///
 /// Creates uniform distribution in the interval [low, high], inclusive of low and high.
 ///
 /// The inclusive lower bound.
 /// The inclusive upper bound.
 ///
 /// Thrown when is greater than .
 ///
 public static UniformSByte NewInclusive(SByte low, SByte high) => UniformSByte.CreateInclusive(low, high);


 ///
 /// Creates uniform distribution in the interval [low, high), inclusive of low and exclusive of high.
 ///
 /// The inclusive lower bound.
 /// The exclusive upper bound.
 ///
 /// Thrown when is greater than or equal to .
 ///
 public static UniformInt16 New(Int16 low, Int16 high) => UniformInt16.Create(low, high);

 ///
 /// Creates uniform distribution in the interval [low, high], inclusive of low and high.
 ///
 /// The inclusive lower bound.
 /// The inclusive upper bound.
 ///
 /// Thrown when is greater than .
 ///
 public static UniformInt16 NewInclusive(Int16 low, Int16 high) => UniformInt16.CreateInclusive(low, high);


 ///
 /// Creates uniform distribution in the interval [low, high), inclusive of low and exclusive of high.
 ///
 /// The inclusive lower bound.
 /// The exclusive upper bound.
 ///
 /// Thrown when is greater than or equal to .
 ///
 public static UniformInt32 New(Int32 low, Int32 high) => UniformInt32.Create(low, high);

 ///
 /// Creates uniform distribution in the interval [low, high], inclusive of low and high.
 ///
 /// The inclusive lower bound.
 /// The inclusive upper bound.
 ///
 /// Thrown when is greater than .
 ///
 public static UniformInt32 NewInclusive(Int32 low, Int32 high) => UniformInt32.CreateInclusive(low, high);


 ///
 /// Creates uniform distribution in the interval [low, high), inclusive of low and exclusive of high.
 ///
 /// The inclusive lower bound.
 /// The exclusive upper bound.
 ///
 /// Thrown when is greater than or equal to .
 ///
 public static UniformInt64 New(Int64 low, Int64 high) => UniformInt64.Create(low, high);

 ///
 /// Creates uniform distribution in the interval [low, high], inclusive of low and high.
 ///
 /// The inclusive lower bound.
 /// The inclusive upper bound.
 ///
 /// Thrown when is greater than .
 ///
 public static UniformInt64 NewInclusive(Int64 low, Int64 high) => UniformInt64.CreateInclusive(low, high);


 ///
 /// Creates uniform distribution in the interval [low, high), inclusive of low and exclusive of high.
 ///
 /// The inclusive lower bound.
 /// The exclusive upper bound.
 ///
 /// Thrown when is greater than or equal to .
 ///
 public static UniformByte New(Byte low, Byte high) => UniformByte.Create(low, high);

 ///
 /// Creates uniform distribution in the interval [low, high], inclusive of low and high.
 ///
 /// The inclusive lower bound.
 /// The inclusive upper bound.
 ///
 /// Thrown when is greater than .
 ///
 public static UniformByte NewInclusive(Byte low, Byte high) => UniformByte.CreateInclusive(low, high);


 ///
 /// Creates uniform distribution in the interval [low, high), inclusive of low and exclusive of high.
 ///
 /// The inclusive lower bound.
 /// The exclusive upper bound.
 ///
 /// Thrown when is greater than or equal to .
 ///
 public static UniformUInt16 New(UInt16 low, UInt16 high) => UniformUInt16.Create(low, high);

 ///
 /// Creates uniform distribution in the interval [low, high], inclusive of low and high.
 ///
 /// The inclusive lower bound.
 /// The inclusive upper bound.
 ///
 /// Thrown when is greater than .
 ///
 public static UniformUInt16 NewInclusive(UInt16 low, UInt16 high) => UniformUInt16.CreateInclusive(low, high);


 ///
 /// Creates uniform distribution in the interval [low, high), inclusive of low and exclusive of high.
 ///
 /// The inclusive lower bound.
 /// The exclusive upper bound.
 ///
 /// Thrown when is greater than or equal to .
 ///
 public static UniformUInt32 New(UInt32 low, UInt32 high) => UniformUInt32.Create(low, high);

 ///
 /// Creates uniform distribution in the interval [low, high], inclusive of low and high.
 ///
 /// The inclusive lower bound.
 /// The inclusive upper bound.
 ///
 /// Thrown when is greater than .
 ///
 public static UniformUInt32 NewInclusive(UInt32 low, UInt32 high) => UniformUInt32.CreateInclusive(low, high);


 ///
 /// Creates uniform distribution in the interval [low, high), inclusive of low and exclusive of high.
 ///
 /// The inclusive lower bound.
 /// The exclusive upper bound.
 ///
 /// Thrown when is greater than or equal to .
 ///
 public static UniformUInt64 New(UInt64 low, UInt64 high) => UniformUInt64.Create(low, high);

 ///
 /// Creates uniform distribution in the interval [low, high], inclusive of low and high.
 ///
 /// The inclusive lower bound.
 /// The inclusive upper bound.
 ///
 /// Thrown when is greater than .
 ///
 public static UniformUInt64 NewInclusive(UInt64 low, UInt64 high) => UniformUInt64.CreateInclusive(low, high);


 ///
 /// Creates uniform distribution in the interval [low, high), inclusive of low and exclusive of high.
 ///
 /// The inclusive lower bound.
 /// The exclusive upper bound.
 ///
 /// Thrown when is greater than or equal to .
 ///
public static UniformTimeSpan New(TimeSpan low, TimeSpan high) => UniformTimeSpan.Create(low, high);
///
@@ 223,6 +55,167 @@
public static UniformDecimal NewInclusive(Decimal low, Decimal high) => UniformDecimal.CreateInclusive(low, high);
+
+ ///
+ /// Creates uniform distribution in the interval [low, high), inclusive of low and exclusive of high.
+ ///
+ /// The inclusive lower bound.
+ /// The exclusive upper bound.
+ ///
+ /// Thrown when is greater than or equal to .
+ ///
+ public static UniformInt New(SByte low, SByte high) => UniformInt.Create(low, high);
+
+ ///
+ /// Creates uniform distribution in the interval [low, high], inclusive of low and high.
+ ///
+ /// The inclusive lower bound.
+ /// The inclusive upper bound.
+ ///
+ /// Thrown when is greater than .
+ ///
+ public static UniformInt NewInclusive(SByte low, SByte high) => UniformInt.CreateInclusive(low, high);
+
+ ///
+ /// Creates uniform distribution in the interval [low, high), inclusive of low and exclusive of high.
+ ///
+ /// The inclusive lower bound.
+ /// The exclusive upper bound.
+ ///
+ /// Thrown when is greater than or equal to .
+ ///
+ public static UniformInt New(Int16 low, Int16 high) => UniformInt.Create(low, high);
+
+ ///
+ /// Creates uniform distribution in the interval [low, high], inclusive of low and high.
+ ///
+ /// The inclusive lower bound.
+ /// The inclusive upper bound.
+ ///
+ /// Thrown when is greater than .
+ ///
+ public static UniformInt NewInclusive(Int16 low, Int16 high) => UniformInt.CreateInclusive(low, high);
+
+ ///
+ /// Creates uniform distribution in the interval [low, high), inclusive of low and exclusive of high.
+ ///
+ /// The inclusive lower bound.
+ /// The exclusive upper bound.
+ ///
+ /// Thrown when is greater than or equal to .
+ ///
+ public static UniformInt New(Int32 low, Int32 high) => UniformInt.Create(low, high);
+
+ ///
+ /// Creates uniform distribution in the interval [low, high], inclusive of low and high.
+ ///
+ /// The inclusive lower bound.
+ /// The inclusive upper bound.
+ ///
+ /// Thrown when is greater than .
+ ///
+ public static UniformInt NewInclusive(Int32 low, Int32 high) => UniformInt.CreateInclusive(low, high);
+
+ ///
+ /// Creates uniform distribution in the interval [low, high), inclusive of low and exclusive of high.
+ ///
+ /// The inclusive lower bound.
+ /// The exclusive upper bound.
+ ///
+ /// Thrown when is greater than or equal to .
+ ///
+ public static UniformInt New(Int64 low, Int64 high) => UniformInt.Create(low, high);
+
+ ///
+ /// Creates uniform distribution in the interval [low, high], inclusive of low and high.
+ ///
+ /// The inclusive lower bound.
+ /// The inclusive upper bound.
+ ///
+ /// Thrown when is greater than .
+ ///
+ public static UniformInt NewInclusive(Int64 low, Int64 high) => UniformInt.CreateInclusive(low, high);
+
+ ///
+ /// Creates uniform distribution in the interval [low, high), inclusive of low and exclusive of high.
+ ///
+ /// The inclusive lower bound.
+ /// The exclusive upper bound.
+ ///
+ /// Thrown when is greater than or equal to .
+ ///
+ public static UniformInt New(Byte low, Byte high) => UniformInt.Create(low, high);
+
+ ///
+ /// Creates uniform distribution in the interval [low, high], inclusive of low and high.
+ ///
+ /// The inclusive lower bound.
+ /// The inclusive upper bound.
+ ///
+ /// Thrown when is greater than .
+ ///
+ public static UniformInt NewInclusive(Byte low, Byte high) => UniformInt.CreateInclusive(low, high);
+
+ ///
+ /// Creates uniform distribution in the interval [low, high), inclusive of low and exclusive of high.
+ ///
+ /// The inclusive lower bound.
+ /// The exclusive upper bound.
+ ///
+ /// Thrown when is greater than or equal to .
+ ///
+ public static UniformInt New(UInt16 low, UInt16 high) => UniformInt.Create(low, high);
+
+ ///
+ /// Creates uniform distribution in the interval [low, high], inclusive of low and high.
+ ///
+ /// The inclusive lower bound.
+ /// The inclusive upper bound.
+ ///
+ /// Thrown when is greater than .
+ ///
+ public static UniformInt NewInclusive(UInt16 low, UInt16 high) => UniformInt.CreateInclusive(low, high);
+
+ ///
+ /// Creates uniform distribution in the interval [low, high), inclusive of low and exclusive of high.
+ ///
+ /// The inclusive lower bound.
+ /// The exclusive upper bound.
+ ///
+ /// Thrown when is greater than or equal to .
+ ///
+ public static UniformInt New(UInt32 low, UInt32 high) => UniformInt.Create(low, high);
+
+ ///
+ /// Creates uniform distribution in the interval [low, high], inclusive of low and high.
+ ///
+ /// The inclusive lower bound.
+ /// The inclusive upper bound.
+ ///
+ /// Thrown when is greater than .
+ ///
+ public static UniformInt NewInclusive(UInt32 low, UInt32 high) => UniformInt.CreateInclusive(low, high);
+
+ ///
+ /// Creates uniform distribution in the interval [low, high), inclusive of low and exclusive of high.
+ ///
+ /// The inclusive lower bound.
+ /// The exclusive upper bound.
+ ///
+ /// Thrown when is greater than or equal to .
+ ///
+ public static UniformInt New(UInt64 low, UInt64 high) => UniformInt.Create(low, high);
+
+ ///
+ /// Creates uniform distribution in the interval [low, high], inclusive of low and high.
+ ///
+ /// The inclusive lower bound.
+ /// The inclusive upper bound.
+ ///
+ /// Thrown when is greater than .
+ ///
+ public static UniformInt NewInclusive(UInt64 low, UInt64 high) => UniformInt.CreateInclusive(low, high);
+
///
/// Creates uniform distribution in the interval [low, high), inclusive of low and exclusive of high.
///
diff git a/src/RandN/Distributions/Uniform.tt b/src/RandN/Distributions/Uniform.tt
 a/src/RandN/Distributions/Uniform.tt
+++ b/src/RandN/Distributions/Uniform.tt
@@ 4,6 +4,11 @@
<#
var types = new Type[]
{
+ typeof(TimeSpan),
+ typeof(Decimal),
+};
+var genericTypes = new Type[]
+{
typeof(SByte),
typeof(Int16),
typeof(Int32),
@@ 12,8 +17,6 @@
typeof(UInt16),
typeof(UInt32),
typeof(UInt64),
 typeof(TimeSpan),
 typeof(Decimal),
};
#>
using System;
@@ 54,6 +57,33 @@
<#
}
#>
+<#
+foreach (var type in genericTypes)
+{
+ String typeName = type.Name;
+#>
+ ///
+ /// Creates uniform distribution in the interval [low, high), inclusive of low and exclusive of high.
+ ///
+ /// The inclusive lower bound.
+ /// The exclusive upper bound.
+ ///
+ /// Thrown when is greater than or equal to .
+ ///
+ public static UniformInt<<#= typeName #>> New(<#= typeName #> low, <#= typeName #> high) => UniformInt.Create(low, high);
+
+ ///
+ /// Creates uniform distribution in the interval [low, high], inclusive of low and high.
+ ///
+ /// The inclusive lower bound.
+ /// The inclusive upper bound.
+ ///
+ /// Thrown when is greater than .
+ ///
+ public static UniformInt<<#= typeName #>> NewInclusive(<#= typeName #> low, <#= typeName #> high) => UniformInt.CreateInclusive(low, high);
+<#
+}
+#>
///
/// Creates uniform distribution in the interval [low, high), inclusive of low and exclusive of high.
///
diff git a/src/RandN/Distributions/UniformFloat.cs b/src/RandN/Distributions/UniformFloat.cs
 a/src/RandN/Distributions/UniformFloat.cs
+++ b/src/RandN/Distributions/UniformFloat.cs
@@ 178,7 +178,7 @@
/// Use of any other type results in a runtime exception.
///
public readonly struct UniformFloat : IDistribution
 // We're extremely restrictive here to discourage people from trying to use nonsupported type for T
+ // We're extremely restrictive here to discourage people from trying to use unsupported types for T
where T : struct, IComparable, IComparable, IConvertible, IEquatable, IFormattable
{
private readonly T _low;
diff git a/src/RandN/Distributions/UniformFloat.tt b/src/RandN/Distributions/UniformFloat.tt
 a/src/RandN/Distributions/UniformFloat.tt
+++ b/src/RandN/Distributions/UniformFloat.tt
@@ 111,7 +111,7 @@
/// Use of any other type results in a runtime exception.
///
public readonly struct UniformFloat : IDistribution
 // We're extremely restrictive here to discourage people from trying to use nonsupported type for T
+ // We're extremely restrictive here to discourage people from trying to use unsupported types for T
where T : struct, IComparable, IComparable, IConvertible, IEquatable, IFormattable
{
private readonly T _low;
diff git a/src/RandN/Distributions/UniformInt.cs b/src/RandN/Distributions/UniformInt.cs
 a/src/RandN/Distributions/UniformInt.cs
+++ b/src/RandN/Distributions/UniformInt.cs
@@ 4,10 +4,380 @@
using System;
+using Genumerics;
/*** This file is auto generated  any changes made here will be lost. ***/
namespace RandN.Distributions
{
+ internal static class UniformInt
+ {
+
+
+ ///
+ /// Creates a with an exclusive upper bound. Should not
+ /// be used directly; instead, use .
+ ///
+ ///
+ /// Thrown when is greater than or equal to .
+ ///
+ public static UniformInt Create(SByte low, SByte high)
+ {
+ if (low >= high)
+ throw new ArgumentOutOfRangeException(nameof(high), $"{nameof(high)} ({high}) must be higher than {nameof(low)} ({low}).");
+
+ return CreateInclusive(low, (SByte)(high  1));
+ }
+
+
+ ///
+ /// Creates a with an exclusive lower bound. Should not
+ /// be used directly; instead, use .
+ ///
+ ///
+ /// Thrown when is greater than .
+ ///
+ public static UniformInt CreateInclusive(SByte low, SByte high)
+ {
+ if (low > high)
+ throw new ArgumentOutOfRangeException(nameof(high), $"{nameof(high)} ({high}) must be higher than or equal to {nameof(low)} ({low}).");
+
+ var unsignedMax = UInt32.MaxValue;
+ var range = unchecked((UInt32)(high  low + 1));
+ var intsToReject = range == 0 ? 0 : (unsignedMax  range + 1) % range;
+
+ return new UniformInt(low, range, (Byte)intsToReject);
+ }
+
+
+ ///
+ /// Creates a with an exclusive upper bound. Should not
+ /// be used directly; instead, use .
+ ///
+ ///
+ /// Thrown when is greater than or equal to .
+ ///
+ public static UniformInt Create(Int16 low, Int16 high)
+ {
+ if (low >= high)
+ throw new ArgumentOutOfRangeException(nameof(high), $"{nameof(high)} ({high}) must be higher than {nameof(low)} ({low}).");
+
+ return CreateInclusive(low, (Int16)(high  1));
+ }
+
+
+ ///
+ /// Creates a with an exclusive lower bound. Should not
+ /// be used directly; instead, use .
+ ///
+ ///
+ /// Thrown when is greater than .
+ ///
+ public static UniformInt CreateInclusive(Int16 low, Int16 high)
+ {
+ if (low > high)
+ throw new ArgumentOutOfRangeException(nameof(high), $"{nameof(high)} ({high}) must be higher than or equal to {nameof(low)} ({low}).");
+
+ var unsignedMax = UInt32.MaxValue;
+ var range = unchecked((UInt32)(high  low + 1));
+ var intsToReject = range == 0 ? 0 : (unsignedMax  range + 1) % range;
+
+ return new UniformInt(low, range, (UInt16)intsToReject);
+ }
+
+
+ ///
+ /// Creates a with an exclusive upper bound. Should not
+ /// be used directly; instead, use .
+ ///
+ ///
+ /// Thrown when is greater than or equal to .
+ ///
+ public static UniformInt Create(Int32 low, Int32 high)
+ {
+ if (low >= high)
+ throw new ArgumentOutOfRangeException(nameof(high), $"{nameof(high)} ({high}) must be higher than {nameof(low)} ({low}).");
+
+ return CreateInclusive(low, (Int32)(high  1));
+ }
+
+
+ ///
+ /// Creates a with an exclusive lower bound. Should not
+ /// be used directly; instead, use .
+ ///
+ ///
+ /// Thrown when is greater than .
+ ///
+ public static UniformInt CreateInclusive(Int32 low, Int32 high)
+ {
+ if (low > high)
+ throw new ArgumentOutOfRangeException(nameof(high), $"{nameof(high)} ({high}) must be higher than or equal to {nameof(low)} ({low}).");
+
+ var unsignedMax = UInt32.MaxValue;
+ var range = unchecked((UInt32)(high  low + 1));
+ var intsToReject = range == 0 ? 0 : (unsignedMax  range + 1) % range;
+
+ return new UniformInt(low, range, (UInt32)intsToReject);
+ }
+
+
+ ///
+ /// Creates a with an exclusive upper bound. Should not
+ /// be used directly; instead, use .
+ ///
+ ///
+ /// Thrown when is greater than or equal to .
+ ///
+ public static UniformInt Create(Int64 low, Int64 high)
+ {
+ if (low >= high)
+ throw new ArgumentOutOfRangeException(nameof(high), $"{nameof(high)} ({high}) must be higher than {nameof(low)} ({low}).");
+
+ return CreateInclusive(low, (Int64)(high  1));
+ }
+
+
+ ///
+ /// Creates a with an exclusive lower bound. Should not
+ /// be used directly; instead, use .
+ ///
+ ///
+ /// Thrown when is greater than .
+ ///
+ public static UniformInt CreateInclusive(Int64 low, Int64 high)
+ {
+ if (low > high)
+ throw new ArgumentOutOfRangeException(nameof(high), $"{nameof(high)} ({high}) must be higher than or equal to {nameof(low)} ({low}).");
+
+ var unsignedMax = UInt64.MaxValue;
+ var range = unchecked((UInt64)(high  low + 1));
+ var intsToReject = range == 0 ? 0 : (unsignedMax  range + 1) % range;
+
+ return new UniformInt(low, range, (UInt64)intsToReject);
+ }
+
+
+ ///
+ /// Creates a with an exclusive upper bound. Should not
+ /// be used directly; instead, use .
+ ///
+ ///
+ /// Thrown when is greater than or equal to .
+ ///
+ public static UniformInt Create(Byte low, Byte high)
+ {
+ if (low >= high)
+ throw new ArgumentOutOfRangeException(nameof(high), $"{nameof(high)} ({high}) must be higher than {nameof(low)} ({low}).");
+
+ return CreateInclusive(low, (Byte)(high  1));
+ }
+
+
+ ///
+ /// Creates a with an exclusive lower bound. Should not
+ /// be used directly; instead, use .
+ ///
+ ///
+ /// Thrown when is greater than .
+ ///
+ public static UniformInt CreateInclusive(Byte low, Byte high)
+ {
+ if (low > high)
+ throw new ArgumentOutOfRangeException(nameof(high), $"{nameof(high)} ({high}) must be higher than or equal to {nameof(low)} ({low}).");
+
+ var unsignedMax = UInt32.MaxValue;
+ var range = unchecked((UInt32)(high  low + 1));
+ var intsToReject = range == 0 ? 0 : (unsignedMax  range + 1) % range;
+
+ return new UniformInt(low, range, (Byte)intsToReject);
+ }
+
+
+ ///
+ /// Creates a with an exclusive upper bound. Should not
+ /// be used directly; instead, use .
+ ///
+ ///
+ /// Thrown when is greater than or equal to .
+ ///
+ public static UniformInt Create(UInt16 low, UInt16 high)
+ {
+ if (low >= high)
+ throw new ArgumentOutOfRangeException(nameof(high), $"{nameof(high)} ({high}) must be higher than {nameof(low)} ({low}).");
+
+ return CreateInclusive(low, (UInt16)(high  1));
+ }
+
+
+ ///
+ /// Creates a with an exclusive lower bound. Should not
+ /// be used directly; instead, use .
+ ///
+ ///
+ /// Thrown when is greater than .
+ ///
+ public static UniformInt CreateInclusive(UInt16 low, UInt16 high)
+ {
+ if (low > high)
+ throw new ArgumentOutOfRangeException(nameof(high), $"{nameof(high)} ({high}) must be higher than or equal to {nameof(low)} ({low}).");
+
+ var unsignedMax = UInt32.MaxValue;
+ var range = unchecked((UInt32)(high  low + 1));
+ var intsToReject = range == 0 ? 0 : (unsignedMax  range + 1) % range;
+
+ return new UniformInt(low, range, (UInt16)intsToReject);
+ }
+
+
+ ///
+ /// Creates a with an exclusive upper bound. Should not
+ /// be used directly; instead, use .
+ ///
+ ///
+ /// Thrown when is greater than or equal to .
+ ///
+ public static UniformInt Create(UInt32 low, UInt32 high)
+ {
+ if (low >= high)
+ throw new ArgumentOutOfRangeException(nameof(high), $"{nameof(high)} ({high}) must be higher than {nameof(low)} ({low}).");
+
+ return CreateInclusive(low, (UInt32)(high  1));
+ }
+
+
+ ///
+ /// Creates a with an exclusive lower bound. Should not
+ /// be used directly; instead, use .
+ ///
+ ///
+ /// Thrown when is greater than .
+ ///
+ public static UniformInt CreateInclusive(UInt32 low, UInt32 high)
+ {
+ if (low > high)
+ throw new ArgumentOutOfRangeException(nameof(high), $"{nameof(high)} ({high}) must be higher than or equal to {nameof(low)} ({low}).");
+
+ var unsignedMax = UInt32.MaxValue;
+ var range = unchecked((UInt32)(high  low + 1));
+ var intsToReject = range == 0 ? 0 : (unsignedMax  range + 1) % range;
+
+ return new UniformInt(low, range, (UInt32)intsToReject);
+ }
+
+
+ ///
+ /// Creates a with an exclusive upper bound. Should not
+ /// be used directly; instead, use .
+ ///
+ ///
+ /// Thrown when is greater than or equal to .
+ ///
+ public static UniformInt Create(UInt64 low, UInt64 high)
+ {
+ if (low >= high)
+ throw new ArgumentOutOfRangeException(nameof(high), $"{nameof(high)} ({high}) must be higher than {nameof(low)} ({low}).");
+
+ return CreateInclusive(low, (UInt64)(high  1));
+ }
+
+
+ ///
+ /// Creates a with an exclusive lower bound. Should not
+ /// be used directly; instead, use .
+ ///
+ ///
+ /// Thrown when is greater than .
+ ///
+ public static UniformInt CreateInclusive(UInt64 low, UInt64 high)
+ {
+ if (low > high)
+ throw new ArgumentOutOfRangeException(nameof(high), $"{nameof(high)} ({high}) must be higher than or equal to {nameof(low)} ({low}).");
+
+ var unsignedMax = UInt64.MaxValue;
+ var range = unchecked((UInt64)(high  low + 1));
+ var intsToReject = range == 0 ? 0 : (unsignedMax  range + 1) % range;
+
+ return new UniformInt(low, range, (UInt64)intsToReject);
+ }
+
+ }
+
+ ///
+ /// Implements a Uniform for integral types such as and .
+ /// Use of any other type results in a runtime exception.
+ ///
+ public readonly struct UniformInt : IPortableDistribution
+ // We're extremely restrictive here to discourage people from trying to use unsupported types for T
+ where T : struct, IComparable, IComparable, IConvertible, IEquatable, IFormattable
+ {
+ private readonly Number _low;
+ private readonly UInt64 _range;
+ private readonly UInt64 _zone;
+
+ internal UniformInt(Number low, UInt64 range, UInt64 zone)
+ {
+ _low = low;
+ _range = range;
+ _zone = zone;
+ }
+
+ ///
+ public T Sample(TRng rng) where TRng : notnull, IRng
+ {
+ if (typeof(T) == typeof(Int32))
+ {
+ var unsigned = rng.NextUInt32();
+ if (_range == 0) // 0 is a special case where we sample the entire range.
+ return unchecked(Number.Convert(unsigned));
+
+ var zone = UInt32.MaxValue  _zone;
+
+ while (unsigned > zone)
+ {
+ unsigned = rng.NextUInt32();
+ }
+
+ return unchecked(Number.Convert(unsigned % (UInt32)_range) + _low);
+ }
+ else
+ {
+ var unsigned = rng.NextUInt64();
+ if (_range == 0) // 0 is a special case where we sample the entire range.
+ return Number.Convert(unsigned);
+
+ var zone = UInt64.MaxValue  _zone;
+
+ while (unsigned > zone)
+ {
+ unsigned = rng.NextUInt64();
+ }
+
+ return unchecked(Number.Convert(unsigned % _range) + _low);
+ }
+ }
+
+ ///
+ public Boolean TrySample(TRng rng, out T result) where TRng : notnull, IRng
+ {
+ var unsigned = rng.NextUInt64();
+ if (_range == 0) // 0 is a special case where we sample the entire range.
+ {
+ result = Number.Convert(unsigned);
+ return true;
+ }
+
+ var zone = UInt64.MaxValue  _zone;
+
+ if (unsigned > zone)
+ {
+ result = default;
+ return false;
+ }
+
+ result = unchecked(Number.Convert(unsigned % _range) + _low);
+ return true;
+ }
+ }
///
/// A uniform distribution of type .
diff git a/src/RandN/Distributions/UniformInt.tt b/src/RandN/Distributions/UniformInt.tt
 a/src/RandN/Distributions/UniformInt.tt
+++ b/src/RandN/Distributions/UniformInt.tt
@@ 16,10 +16,135 @@
};
#>
using System;
+using Genumerics;
/*** This file is auto generated  any changes made here will be lost. ***/
namespace RandN.Distributions
{
+ internal static class UniformInt
+ {
+<# foreach (var param in types)
+{
+ String type = param.type.Name;
+ String unsigned = param.unsigned.Name;
+ String ularge = param.ularge.Name;
+#>
+
+ ///
+ /// Creates a with an exclusive upper bound. Should not
+ /// be used directly; instead, use .
+ ///
+ ///
+ /// Thrown when is greater than or equal to .
+ ///
+ public static UniformInt<<#= type #>> Create(<#= type #> low, <#= type #> high)
+ {
+ if (low >= high)
+ throw new ArgumentOutOfRangeException(nameof(high), $"{nameof(high)} ({high}) must be higher than {nameof(low)} ({low}).");
+
+ return CreateInclusive(low, (<#= type #>)(high  1));
+ }
+
+
+ ///
+ /// Creates a with an exclusive lower bound. Should not
+ /// be used directly; instead, use .
+ ///
+ ///
+ /// Thrown when is greater than .
+ ///
+ public static UniformInt<<#= type #>> CreateInclusive(<#= type #> low, <#= type #> high)
+ {
+ if (low > high)
+ throw new ArgumentOutOfRangeException(nameof(high), $"{nameof(high)} ({high}) must be higher than or equal to {nameof(low)} ({low}).");
+
+ var unsignedMax = <#= ularge #>.MaxValue;
+ var range = unchecked((<#= ularge #>)(high  low + 1));
+ var intsToReject = range == 0 ? 0 : (unsignedMax  range + 1) % range;
+
+ return new UniformInt<<#= type #>>(low, range, (<#= unsigned #>)intsToReject);
+ }
+<#
+}
+#>
+ }
+
+ ///
+ /// Implements a Uniform for integral types such as and .
+ /// Use of any other type results in a runtime exception.
+ ///
+ public readonly struct UniformInt : IPortableDistribution
+ // We're extremely restrictive here to discourage people from trying to use unsupported types for T
+ where T : struct, IComparable, IComparable, IConvertible, IEquatable, IFormattable
+ {
+ private readonly Number _low;
+ private readonly UInt64 _range;
+ private readonly UInt64 _zone;
+
+ internal UniformInt(Number low, UInt64 range, UInt64 zone)
+ {
+ _low = low;
+ _range = range;
+ _zone = zone;
+ }
+
+ ///
+ public T Sample(TRng rng) where TRng : notnull, IRng
+ {
+ if (typeof(T) == typeof(Int32))
+ {
+ var unsigned = rng.NextUInt32();
+ if (_range == 0) // 0 is a special case where we sample the entire range.
+ return Number.Convert(unsigned);
+
+ var zone = UInt32.MaxValue  _zone;
+
+ while (unsigned > zone)
+ {
+ unsigned = rng.NextUInt32();
+ }
+
+ return unchecked(Number.Convert(unsigned % (UInt32)_range) + _low);
+ }
+ else
+ {
+ var unsigned = rng.NextUInt64();
+ if (_range == 0) // 0 is a special case where we sample the entire range.
+ return Number.Convert(unsigned);
+
+ var zone = UInt64.MaxValue  _zone;
+
+ while (unsigned > zone)
+ {
+ unsigned = rng.NextUInt64();
+ }
+
+ return unchecked(Number.Convert(unsigned % _range) + _low);
+ }
+ }
+
+ ///
+ public Boolean TrySample(TRng rng, out T result) where TRng : notnull, IRng
+ {
+ var unsigned = rng.NextUInt64();
+ if (_range == 0) // 0 is a special case where we sample the entire range.
+ {
+ result = Number.Convert(unsigned);
+ return true;
+ }
+
+ var zone = UInt64.MaxValue  _zone;
+
+ if (unsigned > zone)
+ {
+ result = default;
+ return false;
+ }
+
+ result = unchecked(Number.Convert(unsigned % _range) + _low);
+ return true;
+ }
+ }
<#
foreach (var tuple in types)
{
diff git a/src/RandN/RandN.csproj b/src/RandN/RandN.csproj
 a/src/RandN/RandN.csproj
+++ b/src/RandN/RandN.csproj
@@ 57,6 +57,7 @@
+