about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-12-09 03:41:27 -0800
committerbors <bors@rust-lang.org>2013-12-09 03:41:27 -0800
commita417dbd1c71c168fe7db71efb98d22b5e22dcce1 (patch)
treecf34353237d7ef5ec0f93919d459f80a19222e15 /src
parente5f20212028fd6cb5161dbb2c0f14c9d94f1caea (diff)
parent705b705ba5182745e12652dc7eeeb10828f520d0 (diff)
downloadrust-a417dbd1c71c168fe7db71efb98d22b5e22dcce1.tar.gz
rust-a417dbd1c71c168fe7db71efb98d22b5e22dcce1.zip
auto merge of #10859 : huonw/rust/helper-dists, r=cmr
This moves `std::rand::distribitions::{Normal, StandardNormal}` to `...::distributions::normal`, reexporting `Normal` from `distributions` (and similarly for `Exp` and Exp1`), and adds:
- Log-normal
- Chi-squared
- F
- Student T

all of which are implemented in C++11's random library. Tests in https://github.com/huonw/random-tests/commit/0424b8aded5e608ae386c1f917934a726d9cac6a. Note that these are approximately half documentation & half implementation (of which a significant portion is boilerplate `}`'s and so on).
Diffstat (limited to 'src')
-rw-r--r--src/libstd/rand/distributions/exponential.rs141
-rw-r--r--src/libstd/rand/distributions/gamma.rs212
-rw-r--r--src/libstd/rand/distributions/mod.rs250
-rw-r--r--src/libstd/rand/distributions/normal.rs211
4 files changed, 568 insertions, 246 deletions
diff --git a/src/libstd/rand/distributions/exponential.rs b/src/libstd/rand/distributions/exponential.rs
new file mode 100644
index 00000000000..4244e6bacdb
--- /dev/null
+++ b/src/libstd/rand/distributions/exponential.rs
@@ -0,0 +1,141 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! The exponential distribution.
+
+use rand::{Rng, Rand};
+use rand::distributions::{ziggurat, ziggurat_tables, Sample, IndependentSample};
+
+/// A wrapper around an `f64` to generate Exp(1) random numbers.
+///
+/// See `Exp` for the general exponential distribution.Note that this
+ // has to be unwrapped before use as an `f64` (using either
+/// `*` or `cast::transmute` is safe).
+///
+/// Implemented via the ZIGNOR variant[1] of the Ziggurat method. The
+/// exact description in the paper was adjusted to use tables for the
+/// exponential distribution rather than normal.
+///
+/// [1]: Jurgen A. Doornik (2005). [*An Improved Ziggurat Method to
+/// Generate Normal Random
+/// Samples*](http://www.doornik.com/research/ziggurat.pdf). Nuffield
+/// College, Oxford
+pub struct Exp1(f64);
+
+// This could be done via `-rng.gen::<f64>().ln()` but that is slower.
+impl Rand for Exp1 {
+    #[inline]
+    fn rand<R:Rng>(rng: &mut R) -> Exp1 {
+        #[inline]
+        fn pdf(x: f64) -> f64 {
+            (-x).exp()
+        }
+        #[inline]
+        fn zero_case<R:Rng>(rng: &mut R, _u: f64) -> f64 {
+            ziggurat_tables::ZIG_EXP_R - rng.gen::<f64>().ln()
+        }
+
+        Exp1(ziggurat(rng, false,
+                      &ziggurat_tables::ZIG_EXP_X,
+                      &ziggurat_tables::ZIG_EXP_F,
+                      pdf, zero_case))
+    }
+}
+
+/// The exponential distribution `Exp(lambda)`.
+///
+/// This distribution has density function: `f(x) = lambda *
+/// exp(-lambda * x)` for `x > 0`.
+///
+/// # Example
+///
+/// ```rust
+/// use std::rand;
+/// use std::rand::distributions::{Exp, IndependentSample};
+///
+/// fn main() {
+///     let exp = Exp::new(2.0);
+///     let v = exp.ind_sample(&mut rand::task_rng());
+///     println!("{} is from a Exp(2) distribution", v);
+/// }
+/// ```
+pub struct Exp {
+    /// `lambda` stored as `1/lambda`, since this is what we scale by.
+    priv lambda_inverse: f64
+}
+
+impl Exp {
+    /// Construct a new `Exp` with the given shape parameter
+    /// `lambda`. Fails if `lambda <= 0`.
+    pub fn new(lambda: f64) -> Exp {
+        assert!(lambda > 0.0, "Exp::new called with `lambda` <= 0");
+        Exp { lambda_inverse: 1.0 / lambda }
+    }
+}
+
+impl Sample<f64> for Exp {
+    fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
+}
+impl IndependentSample<f64> for Exp {
+    fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
+        (*rng.gen::<Exp1>()) * self.lambda_inverse
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use rand::*;
+    use super::*;
+    use iter::range;
+    use option::{Some, None};
+
+    #[test]
+    fn test_exp() {
+        let mut exp = Exp::new(10.0);
+        let mut rng = task_rng();
+        for _ in range(0, 1000) {
+            assert!(exp.sample(&mut rng) >= 0.0);
+            assert!(exp.ind_sample(&mut rng) >= 0.0);
+        }
+    }
+    #[test]
+    #[should_fail]
+    fn test_exp_invalid_lambda_zero() {
+        Exp::new(0.0);
+    }
+    #[test]
+    #[should_fail]
+    fn test_exp_invalid_lambda_neg() {
+        Exp::new(-10.0);
+    }
+}
+
+#[cfg(test)]
+mod bench {
+    use extra::test::BenchHarness;
+    use rand::{XorShiftRng, RAND_BENCH_N};
+    use super::*;
+    use iter::range;
+    use option::{Some, None};
+    use mem::size_of;
+
+    #[bench]
+    fn rand_exp(bh: &mut BenchHarness) {
+        let mut rng = XorShiftRng::new();
+        let mut exp = Exp::new(2.71828 * 3.14159);
+
+        bh.iter(|| {
+            for _ in range(0, RAND_BENCH_N) {
+                exp.sample(&mut rng);
+            }
+        });
+        bh.bytes = size_of::<f64>() as u64 * RAND_BENCH_N;
+    }
+}
diff --git a/src/libstd/rand/distributions/gamma.rs b/src/libstd/rand/distributions/gamma.rs
index e0428742459..ae7ff99af92 100644
--- a/src/libstd/rand/distributions/gamma.rs
+++ b/src/libstd/rand/distributions/gamma.rs
@@ -8,10 +8,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-//! The Gamma distribution.
+//! The Gamma and derived distributions.
 
 use rand::{Rng, Open01};
