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 std::str::FromStr;

use strafe_testing::{
    r::{RString, RTester},
    r_assert_relative_equal, r_assert_relative_equal_result,
};
use strafe_type::FloatConstraint;

use crate::{
    distribution::hyper::HyperGeometricBuilder,
    reset_statics,
    rng::MarsagliaMulticarry,
    traits::{Distribution, RNG},
};

pub fn density_inner(x: f64, group_1: f64, group_2: f64, number_drawn: f64) {
    let mut builder = HyperGeometricBuilder::new();
    builder.with_group_1(group_1);
    builder.with_group_2(group_2);
    builder.with_number_drawn(number_drawn);
    let hyper = builder.build().expect("Build error");

    let rust_ans = hyper.density(x);
    let r_ans = RTester::new()
        .set_display(&RString::from_string(format!(
            "dhyper({}, {}, {}, {}, log={})",
            RString::from_f64(x),
            RString::from_f64(group_1),
            RString::from_f64(group_2),
            RString::from_f64(number_drawn),
            RString::from_bool(false),
        )))
        .run()
        .expect("R running error")
        .as_f64();

    r_assert_relative_equal_result!(r_ans, &rust_ans);
}

pub fn log_density_inner(x: f64, group_1: f64, group_2: f64, number_drawn: f64) {
    let mut builder = HyperGeometricBuilder::new();
    builder.with_group_1(group_1);
    builder.with_group_2(group_2);
    builder.with_number_drawn(number_drawn);
    let hyper = builder.build().expect("Build error");

    let rust_ans = hyper.log_density(x);
    let r_ans = RTester::new()
        .set_display(&RString::from_string(format!(
            "dhyper({}, {}, {}, {}, log={})",
            RString::from_f64(x),
            RString::from_f64(group_1),
            RString::from_f64(group_2),
            RString::from_f64(number_drawn),
            RString::from_bool(true),
        )))
        .run()
        .expect("R running error")
        .as_f64();

    r_assert_relative_equal_result!(r_ans, &rust_ans);
}

pub fn probability_inner(q: f64, group_1: f64, group_2: f64, number_drawn: f64, lower_tail: bool) {
    let mut builder = HyperGeometricBuilder::new();
    builder.with_group_1(group_1);
    builder.with_group_2(group_2);
    builder.with_number_drawn(number_drawn);
    let hyper = builder.build().expect("Build error");

    let rust_ans = hyper.probability(q, lower_tail);
    let r_ans = RTester::new()
        .set_display(&RString::from_string(format!(
            "phyper({}, {}, {}, {}, lower.tail={}, log={})",
            RString::from_f64(q),
            RString::from_f64(group_1),
            RString::from_f64(group_2),
            RString::from_f64(number_drawn),
            RString::from_bool(lower_tail),
            RString::from_bool(false),
        )))
        .run()
        .expect("R running error")
        .as_f64();

    r_assert_relative_equal_result!(r_ans, &rust_ans);
}

pub fn log_probability_inner(
    q: f64,
    group_1: f64,
    group_2: f64,
    number_drawn: f64,
    lower_tail: bool,
) {
    let mut builder = HyperGeometricBuilder::new();
    builder.with_group_1(group_1);
    builder.with_group_2(group_2);
    builder.with_number_drawn(number_drawn);
    let hyper = builder.build().expect("Build error");

    let rust_ans = hyper.log_probability(q, lower_tail);
    let r_ans = RTester::new()
        .set_display(&RString::from_string(format!(
            "phyper({}, {}, {}, {}, lower.tail={}, log={})",
            RString::from_f64(q),
            RString::from_f64(group_1),
            RString::from_f64(group_2),
            RString::from_f64(number_drawn),
            RString::from_bool(lower_tail),
            RString::from_bool(true),
        )))
        .run()
        .expect("R running error")
        .as_f64();

    r_assert_relative_equal_result!(r_ans, &rust_ans);
}

pub fn quantile_inner(p: f64, group_1: f64, group_2: f64, number_drawn: f64, lower_tail: bool) {
    let mut builder = HyperGeometricBuilder::new();
    builder.with_group_1(group_1);
    builder.with_group_2(group_2);
    builder.with_number_drawn(number_drawn);
    let hyper = builder.build().expect("Build error");

    let rust_ans = hyper.quantile(p, lower_tail);
    let r_ans = RTester::new()
        .set_display(&RString::from_string(format!(
            "qhyper({}, {}, {}, {}, lower.tail={}, log={})",
            RString::from_f64(p),
            RString::from_f64(group_1),
            RString::from_f64(group_2),
            RString::from_f64(number_drawn),
            RString::from_bool(lower_tail),
            RString::from_bool(false),
        )))
        .run()
        .expect("R running error")
        .as_f64();

    r_assert_relative_equal_result!(r_ans, &rust_ans);
}

pub fn log_quantile_inner(p: f64, group_1: f64, group_2: f64, number_drawn: f64, lower_tail: bool) {
    let mut builder = HyperGeometricBuilder::new();
    builder.with_group_1(group_1);
    builder.with_group_2(group_2);
    builder.with_number_drawn(number_drawn);
    let hyper = builder.build().expect("Build error");

    let rust_ans = hyper.log_quantile(p, lower_tail);
    let r_ans = RTester::new()
        .set_display(&RString::from_string(format!(
            "qhyper({}, {}, {}, {}, lower.tail={}, log={})",
            RString::from_f64(p),
            RString::from_f64(group_1),
            RString::from_f64(group_2),
            RString::from_f64(number_drawn),
            RString::from_bool(lower_tail),
            RString::from_bool(true),
        )))
        .run()
        .expect("R running error")
        .as_f64();

    r_assert_relative_equal_result!(r_ans, &rust_ans);
}

pub fn random_inner(seed: u16, group_1: f64, group_2: f64, number_drawn: f64) {
    let num = 50;

    let r_state = RTester::new()
        .set_seed(seed)
        .set_display(&RString::from_str(".Random.seed").unwrap())
        .run()
        .unwrap()
        .as_f64_vec()
        .unwrap()
        .into_iter()
        .skip(1)
        .map(|i| i as i32)
        .collect::<Vec<_>>();

    let mut rust_rng = MarsagliaMulticarry::new();
    rust_rng.set_seed(seed as u32);

    let rust_state = rust_rng.get_state();

    for (r, rust) in r_state.iter().zip(rust_state.iter()) {
        assert_eq!(r, rust, "States not equal!");
    }

    let r_ret = RTester::new()
        .set_seed(seed)
        .set_display(&RString::from_string(format!(
            "rhyper({}, {}, {}, {})",
            RString::from_f64(num),
            RString::from_f64(group_1),
            RString::from_f64(group_2),
            RString::from_f64(number_drawn),
        )))
        .run()
        .unwrap()
        .as_f64_vec()
        .unwrap();

    let mut builder = HyperGeometricBuilder::new();
    builder.with_group_1(group_1);
    builder.with_group_2(group_2);
    builder.with_number_drawn(number_drawn);
    let hyper = builder.build().expect("Build error");
    reset_statics();
    let rust_ret = (0..num)
        .map(|_| hyper.random_sample(&mut rust_rng))
        .collect::<Vec<_>>();

    for (r, rust) in r_ret.iter().zip(rust_ret.iter()) {
        r_assert_relative_equal!(*r, rust.unwrap());
    }
}