r2rs-nmath 0.1.1

Statistics programming for Rust based on R's nmath package
Documentation
// "Whatever you do, work at it with all your heart, as working for the Lord,
// not for human masters, since you know that you will receive an inheritance
// from the Lord as a reward. It is the Lord Christ you are serving."
// (Col 3:23-24)

use strafe_type::{LogProbability64, Natural64, Probability64, Real64};

use crate::{
    distribution::birthday::{p::pbirthday, q::qbirthday},
    traits::{Distribution, RNG},
};

mod p;
mod q;

/// # Probability of coincidences
///
/// ## Description:
///
/// Computes answers to a generalised _birthday paradox_ problem.
/// ‘probability’ computes the probability of a coincidence and
/// ‘quantile’ computes the smallest number of observations needed to
/// have at least a specified probability of coincidence.
///
/// Usage:
///
/// ```rust
/// # use r2rs_nmath::distribution::BirthdayBuilder;
/// # use r2rs_nmath::traits::Distribution;
/// let birthday = BirthdayBuilder::new()
///     .with_classes(365)
///     .with_coincident(2)
///     .build();
/// birthday.quantile(0.5, true);
/// birthday.probability(12, true);
/// ```
///
/// Arguments:
///
/// * classes: How many distinct categories the people could fall into
/// * coincident: The number of people to fall in the same category
///
/// ## Details:
///
/// The birthday paradox is that a very small number of people, 23,
/// suffices to have a 50-50 chance that two or more of them have the
/// same birthday.  This function generalises the calculation to
/// probabilities other than 0.5, numbers of coincident events other
/// than 2, and numbers of classes other than 365.
///
/// The formula used is approximate for ‘coincident > 2’.  The
/// approximation is very good for moderate values of ‘prob’ but less
/// good for very small probabilities.
///
/// ## Value:
///
/// * quantile: Minimum number of people needed for a probability of at
/// least ‘prob’ that ‘k’ or more of them have the same one out
/// of ‘classes’ equiprobable labels.
/// * probability: Probability of the specified coincidence.
///
/// ## References:
///
/// Diaconis, P. and Mosteller F. (1989).  Methods for studying
/// coincidences.  _Journal of the American Statistical Association_,
/// *84*, 853-861.  doi:10.1080/01621459.1989.10478847
/// <https://doi.org/10.1080/01621459.1989.10478847>.
///
/// ## Examples:
///
/// The standard version
/// ```rust
/// # use r2rs_nmath::distribution::BirthdayBuilder;
/// # use r2rs_nmath::traits::Distribution;
/// let birthday = BirthdayBuilder::new().build();
/// let quan = birthday.quantile(0.5, true);
/// println!("{quan}");
/// # use std::{fs::File, io::Write};
/// # let mut f = File::create("src/distribution/birthday/doctest_out/birthday_default.md").unwrap();
/// # writeln!(f, "```output").unwrap();
/// # writeln!(f, "{quan}").unwrap();
/// # writeln!(f, "```").unwrap();
/// ```
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = include_str!("doctest_out/birthday_default.md")))]
///
/// Probability of > 2 people with the same birthday
/// ```rust
/// # use r2rs_nmath::distribution::BirthdayBuilder;
/// # use r2rs_nmath::traits::Distribution;
/// let birthday = BirthdayBuilder::new().with_coincident(3).build();
/// let prob = birthday.probability(23, true);
/// println!("{prob}");
/// # use std::{fs::File, io::Write};
/// # let mut f = File::create("src/distribution/birthday/doctest_out/more_than_2.md").unwrap();
/// # writeln!(f, "```output").unwrap();
/// # writeln!(f, "{prob}").unwrap();
/// # writeln!(f, "```").unwrap();
/// ```
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = include_str!("doctest_out/more_than_2.md")))]
///
/// Examples from Diaconis & Mosteller p. 858.
///
/// 'Coincidence' is that husband, wife, daughter all born on the 16th
/// ```rust
/// # use r2rs_nmath::distribution::BirthdayBuilder;
/// # use r2rs_nmath::traits::Distribution;
/// let quan = BirthdayBuilder::new()
///     .with_classes(30)
///     .with_coincident(3)
///     .build()
///     .quantile(0.5, true);
/// println!("{quan}");
/// # use std::{fs::File, io::Write};
/// # let mut f = File::create("src/distribution/birthday/doctest_out/all_born.md").unwrap();
/// # writeln!(f, "```output").unwrap();
/// # writeln!(f, "{quan}").unwrap();
/// # writeln!(f, "```").unwrap();
/// ```
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = include_str!("doctest_out/all_born.md")))]
///
/// Same 4-digit PIN number
/// ```rust
/// # use r2rs_nmath::distribution::BirthdayBuilder;
/// # use r2rs_nmath::traits::Distribution;
/// let birthday = BirthdayBuilder::new()
///     .with_classes(10.0_f64.powi(4))
///     .build();
/// let quan = birthday.quantile(0.5, true);
/// println!("{quan}");
/// # use std::{fs::File, io::Write};
/// # let mut f = File::create("src/distribution/birthday/doctest_out/pin.md").unwrap();
/// # writeln!(f, "```output").unwrap();
/// # writeln!(f, "{quan}").unwrap();
/// # writeln!(f, "```").unwrap();
/// ```
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = include_str!("doctest_out/pin.md")))]
///
/// 0.9 probability of three or more coincident birthdays
/// ```rust
/// # use r2rs_nmath::distribution::BirthdayBuilder;
/// # use r2rs_nmath::traits::Distribution;
/// let birthday = BirthdayBuilder::new().with_coincident(3).build();
/// let quan = birthday.quantile(0.9, true);
/// println!("{quan}");
/// # use std::{fs::File, io::Write};
/// # let mut f = File::create("src/distribution/birthday/doctest_out/point_nine.md").unwrap();
/// # writeln!(f, "```output").unwrap();
/// # writeln!(f, "{quan}").unwrap();
/// # writeln!(f, "```").unwrap();
/// ```
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = include_str!("doctest_out/point_nine.md")))]
///
/// Chance of 4 or more coincident birthdays in 150 people
/// ```rust
/// # use r2rs_nmath::distribution::BirthdayBuilder;
/// # use r2rs_nmath::traits::Distribution;
/// let birthday = BirthdayBuilder::new().with_coincident(4).build();
/// let prob = birthday.probability(150, true);
/// println!("{prob}");
/// # use std::{fs::File, io::Write};
/// # let mut f = File::create("src/distribution/birthday/doctest_out/one_fifty.md").unwrap();
/// # writeln!(f, "```output").unwrap();
/// # writeln!(f, "{prob}").unwrap();
/// # writeln!(f, "```").unwrap();
/// ```
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = include_str!("doctest_out/one_fifty.md")))]
///
/// 100 or more coincident birthdays in 1000 people: very rare
/// ```rust
/// # use r2rs_nmath::distribution::BirthdayBuilder;
/// # use r2rs_nmath::traits::Distribution;
/// # use strafe_type::FloatConstraint;
/// let birthday = BirthdayBuilder::new().with_coincident(100).build();
/// let prob = birthday.probability(1000, true).unwrap();
/// println!("{prob:e}");
/// # use std::{fs::File, io::Write};
/// # let mut f = File::create("src/distribution/birthday/doctest_out/thousand.md").unwrap();
/// # writeln!(f, "```output").unwrap();
/// # writeln!(f, "{prob:e}").unwrap();
/// # writeln!(f, "```").unwrap();
/// ```
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = include_str!("doctest_out/thousand.md")))]
pub struct Birthday {
    classes: Natural64,
    coincident: Natural64,
}

