diff --git a/src/Numerics/Distributions/Discrete/Bernoulli.cs b/src/Numerics/Distributions/Discrete/Bernoulli.cs
index 43828368..d60addf1 100644
--- a/src/Numerics/Distributions/Discrete/Bernoulli.cs
+++ b/src/Numerics/Distributions/Discrete/Bernoulli.cs
@@ -306,7 +306,7 @@ namespace MathNet.Numerics.Distributions
}
///
- /// Samples an array of Bernoulli distributed random variables.
+ /// Samples a sequence of Bernoulli distributed random variables.
///
/// The random number generator to use.
/// The probability of generating a 1.
diff --git a/src/Numerics/Distributions/Discrete/DiscreteUniform.cs b/src/Numerics/Distributions/Discrete/DiscreteUniform.cs
new file mode 100644
index 00000000..36ad4eb7
--- /dev/null
+++ b/src/Numerics/Distributions/Discrete/DiscreteUniform.cs
@@ -0,0 +1,354 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://mathnet.opensourcedotnet.info
+//
+// Copyright (c) 2009 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.Distributions
+{
+ using System;
+ using System.Collections.Generic;
+ using Properties;
+
+ ///
+ /// The discrete uniform distribution is a distribution over integers. The distribution
+ /// is parameterized by a lower and upper bound (both inclusive).
+ ///
+ /// The distribution will use the by default.
+ /// Users can set the random number generator by using the property.
+ /// The statistics classes will check all the incoming parameters whether they are in the allowed
+ /// range. This might involve heavy computation. Optionally, by setting Control.CheckDistributionParameters
+ /// to false, all parameter checks can be turned off.
+ public class DiscreteUniform : IDiscreteDistribution
+ {
+ ///
+ /// The distribution's lower bound.
+ ///
+ private int _lower;
+
+ ///
+ /// The distribution's upper bound.
+ ///
+ private int _upper;
+
+ ///
+ /// The distribution's random number generator.
+ ///
+ private Random _random;
+
+ ///
+ /// Initializes a new instance of the DiscreteUniform class.
+ ///
+ /// Lower bound.
+ /// Upper bound; must be at least as large as .
+ public DiscreteUniform(int lower, int upper)
+ {
+ SetParameters(lower, upper);
+ RandomSource = new System.Random();
+ }
+
+ ///
+ /// A string representation of the distribution.
+ ///
+ public override string ToString()
+ {
+ return "DiscreteUniform(Lower = " + _lower + ", Upper = " + _upper + ")";
+ }
+
+ ///
+ /// Checks whether the parameters of the distribution are valid.
+ ///
+ /// Lower bound.
+ /// Upper bound; must be at least as large as .
+ /// True when the parameters are valid, false otherwise.
+ private static bool IsValidParameterSet(int lower, int upper)
+ {
+ if (lower <= upper)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Sets the parameters of the distribution after checking their validity.
+ ///
+ /// Lower bound.
+ /// Upper bound; must be at least as large as .
+ /// When the parameters don't pass the function.
+ private void SetParameters(int lower, int upper)
+ {
+ if (Control.CheckDistributionParameters && !IsValidParameterSet(lower, upper))
+ {
+ throw new ArgumentOutOfRangeException(Resources.InvalidDistributionParameters);
+ }
+
+ _lower = lower;
+ _upper = upper;
+ }
+
+ ///
+ /// Gets or sets the lower bound of the probability distribution.
+ ///
+ public int LowerBound
+ {
+ get
+ {
+ return _lower;
+ }
+
+ set
+ {
+ SetParameters(value, _upper);
+ }
+ }
+
+ ///
+ /// Gets or sets the upper bound of the probability distribution.
+ ///
+ public int UpperBound
+ {
+ get
+ {
+ return _upper;
+ }
+
+ set
+ {
+ SetParameters(_lower, value);
+ }
+ }
+
+ #region IDistribution Members
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public Random RandomSource
+ {
+ get
+ {
+ return _random;
+ }
+
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException();
+ }
+
+ _random = value;
+ }
+ }
+
+ ///
+ /// Gets the mean of the distribution.
+ ///
+ public double Mean
+ {
+ get { return (_lower + _upper) / 2.0; }
+ }
+
+ ///
+ /// Gets the standard deviation of the distribution.
+ ///
+ public double StdDev
+ {
+ get { return System.Math.Sqrt(((_upper - _lower + 1.0) * (_upper - _lower + 1.0) - 1.0) / 12.0); }
+ }
+
+ ///
+ /// Gets the variance of the distribution.
+ ///
+ public double Variance
+ {
+ get { return ((_upper - _lower + 1.0) * (_upper - _lower + 1.0) - 1.0) / 12.0; }
+ }
+
+ ///
+ /// Gets the entropy of the distribution.
+ ///
+ public double Entropy
+ {
+ get { return System.Math.Log(_upper - _lower + 1.0); }
+ }
+
+ ///
+ /// Gets the skewness of the distribution.
+ ///
+ public double Skewness
+ {
+ get { return 0.0; }
+ }
+
+ ///
+ /// Gets the smallest element in the domain of the distributions which can be represented by an integer.
+ ///
+ public int Minimum { get { return _lower; } }
+
+ ///
+ /// Gets the largest element in the domain of the distributions which can be represented by an integer.
+ ///
+ public int Maximum { get { return _upper; } }
+
+ ///
+ /// Computes the cumulative distribution function of the Bernoulli distribution.
+ ///
+ /// The location at which to compute the cumulative density.
+ /// the cumulative density at .
+ public double CumulativeDistribution(double x)
+ {
+ if (x < _lower)
+ {
+ return 0.0;
+ }
+ else if (x >= _upper)
+ {
+ return 1.0;
+ }
+
+ return Math.Min(1.0, (Math.Floor(x) - _lower + 1) / (_upper - _lower + 1));
+ }
+
+ #endregion
+
+ #region IDiscreteDistribution Members
+
+ ///
+ /// The mode of the distribution; since every element in the domain has the same probability this method returns the middle one.
+ ///
+ public int Mode
+ {
+ get { return (int) Math.Floor((_lower + _upper) / 2.0); }
+ }
+
+ ///
+ /// The median of the distribution.
+ ///
+ public int Median
+ {
+ get { return (int)Math.Floor((_lower + _upper) / 2.0); }
+ }
+
+ ///
+ /// Computes the probability of a specific value.
+ ///
+ public double Probability(int val)
+ {
+ if (val >= _lower && val <= _upper)
+ {
+ return 1.0 / (_upper - _lower + 1);
+ }
+
+ return 0.0;
+ }
+
+ ///
+ /// Computes the probability of a specific value.
+ ///
+ public double ProbabilityLn(int val)
+ {
+ if (val >= _lower && val <= _upper)
+ {
+ return - Math.Log(_upper - _lower + 1);
+ }
+
+ return Double.NegativeInfinity;
+ }
+
+ ///
+ /// Samples a uniformly distributed random variable.
+ ///
+ public int Sample()
+ {
+ return DoSample(RandomSource, _lower, _upper);
+ }
+
+ ///
+ /// Samples an array of uniformly distributed random variables.
+ ///
+ /// a sequence of samples from the distribution.
+ public IEnumerable Samples()
+ {
+ while (true)
+ {
+ yield return DoSample(RandomSource, _lower, _upper);
+ }
+ }
+
+ #endregion
+
+ ///
+ /// Samples a uniformly distributed random variable.
+ ///
+ /// The random number generator to use.
+ /// The lower bound of the uniform random variable.
+ /// The upper bound of the uniform random variable.
+ /// A sample from the discrete uniform distribution.
+ public static int Sample(System.Random rnd, int lower, int upper)
+ {
+ if (Control.CheckDistributionParameters && !IsValidParameterSet(lower, upper))
+ {
+ throw new ArgumentOutOfRangeException(Resources.InvalidDistributionParameters);
+ }
+
+ return DoSample(rnd, lower, upper);
+ }
+
+ ///
+ /// Samples a sequence of uniformly distributed random variables.
+ ///
+ /// The random number generator to use.
+ /// The lower bound of the uniform random variable.
+ /// The upper bound of the uniform random variable.
+ /// a sequence of samples from the discrete uniform distribution.
+ public static IEnumerable Samples(System.Random rnd, int lower, int upper)
+ {
+ if (Control.CheckDistributionParameters && !IsValidParameterSet(lower, upper))
+ {
+ throw new ArgumentOutOfRangeException(Resources.InvalidDistributionParameters);
+ }
+
+ while (true)
+ {
+ yield return DoSample(rnd, lower, upper);
+ }
+ }
+
+ ///
+ /// Generates one sample from the discrete uniform distribution. This method does not do any parameter checking.
+ ///
+ /// The random source to use.
+ /// The lower bound of the uniform random variable.
+ /// The upper bound of the uniform random variable.
+ /// A random sample from the discrete uniform distribution.
+ private static int DoSample(System.Random rnd, int lower, int upper)
+ {
+ return rnd.Next() % (upper - lower + 1) + lower;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj
index 068ff5fd..45eefabf 100644
--- a/src/Numerics/Numerics.csproj
+++ b/src/Numerics/Numerics.csproj
@@ -57,6 +57,7 @@
+
diff --git a/src/UnitTests/DistributionTests/Discrete/DiscreteUniformTests.cs b/src/UnitTests/DistributionTests/Discrete/DiscreteUniformTests.cs
new file mode 100644
index 00000000..888e90c9
--- /dev/null
+++ b/src/UnitTests/DistributionTests/Discrete/DiscreteUniformTests.cs
@@ -0,0 +1,261 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://mathnet.opensourcedotnet.info
+//
+// Copyright (c) 2009 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.UnitTests.DistributionTests
+{
+ using System;
+ using System.Linq;
+ using MbUnit.Framework;
+ using MathNet.Numerics.Distributions;
+
+ [TestFixture]
+ public class DiscreteUniformTests
+ {
+ [SetUp]
+ public void SetUp()
+ {
+ Control.CheckDistributionParameters = true;
+ }
+
+ [Test]
+ [Row(-10, 10)]
+ [Row(0, 4)]
+ [Row(10, 20)]
+ [Row(20, 20)]
+ public void CanCreateDiscreteUniform(int l, int u)
+ {
+ var du = new DiscreteUniform(l, u);
+ AssertEx.AreEqual(l, du.LowerBound);
+ AssertEx.AreEqual(u, du.UpperBound);
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ [Row(-1, -2)]
+ [Row(6, 5)]
+ public void DiscreteUniformCreateFailsWithBadParameters(int l, int u)
+ {
+ var du = new DiscreteUniform(l, u);
+ }
+
+ [Test]
+ public void ValidateToString()
+ {
+ var b = new DiscreteUniform(0, 10);
+ AssertEx.AreEqual("DiscreteUniform(Lower = 0, Upper = 10)", b.ToString());
+ }
+
+ [Test]
+ [Row(0)]
+ [Row(3)]
+ [Row(10)]
+ public void CanSetLowerBound(int p)
+ {
+ var b = new DiscreteUniform(0, 10);
+ b.LowerBound = p;
+ }
+
+ [Test]
+ [Row(0)]
+ [Row(3)]
+ [Row(10)]
+ public void CanSetUpperBound(int p)
+ {
+ var b = new DiscreteUniform(0, 10);
+ b.UpperBound = p;
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ [Row(11)]
+ [Row(20.0)]
+ public void SetLowerBoundFails(int p)
+ {
+ var b = new DiscreteUniform(0, 10);
+ b.LowerBound = p;
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ [Row(-11)]
+ [Row(-20)]
+ public void SetUpperBoundFails(int p)
+ {
+ var b = new DiscreteUniform(0, 10);
+ b.UpperBound = p;
+ }
+
+ [Test]
+ [Row(-10, 10, 3.0445224377234229965005979803657054342845752874046093)]
+ [Row(0, 4, 1.6094379124341003746007593332261876395256013542685181)]
+ [Row(10, 20, 2.3978952727983705440619435779651292998217068539374197)]
+ [Row(20, 20, 0.0)]
+ public void ValidateEntropy(int l, int u, double e)
+ {
+ var du = new DiscreteUniform(l, u);
+ AssertHelpers.AlmostEqual(e, du.Entropy, 14);
+ }
+
+ [Test]
+ [Row(-10, 10)]
+ [Row(0, 4)]
+ [Row(10, 20)]
+ [Row(20, 20)]
+ public void ValidateSkewness(int l, int u)
+ {
+ var du = new DiscreteUniform(l, u);
+ AssertEx.AreEqual(0.0, du.Skewness);
+ }
+
+ [Test]
+ [Row(-10, 10, 0)]
+ [Row(0, 4, 2)]
+ [Row(10, 20, 15)]
+ [Row(20, 20, 20)]
+ public void ValidateMode(int l, int u, int m)
+ {
+ var du = new DiscreteUniform(l, u);
+ AssertEx.AreEqual(m, du.Mode);
+ }
+
+ [Test]
+ [Row(-10, 10, 0)]
+ [Row(0, 4, 2)]
+ [Row(10, 20, 15)]
+ [Row(20, 20, 20)]
+ public void ValidateMedian(int l, int u, int m)
+ {
+ var du = new DiscreteUniform(l, u);
+ Assert.AreEqual(m, du.Median);
+ }
+
+ [Test]
+ [Row(-10, 10, 0.0)]
+ [Row(0, 4, 2.0)]
+ [Row(10, 20, 15.0)]
+ [Row(20, 20, 20.0)]
+ public void ValidateMean(int l, int u, double m)
+ {
+ var du = new DiscreteUniform(l, u);
+ Assert.AreEqual(m, du.Mean);
+ }
+
+ [Test]
+ public void ValidateMinimum()
+ {
+ var b = new DiscreteUniform(-10, 10);
+ AssertEx.AreEqual(-10, b.Minimum);
+ }
+
+ [Test]
+ public void ValidateMaximum()
+ {
+ var b = new DiscreteUniform(-10, 10);
+ AssertEx.AreEqual(10, b.Maximum);
+ }
+
+ [Test]
+ [Row(-10, 10, -5, 1/21.0)]
+ [Row(-10, 10, 1, 1 / 21.0)]
+ [Row(-10, 10, 10, 1 / 21.0)]
+ [Row(-10, -10, 0, 0.0)]
+ [Row(-10, -10, -10, 1.0)]
+ public void ValidateProbability(int l, int u, int x, double p)
+ {
+ var b = new DiscreteUniform(l, u);
+ AssertEx.AreEqual(p, b.Probability(x));
+ }
+
+ [Test]
+ [Row(-10, 10, -5, -3.0445224377234229965005979803657054342845752874046093)]
+ [Row(-10, 10, 1, -3.0445224377234229965005979803657054342845752874046093)]
+ [Row(-10, 10, 10, -3.0445224377234229965005979803657054342845752874046093)]
+ [Row(-10, -10, 0, Double.NegativeInfinity)]
+ [Row(-10, -10, -10, 0.0)]
+ public void ValidateProbabilityLn(int l, int u, int x, double dln)
+ {
+ var b = new DiscreteUniform(l, u);
+ AssertEx.AreEqual(dln, b.ProbabilityLn(x));
+ }
+
+ [Test]
+ public void CanSampleStatic()
+ {
+ var d = DiscreteUniform.Sample(new Random(), 0, 10);
+ }
+
+ [Test]
+ public void CanSampleSequenceStatic()
+ {
+ var ied = DiscreteUniform.Samples(new Random(), 0, 10);
+ var arr = ied.Take(5).ToArray();
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void FailSampleStatic()
+ {
+ var d = DiscreteUniform.Sample(new Random(), 20, 10);
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void FailSampleSequenceStatic()
+ {
+ var ied = DiscreteUniform.Samples(new Random(), 20, 10).First();
+ }
+
+ [Test]
+ public void CanSample()
+ {
+ var n = new DiscreteUniform(0, 10);
+ var d = n.Sample();
+ }
+
+ [Test]
+ public void CanSampleSequence()
+ {
+ var n = new DiscreteUniform(0, 10);
+ var ied = n.Samples();
+ var e = ied.Take(5).ToArray();
+ }
+
+ [Test]
+ [Row(-10, 10, -5, 6.0 / 21.0)]
+ [Row(-10, 10, 1, 12.0 / 21.0)]
+ [Row(-10, 10, 10, 1.0)]
+ [Row(-10, -10, 0, 1.0)]
+ [Row(-10, -10, -10, 1.0)]
+ [Row(-10, -10, -11, 0.0)]
+ public void ValidateCumulativeDistribution(int l, int u, double x, double cdf)
+ {
+ var b = new DiscreteUniform(l, u);
+ AssertEx.AreEqual(cdf, b.CumulativeDistribution(x));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj
index d93aff16..25bc2450 100644
--- a/src/UnitTests/UnitTests.csproj
+++ b/src/UnitTests/UnitTests.csproj
@@ -71,6 +71,7 @@
+