r2rs-nmath 0.1.1

Statistics programming for Rust based on R's nmath package
Documentation
mod d;
mod p;
mod q;
mod r;

use strafe_type::{Finite64, FloatConstraint, LogProbability64, Probability64, Real64};

pub(crate) use self::{d::*, p::*, q::*, r::*};
use crate::traits::{Distribution, RNG};

/// # The Uniform Distribution
///
/// ## Description
///
/// These functions provide information about the uniform distribution on the interval from min to
/// max. dunif gives the density, punif gives the distribution function qunif gives the quantile
/// function and runif generates random deviates.
///
/// ## Arguments
///
/// * min, max: lower and upper limits of the distribution. Must be finite.
///
/// ## Details
///
/// If min or max are not specified they assume the default values of 0 and 1 respectively.
///
/// The uniform distribution has density
///
/// $ f(x) = \frac{1}{\text{max}-\text{min}} $
///
/// for min ≤ x ≤ max.
///
/// For the case of u := min == max, the limit case of X == u is assumed, although there is no
/// density in that case and dunif will return NaN (the error condition).
///
/// runif will not generate either of the extreme values unless max = min or max-min is small
/// compared to min, and in particular not for the default arguments.
///
/// ## Density Plot
///
/// ```rust
/// # use r2rs_base::traits::StatisticalSlice;
/// # use r2rs_nmath::{distribution::UniformBuilder, traits::Distribution};
/// # use strafe_plot::prelude::{IntoDrawingArea, Line, Plot, PlotOptions, SVGBackend, BLACK};
/// # use strafe_type::FloatConstraint;
/// let unif = UniformBuilder::new().build();
/// let x = <[f64]>::sequence(-1.0, 2.0, 1000);
/// let y = x
///     .iter()
///     .map(|x| unif.density(x).unwrap())
///     .collect::<Vec<_>>();
///
/// let root = SVGBackend::new("density.svg", (1024, 768)).into_drawing_area();
/// Plot::new()
///     .with_options(PlotOptions {
///         x_axis_label: "x".to_string(),
///         y_axis_label: "density".to_string(),
///         ..Default::default()
///     })
///     .with_plottable(Line {
///         x,
///         y,
///         color: BLACK,
///         ..Default::default()
///     })
///     .plot(&root)
///     .unwrap();
/// # use std::fs::rename;
/// #     drop(root);
/// #     rename(
/// #             format!("density.svg"),
/// #             format!("src/distribution/unif/doctest_out/density.svg"),
/// #     )
/// #     .unwrap();
/// ```
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = embed_doc_image::embed_image!("density", "src/distribution/unif/doctest_out/density.svg")))]
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = "![Density][density]"))]
///
/// ## Note
///
/// The characteristics of output from pseudo-random number generators (such as precision and
/// periodicity) vary widely. See .Random.seed for more information on R's random number generation
/// algorithms.
///
/// ## References
///
/// Becker, R. A., Chambers, J. M. and Wilks, A. R. (1988) The New S Language. Wadsworth &
/// Brooks/Cole.
///
/// ## See Also
///
/// RNG about random number generation in R.
///
/// Distributions for other standard distributions.
///
/// ## Examples
/// These relations always hold
/// ```rust
/// # use r2rs_nmath::{
/// #     distribution::UniformBuilder,
/// #     rng::MersenneTwister,
/// #     traits::{Distribution, RNG},
/// # };
/// # use strafe_type::FloatConstraint;
/// let unif = UniformBuilder::new().build();
///
/// let mut rng = MersenneTwister::new();
/// rng.set_seed(1);
///
/// let u = (0..20)
///     .map(|_| unif.random_sample(&mut rng).unwrap())
///     .collect::<Vec<_>>();
///
/// let p = u
///     .iter()
///     .map(|&u| unif.probability(u, true).unwrap() == u)
///     .collect::<Vec<_>>();
/// println!("{p:?}");
/// let d = u
///     .iter()
///     .map(|&u| unif.density(u).unwrap() == 1.0)
///     .collect::<Vec<_>>();
/// println!("{d:?}");
/// # use std::{fs::File, io::Write};
/// # let mut f = File::create("src/distribution/unif/doctest_out/pd.md").unwrap();
/// # writeln!(f, "```output").unwrap();
/// # writeln!(f, "{p:?}").unwrap();
/// # writeln!(f, "{d:?}").unwrap();
/// # writeln!(f, "```").unwrap();
/// ```
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = include_str!("doctest_out/pd.md")))]
///
/// ~ = 1/12 = .08333
/// ```rust
/// # use r2rs_nmath::{
/// #     distribution::UniformBuilder,
/// #     rng::MersenneTwister,
/// #     traits::{Distribution, RNG},
/// # };
/// # use r2rs_stats::funcs::variance;
/// # use strafe_type::FloatConstraint;
/// let unif = UniformBuilder::new().build();
///
/// let mut rng = MersenneTwister::new();
/// rng.set_seed(1);
///
/// let r = (0..10_000)
///     .map(|_| unif.random_sample(&mut rng).unwrap())
///     .collect::<Vec<_>>();
/// println!("{}", variance(&r));
/// # use std::{fs::File, io::Write};
/// # let mut f = File::create("src/distribution/unif/doctest_out/rand_var.md").unwrap();
/// # writeln!(f, "```output").unwrap();
/// # writeln!(f, "{}", variance(&r)).unwrap();
/// # writeln!(f, "```").unwrap();
/// ```
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = include_str!("doctest_out/rand_var.md")))]
pub struct Uniform {
    lower_bound: Finite64,
    upper_bound: Finite64,
}