-use super::{IndependentSample, Sample, StandardNormal, Exp};
+use super::{IndependentSample, Sample, Exp};
+use super::normal::StandardNormal;
 use num;
 
 /// The Gamma distribution `Gamma(shape, scale)` distribution.
@@ -168,6 +169,213 @@ impl IndependentSample<f64> for GammaLargeShape {
     }
 }
 
+/// The chi-squared distribution `χ²(k)`, where `k` is the degrees of
+/// freedom.
+///
+/// For `k > 0` integral, this distribution is the sum of the squares
+/// of `k` independent standard normal random variables. For other
+/// `k`, this uses the equivalent characterisation `χ²(k) = Gamma(k/2,
+/// 2)`.
+///
+/// # Example
+///
+/// ```rust
+/// use std::rand;
+/// use std::rand::distributions::{ChiSquared, IndependentSample};
+///
+/// fn main() {
+///     let chi = ChiSquared::new(11.0);
+///     let v = chi.ind_sample(&mut rand::task_rng());
+///     println!("{} is from a χ²(11) distribution", v)
+/// }
+/// ```
+pub enum ChiSquared {
+    // k == 1, Gamma(alpha, ..) is particularly slow for alpha < 1,
+    // e.g. when alpha = 1/2 as it would be for this case, so special-
+    // casing and using the definition of N(0,1)^2 is faster.
+    priv DoFExactlyOne,
+    priv DoFAnythingElse(Gamma)
+}
+
+impl ChiSquared {
+    /// Create a new chi-squared distribution with degrees-of-freedom
+    /// `k`. Fails if `k < 0`.
+    pub fn new(k: f64) -> ChiSquared {
+        if k == 1.0 {
+            DoFExactlyOne
+        } else {
+            assert!(k > 0.0, "ChiSquared::new called with `k` < 0");
+            DoFAnythingElse(Gamma::new(0.5 * k, 2.0))
+        }
+    }
+}
+impl Sample<f64> for ChiSquared {
+    fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
+}
+impl IndependentSample<f64> for ChiSquared {
+    fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
+        match *self {
+            DoFExactlyOne => {
+                // k == 1 => N(0,1)^2
+                let norm = *rng.gen::<StandardNormal>();
+                norm * norm
+            }
+            DoFAnythingElse(ref g) => g.ind_sample(rng)
+        }
+    }
+}
+
+/// The Fisher F distribution `F(m, n)`.
+///
+/// This distribution is equivalent to the ratio of two normalised
+/// chi-squared distributions, that is, `F(m,n) = (χ²(m)/m) /
+/// (χ²(n)/n)`.
+///
+/// # Example
+///
+/// ```rust
+/// use std::rand;
+/// use std::rand::distributions::{FisherF, IndependentSample};
+///
+/// fn main() {
+///     let f = FisherF::new(2.0, 32.0);
+///     let v = f.ind_sample(&mut rand::task_rng());
+///     println!("{} is from an F(2, 32) distribution", v)
+/// }
+/// ```
+pub struct FisherF {
+    priv numer: ChiSquared,
+    priv denom: ChiSquared,
+    // denom_dof / numer_dof so that this can just be a straight
+    // multiplication, rather than a division.
+    priv dof_ratio: f64,
+}
+
+impl FisherF {
+    /// Create a new `FisherF` distribution, with the given
+    /// parameter. Fails if either `m` or `n` are not positive.
+    pub fn new(m: f64, n: f64) -> FisherF {
+        assert!(m > 0.0, "FisherF::new called with `m < 0`");
+        assert!(n > 0.0, "FisherF::new called with `n < 0`");
+
+        FisherF {
+            numer: ChiSquared::new(m),
+            denom: ChiSquared::new(n),
+            dof_ratio: n / m
+        }
+    }
+}
+impl Sample<f64> for FisherF {
+    fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
+}
+impl IndependentSample<f64> for FisherF {
+    fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
+        self.numer.ind_sample(rng) / self.denom.ind_sample(rng) * self.dof_ratio
+    }
+}
+
+/// The Student t distribution, `t(nu)`, where `nu` is the degrees of
+/// freedom.
+///
+/// # Example
+///
+/// ```rust
+/// use std::rand;
+/// use std::rand::distributions::{StudentT, IndependentSample};
+///
+/// fn main() {
+///     let t = StudentT::new(11.0);
+///     let v = t.ind_sample(&mut rand::task_rng());
+///     println!("{} is from a t(11) distribution", v)
+/// }
+/// ```
+pub struct StudentT {
+    priv chi: ChiSquared,
+    priv dof: f64
+}
+
+impl StudentT {
+    /// Create a new Student t distribution with `n` degrees of
+    /// freedom. Fails if `n <= 0`.
+    pub fn new(n: f64) -> StudentT {
+        assert!(n > 0.0, "StudentT::new called with `n <= 0`");
+        StudentT {
+            chi: ChiSquared::new(n),
+            dof: n
+        }
+    }
+}
+impl Sample<f64> for StudentT {
+    fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
+}
+impl IndependentSample<f64> for StudentT {
+    fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
+        let norm = *rng.gen::<StandardNormal>();
+        norm * (self.dof / self.chi.ind_sample(rng)).sqrt()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use rand::*;
+    use super::*;
+    use iter::range;
+    use option::{Some, None};
+
+    #[test]
+    fn test_chi_squared_one() {
+        let mut chi = ChiSquared::new(1.0);
+        let mut rng = task_rng();
+        for _ in range(0, 1000) {
+            chi.sample(&mut rng);
+            chi.ind_sample(&mut rng);
+        }
+    }
+    #[test]
+    fn test_chi_squared_small() {
+        let mut chi = ChiSquared::new(0.5);
+        let mut rng = task_rng();
+        for _ in range(0, 1000) {
+            chi.sample(&mut rng);
+            chi.ind_sample(&mut rng);
+        }
+    }
+    #[test]
+    fn test_chi_squared_large() {
+        let mut chi = ChiSquared::new(30.0);
+        let mut rng = task_rng();
+        for _ in range(0, 1000) {
+            chi.sample(&mut rng);
+            chi.ind_sample(&mut rng);
+        }
+    }
+    #[test]
+    #[should_fail]
+    fn test_log_normal_invalid_dof() {
+        ChiSquared::new(-1.0);
+    }
+
+    #[test]
+    fn test_f() {
+        let mut f = FisherF::new(2.0, 32.0);
+        let mut rng = task_rng();
+        for _ in range(0, 1000) {
+            f.sample(&mut rng);
+            f.ind_sample(&mut rng);
+        }
+    }
+
+    #[test]
+    fn test_t() {
+        let mut t = StudentT::new(11.0);
+        let mut rng = task_rng();
+        for _ in range(0, 1000) {
+            t.sample(&mut rng);
+            t.ind_sample(&mut rng);
+        }
+    }
+}
+
 #[cfg(test)]
 mod bench {
     use super::*;
diff --git a/src/libstd/rand/distributions/mod.rs b/src/libstd/rand/distributions/mod.rs
index 4778e81f951..a381ac35d30 100644
--- a/src/libstd/rand/distributions/mod.rs
+++ b/src/libstd/rand/distributions/mod.rs
@@ -23,14 +23,18 @@ that do not need to record state.
 use iter::range;
 use option::{Some, None};
 use num;
-use rand::{Rng, Rand, Open01};
+use rand::{Rng, Rand};
 use clone::Clone;
 
 pub use self::range::Range;
-pub use self::gamma::Gamma;
+pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT};
+pub use self::normal::{Normal, LogNormal};
+pub use self::exponential::Exp;
 
 pub mod range;
 pub mod gamma;