impl Distribution for Birthday {
    fn density<R: Into<Real64>>(&self, _x: R) -> Real64 {
        unimplemented!()
    }

    fn log_density<R: Into<Real64>>(&self, _x: R) -> Real64 {
        unimplemented!()
    }

    fn probability<R: Into<Real64>>(&self, q: R, _lower_tail: bool) -> Probability64 {
        pbirthday(q, self.classes, self.coincident)
    }

    fn log_probability<R: Into<Real64>>(&self, _q: R, _lower_tail: bool) -> LogProbability64 {
        unimplemented!()
    }

    fn quantile<P: Into<Probability64>>(&self, p: P, _lower_tail: bool) -> Real64 {
        qbirthday(p, self.classes, self.coincident)
    }

    fn log_quantile<LP: Into<LogProbability64>>(&self, _p: LP, _lower_tail: bool) -> Real64 {
        unimplemented!()
    }

    fn random_sample<R: RNG>(&self, _rng: &mut R) -> Real64 {
        unimplemented!()
    }
}

pub struct BirthdayBuilder {
    classes: Option<Natural64>,
    coincident: Option<Natural64>,
}

impl BirthdayBuilder {
    pub fn new() -> Self {
        Self {
            classes: None,
            coincident: None,
        }
    }

    pub fn with_classes<N: Into<Natural64>>(&mut self, classes: N) -> &mut Self {
        self.classes = Some(classes.into());
        self
    }

    pub fn with_coincident<N: Into<Natural64>>(&mut self, coincident: N) -> &mut Self {
        self.coincident = Some(coincident.into());
        self
    }

    pub fn build(&self) -> Birthday {
        let classes = self.classes.unwrap_or(365.0.into());
        let coincident = self.coincident.unwrap_or(2.0.into());

        Birthday {
            classes,
            coincident,
        }
    }
}

#[cfg(test)]
mod tests;

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

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