impl Distribution for Uniform {
    fn density<R: Into<Real64>>(&self, x: R) -> Real64 {
        dunif(x, self.lower_bound, self.upper_bound, false)
    }

    fn log_density<R: Into<Real64>>(&self, x: R) -> Real64 {
        dunif(x, self.lower_bound, self.upper_bound, true)
    }

    fn probability<R: Into<Real64>>(&self, q: R, lower_tail: bool) -> Probability64 {
        punif(q, self.lower_bound, self.upper_bound, lower_tail)
    }

    fn log_probability<R: Into<Real64>>(&self, q: R, lower_tail: bool) -> LogProbability64 {
        log_punif(q, self.lower_bound, self.upper_bound, lower_tail)
    }

    fn quantile<P: Into<Probability64>>(&self, p: P, lower_tail: bool) -> Real64 {
        qunif(p, self.lower_bound, self.upper_bound, lower_tail)
    }

    fn log_quantile<LP: Into<LogProbability64>>(&self, p: LP, lower_tail: bool) -> Real64 {
        log_qunif(p, self.lower_bound, self.upper_bound, lower_tail)
    }

    fn random_sample<R: RNG>(&self, rng: &mut R) -> Real64 {
        runif(self.lower_bound, self.upper_bound, rng)
    }
}

pub struct UniformBuilder {
    lower_bound: Option<Finite64>,
    upper_bound: Option<Finite64>,
}

impl UniformBuilder {
    pub fn new() -> Self {
        Self {
            lower_bound: None,
            upper_bound: None,
        }
    }

    pub fn with_bounds<F1: Into<Finite64>, F2: Into<Finite64>>(
        &mut self,
        bound1: F1,
        bound2: F2,
    ) -> &mut Self {
        let bound1 = bound1.into();
        let bound2 = bound2.into();
        if bound1.unwrap() < bound2.unwrap() {
            self.lower_bound = Some(bound1);
            self.upper_bound = Some(bound2);
        } else {
            self.lower_bound = Some(bound2);
            self.upper_bound = Some(bound1);
        }
        self
    }

    pub fn build(&self) -> Uniform {
        let lower_bound = self.lower_bound.unwrap_or(0.0.into());
        let upper_bound = self.upper_bound.unwrap_or(1.0.into());

        Uniform {
            lower_bound,
            upper_bound,
        }
    }
}

#[cfg(test)]
mod tests;

#[cfg(all(test, feature = "enable_proptest"))]
mod proptests;

#[cfg(all(test, feature = "enable_covtest"))]
mod covtests;