+pub mod normal;
+pub mod exponential;
 
 /// Types that can be used to create a random instance of `Support`.
 pub trait Sample<Support> {
@@ -246,181 +250,10 @@ fn ziggurat<R:Rng>(
     }
 }
 
-/// A wrapper around an `f64` to generate N(0, 1) random numbers
-/// (a.k.a.  a standard normal, or Gaussian).
-///
-/// See `Normal` for the general normal distribution. That this has to
-/// be unwrapped before use as an `f64` (using either `*` or
-/// `cast::transmute` is safe).
-///
-/// Implemented via the ZIGNOR variant[1] of the Ziggurat method.
-///
-/// [1]: Jurgen A. Doornik (2005). [*An Improved Ziggurat Method to
-/// Generate Normal Random
-/// Samples*](http://www.doornik.com/research/ziggurat.pdf). Nuffield
-/// College, Oxford
-pub struct StandardNormal(f64);
-
-impl Rand for StandardNormal {
-    fn rand<R:Rng>(rng: &mut R) -> StandardNormal {
-        #[inline]
-        fn pdf(x: f64) -> f64 {
-            ((-x*x/2.0) as f64).exp()
-        }
-        #[inline]
-        fn zero_case<R:Rng>(rng: &mut R, u: f64) -> f64 {
-            // compute a random number in the tail by hand
-
-            // strange initial conditions, because the loop is not
-            // do-while, so the condition should be true on the first
-            // run, they get overwritten anyway (0 < 1, so these are
-            // good).
-            let mut x = 1.0f64;
-            let mut y = 0.0f64;
-
-            while -2.0 * y < x * x {
-                let x_ = *rng.gen::<Open01<f64>>();
-                let y_ = *rng.gen::<Open01<f64>>();
-
-                x = x_.ln() / ziggurat_tables::ZIG_NORM_R;
-                y = y_.ln();
-            }
-
-            if u < 0.0 { x - ziggurat_tables::ZIG_NORM_R } else { ziggurat_tables::ZIG_NORM_R - x }
-        }
-
-        StandardNormal(ziggurat(
-            rng,
-            true, // this is symmetric
-            &ziggurat_tables::ZIG_NORM_X,
-            &ziggurat_tables::ZIG_NORM_F,
-            pdf, zero_case))
-    }
-}
-
-/// The normal distribution `N(mean, std_dev**2)`.
-///
-/// This uses the ZIGNOR variant of the Ziggurat method, see
-/// `StandardNormal` for more details.
-///
-/// # Example
-///
-/// ```
-/// use std::rand;
-/// use std::rand::distributions::{Normal, IndependentSample};
-///
-/// fn main() {
-///     let normal = Normal::new(2.0, 3.0);
-///     let v = normal.ind_sample(rand::task_rng());
-///     println!("{} is from a N(2, 9) distribution", v)
-/// }
-/// ```
-pub struct Normal {
-    priv mean: f64,
-    priv std_dev: f64
-}
-
-impl Normal {
-    /// Construct a new `Normal` distribution with the given mean and
-    /// standard deviation. Fails if `std_dev < 0`.
-    pub fn new(mean: f64, std_dev: f64) -> Normal {
-        assert!(std_dev >= 0.0, "Normal::new called with `std_dev` < 0");
-        Normal {
-            mean: mean,
-            std_dev: std_dev
-        }
-    }
-}
-impl Sample<f64> for Normal {
-    fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
-}
-impl IndependentSample<f64> for Normal {
-    fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
-        self.mean + self.std_dev * (*rng.gen::<StandardNormal>())
-    }
-}
-
-/// A wrapper around an `f64` to generate Exp(1) random numbers.
-///
-/// See `Exp` for the general exponential distribution.Note that this
- // has to be unwrapped before use as an `f64` (using either
-/// `*` or `cast::transmute` is safe).
-///
-/// Implemented via the ZIGNOR variant[1] of the Ziggurat method. The
-/// exact description in the paper was adjusted to use tables for the
-/// exponential distribution rather than normal.
-///
-/// [1]: Jurgen A. Doornik (2005). [*An Improved Ziggurat Method to
-/// Generate Normal Random
-/// Samples*](http://www.doornik.com/research/ziggurat.pdf). Nuffield
-/// College, Oxford
-pub struct Exp1(f64);
-
-// This could be done via `-rng.gen::<f64>().ln()` but that is slower.
-impl Rand for Exp1 {
-    #[inline]
-    fn rand<R:Rng>(rng: &mut R) -> Exp1 {
-        #[inline]
-        fn pdf(x: f64) -> f64 {
-            (-x).exp()
-        }
-        #[inline]
-        fn zero_case<R:Rng>(rng: &mut R, _u: f64) -> f64 {
-            ziggurat_tables::ZIG_EXP_R - rng.gen::<f64>().ln()
-        }
-
-        Exp1(ziggurat(rng, false,
-                      &ziggurat_tables::ZIG_EXP_X,
-                      &ziggurat_tables::ZIG_EXP_F,
-                      pdf, zero_case))
-    }
-}
-
-/// The exponential distribution `Exp(lambda)`.
-///
-/// This distribution has density function: `f(x) = lambda *
-/// exp(-lambda * x)` for `x > 0`.
-///
-/// # Example
-///
-/// ```
-/// use std::rand;
-/// use std::rand::distributions::{Exp, IndependentSample};
-///
-/// fn main() {
-///     let exp = Exp::new(2.0);
-///     let v = exp.ind_sample(rand::task_rng());
-///     println!("{} is from a Exp(2) distribution", v);
-/// }
-/// ```
-pub struct Exp {
-    /// `lambda` stored as `1/lambda`, since this is what we scale by.
-    priv lambda_inverse: f64
-}
-
-impl Exp {
-    /// Construct a new `Exp` with the given shape parameter
-    /// `lambda`. Fails if `lambda <= 0`.
-    pub fn new(lambda: f64) -> Exp {
-        assert!(lambda > 0.0, "Exp::new called with `lambda` <= 0");
-        Exp { lambda_inverse: 1.0 / lambda }
-    }
-}
-
-impl Sample<f64> for Exp {
-    fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
-}
-impl IndependentSample<f64> for Exp {
-    fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
-        (*rng.gen::<Exp1>()) * self.lambda_inverse
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use rand::*;
     use super::*;
-    use iter::range;
     use option::{Some, None};
 
     struct ConstRand(uint);
@@ -449,42 +282,6 @@ mod tests {
         assert_eq!(*rand_sample.sample(&mut task_rng()), 0);
         assert_eq!(*rand_sample.ind_sample(&mut task_rng()), 0);
     }
-
-    #[test]
-    fn test_normal() {
-        let mut norm = Normal::new(10.0, 10.0);
-        let mut rng = task_rng();
-        for _ in range(0, 1000) {
-            norm.sample(&mut rng);
-            norm.ind_sample(&mut rng);
-        }
-    }
-    #[test]
-    #[should_fail]
-    fn test_normal_invalid_sd() {
-        Normal::new(10.0, -1.0);
-    }
-
-    #[test]
-    fn test_exp() {
-        let mut exp = Exp::new(10.0);
-        let mut rng = task_rng();
-        for _ in range(0, 1000) {
-            assert!(exp.sample(&mut rng) >= 0.0);
-            assert!(exp.ind_sample(&mut rng) >= 0.0);
-        }
-    }
-    #[test]
-    #[should_fail]
-    fn test_exp_invalid_lambda_zero() {
-        Exp::new(0.0);
-    }
-    #[test]
-    #[should_fail]
-    fn test_exp_invalid_lambda_neg() {
-        Exp::new(-10.0);
-    }
-
     #[test]
     fn test_weighted_choice() {
         // this makes assumptions about the internal implementation of
@@ -556,38 +353,3 @@ mod tests {
                               Weighted { weight: 1, item: 3 }]);
     }
 }
-
-#[cfg(test)]
-mod bench {
-    use extra::test::BenchHarness;
-    use rand::{XorShiftRng, RAND_BENCH_N};
-    use super::*;
-    use iter::range;
-    use option::{Some, None};
-    use mem::size_of;
-
-    #[bench]
-    fn rand_normal(bh: &mut BenchHarness) {
-        let mut rng = XorShiftRng::new();
-        let mut normal = Normal::new(-2.71828, 3.14159);
-
-        bh.iter(|| {
-            for _ in range(0, RAND_BENCH_N) {
-                normal.sample(&mut rng);
-            }
-        });
-        bh.bytes = size_of::<f64>() as u64 * RAND_BENCH_N;
-    }
-    #[bench]
-    fn rand_exp(bh: &mut BenchHarness) {
-        let mut rng = XorShiftRng::new();
-        let mut exp = Exp::new(2.71828 * 3.14159);
-
-        bh.iter(|| {
-            for _ in range(0, RAND_BENCH_N) {
-                exp.sample(&mut rng);
-            }
-        });
-        bh.bytes = size_of::<f64>() as u64 * RAND_BENCH_N;
-    }
-}
diff --git a/src/libstd/rand/distributions/normal.rs b/src/libstd/rand/distributions/normal.rs
new file mode 100644
index 00000000000..a779f4f6066
--- /dev/null
+++ b/src/libstd/rand/distributions/normal.rs
@@ -0,0 +1,211 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! The normal and derived distributions.
+
+use rand::{Rng, Rand, Open01};
+use rand::distributions::{ziggurat, ziggurat_tables, Sample, IndependentSample};
+
+/// A wrapper around an `f64` to generate N(0, 1) random numbers
+/// (a.k.a.  a standard normal, or Gaussian).
+///
+/// See `Normal` for the general normal distribution. That this has to
+/// be unwrapped before use as an `f64` (using either `*` or
+/// `cast::transmute` is safe).
+///
+/// Implemented via the ZIGNOR variant[1] of the Ziggurat method.
+///
+/// [1]: Jurgen A. Doornik (2005). [*An Improved Ziggurat Method to
+/// Generate Normal Random
+/// Samples*](http://www.doornik.com/research/ziggurat.pdf). Nuffield
+/// College, Oxford
+pub struct StandardNormal(f64);
+
+impl Rand for StandardNormal {
+    fn rand<R:Rng>(rng: &mut R) -> StandardNormal {
+        #[inline]
+        fn pdf(x: f64) -> f64 {
+            ((-x*x/2.0) as f64).exp()
+        }
+        #[inline]
+        fn zero_case<R:Rng>(rng: &mut R, u: f64) -> f64 {
+            // compute a random number in the tail by hand
+
+            // strange initial conditions, because the loop is not
+            // do-while, so the condition should be true on the first
+            // run, they get overwritten anyway (0 < 1, so these are
+            // good).
+            let mut x = 1.0f64;
+            let mut y = 0.0f64;
+
+            while -2.0 * y < x * x {
+                let x_ = *rng.gen::<Open01<f64>>();
+                let y_ = *rng.gen::<Open01<f64>>();
+
+                x = x_.ln() / ziggurat_tables::ZIG_NORM_R;
+                y = y_.ln();
+            }
+
+            if u < 0.0 { x - ziggurat_tables::ZIG_NORM_R } else { ziggurat_tables::ZIG_NORM_R - x }
+        }
+
+        StandardNormal(ziggurat(
+            rng,
+            true, // this is symmetric
+            &ziggurat_tables::ZIG_NORM_X,
+            &ziggurat_tables::ZIG_NORM_F,
+            pdf, zero_case))
+    }
+}
+
+/// The normal distribution `N(mean, std_dev**2)`.
+///
+/// This uses the ZIGNOR variant of the Ziggurat method, see
+/// `StandardNormal` for more details.
+///
+/// # Example
+///
+/// ```rust
+/// use std::rand;
+/// use std::rand::distributions::{Normal, IndependentSample};
+///
+/// fn main() {
+///     // mean 2, standard deviation 3
+///     let normal = Normal::new(2.0, 3.0);
+///     let v = normal.ind_sample(&mut rand::task_rng());
+///     println!("{} is from a N(2, 9) distribution", v)
+/// }
+/// ```
+pub struct Normal {
+    priv mean: f64,
+    priv std_dev: f64
+}
+
+impl Normal {
+    /// Construct a new `Normal` distribution with the given mean and
+    /// standard deviation. Fails if `std_dev < 0`.
+    pub fn new(mean: f64, std_dev: f64) -> Normal {
+        assert!(std_dev >= 0.0, "Normal::new called with `std_dev` < 0");
+        Normal {
+            mean: mean,
+            std_dev: std_dev
+        }
+    }
+}
+impl Sample<f64> for Normal {
+    fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
+}
+impl IndependentSample<f64> for Normal {
+    fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
+        self.mean + self.std_dev * (*rng.gen::<StandardNormal>())
+    }
+}
+
+
+/// The log-normal distribution `ln N(mean, std_dev**2)`.
+///
+/// If `X` is log-normal distributed, then `ln(X)` is `N(mean,
+/// std_dev**2)` distributed.
+///
+/// # Example
+///
+/// ```rust
+/// use std::rand;
+/// use std::rand::distributions::{LogNormal, IndependentSample};
+///
+/// fn main() {
+///     // mean 2, standard deviation 3
+///     let log_normal = LogNormal::new(2.0, 3.0);
+///     let v = normal.ind_sample(&mut rand::task_rng());
+///     println!("{} is from an ln N(2, 9) distribution", v)
+/// }
+/// ```
+pub struct LogNormal {
+    priv norm: Normal
+}
+
+impl LogNormal {
+    /// Construct a new `LogNormal` distribution with the given mean
+    /// and standard deviation. Fails if `std_dev < 0`.
+    pub fn new(mean: f64, std_dev: f64) -> LogNormal {
+        assert!(std_dev >= 0.0, "LogNormal::new called with `std_dev` < 0");
+        LogNormal { norm: Normal::new(mean, std_dev) }
+    }
+}
+impl Sample<f64> for LogNormal {
+    fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
+}
+impl IndependentSample<f64> for LogNormal {
+    fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
+        self.norm.ind_sample(rng).exp()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use rand::*;
+    use super::*;
+    use iter::range;
+    use option::{Some, None};
+
+    #[test]
+    fn test_normal() {
+        let mut norm = Normal::new(10.0, 10.0);
+        let mut rng = task_rng();
+        for _ in range(0, 1000) {
+            norm.sample(&mut rng);
+            norm.ind_sample(&mut rng);
+        }
+    }
+    #[test]
+    #[should_fail]
+    fn test_normal_invalid_sd() {
+        Normal::new(10.0, -1.0);
+    }
+
+
+    #[test]
+    fn test_log_normal() {
+        let mut lnorm = LogNormal::new(10.0, 10.0);
+        let mut rng = task_rng();
+        for _ in range(0, 1000) {
+            lnorm.sample(&mut rng);
+            lnorm.ind_sample(&mut rng);
+        }
+    }
+    #[test]
+    #[should_fail]
+    fn test_log_normal_invalid_sd() {
+        LogNormal::new(10.0, -1.0);
+    }
+}
+
+#[cfg(test)]
+mod bench {
+    use extra::test::BenchHarness;
+    use rand::{XorShiftRng, RAND_BENCH_N};
+    use super::*;
+    use iter::range;
+    use option::{Some, None};
+    use mem::size_of;
+
+    #[bench]
+    fn rand_normal(bh: &mut BenchHarness) {
+        let mut rng = XorShiftRng::new();
+        let mut normal = Normal::new(-2.71828, 3.14159);
+
+        bh.iter(|| {
+            for _ in range(0, RAND_BENCH_N) {
+                normal.sample(&mut rng);
+            }
+        });
+        bh.bytes = size_of::<f64>() as u64 * RAND_BENCH_N;
+    }
+}