use cas_attrs::builtin;
use crate::consts::{E, ONE, PI, TAU, TWO, ZERO};
use crate::primitive::{float, float_from_str, int};
use once_cell::sync::Lazy;
use rug::{float::Special, ops::Pow, Float, Integer};
use super::miscellaneous::partial_factorial;
#[derive(Debug)]
pub struct Erf;
#[cfg_attr(feature = "numerical", builtin)]
impl Erf {
pub fn eval_static(x: Float) -> Float {
x.erf()
}
}
#[derive(Debug)]
pub struct Erfc;
#[cfg_attr(feature = "numerical", builtin)]
impl Erfc {
pub fn eval_static(x: Float) -> Float {
x.erfc()
}
}
static INV_ERF_A: Lazy<[Float; 4]> = Lazy::new(|| [
float_from_str("0.886226899"),
float_from_str("-1.645349621"),
float_from_str("0.914624893"),
float_from_str("-0.140543331"),
]);
static INV_ERF_B: Lazy<[Float; 5]> = Lazy::new(|| [
float(&*ONE),
float_from_str("-2.118377725"),
float_from_str("1.442710462"),
float_from_str("-0.329097515"),
float_from_str("0.012229801"),
]);
static INV_ERF_C: Lazy<[Float; 4]> = Lazy::new(|| [
float_from_str("-1.970840454"),
float_from_str("-1.62490649"),
float_from_str("3.429567803"),
float_from_str("1.641345311"),
]);
static INV_ERF_D: Lazy<[Float; 3]> = Lazy::new(|| [
float(&*ONE),
float_from_str("3.543889200"),
float_from_str("1.637067800"),
]);
#[derive(Debug)]
pub struct Inverf;
#[cfg_attr(feature = "numerical", builtin)]
impl Inverf {
pub fn eval_static(x: Float) -> Float {
if x <= -1 {
return float(Special::NegInfinity);
} else if x >= 1 {
return float(Special::Infinity);
}
let mut z = float(x.abs_ref());
let fold_arr = |arr: &[Float], factor: &Float| {
(1..arr.len())
.rev()
.fold(float(&arr[arr.len() - 1]), |acc, i| {
acc * factor + float(&arr[i - 1])
})
};
let mut r = if z <= 0.7 {
let x2 = float(z.square_ref());
let num = &z * fold_arr(&*INV_ERF_A, &x2);
let den = fold_arr(&*INV_ERF_B, &x2);
num / den
} else {
let y = {
let inner_log = (&*ONE - float(&z)) / &*TWO;
(-inner_log.ln()).sqrt()
};
let num = fold_arr(&*INV_ERF_C, &y);
let den = fold_arr(&*INV_ERF_D, &y);
num / den
};
r = if x.is_sign_negative() { -r } else { r };
z = if x.is_sign_negative() { -z } else { z };
let f = 2 / float(&*PI).sqrt();
r -= (float(r.erf_ref()) - &z) / (&f * float((-float(r.square_ref())).exp_ref()));
r -= (float(r.erf_ref()) - &z) / (&f * float((-float(r.square_ref())).exp_ref()));
r
}
}
#[derive(Debug)]
pub struct Normpdf;
#[cfg_attr(feature = "numerical", builtin)]
impl Normpdf {
pub fn eval_static(x: Float, m: Option<Float>, s: Option<Float>) -> Float {
let m = m.unwrap_or_else(|| float(&*ZERO));
let s = s.unwrap_or_else(|| float(&*ONE));
let exp_arg = Float::exp(float(x - m).square() / (-2 * float(s.square_ref())));
let bot = s * float(TAU.sqrt_ref());
exp_arg / bot
}
}
#[derive(Debug)]
pub struct Normcdf;
#[cfg_attr(feature = "numerical", builtin)]
impl Normcdf {
pub fn eval_static(a: Float, b: Float, m: Option<Float>, s: Option<Float>) -> Float {
let m = m.unwrap_or_else(|| float(&*ZERO));
let s = s.unwrap_or_else(|| float(&*ONE));
let sqrt_two = float(TWO.sqrt_ref());
let z_a = (a - &m) / float(&s * &sqrt_two);
let z_b = (b - &m) / float(&s * &sqrt_two);
(z_b.erf() - z_a.erf()) / &*TWO
}
}
#[derive(Debug)]
pub struct Invnorm;
#[cfg_attr(feature = "numerical", builtin)]
impl Invnorm {
pub fn eval_static(p: Float, m: Option<Float>, s: Option<Float>) -> Float {
let m = m.unwrap_or_else(|| float(&*ZERO));
let s = s.unwrap_or_else(|| float(&*ONE));
let sqrt_two = float(TWO.sqrt_ref());
let z = sqrt_two * Inverf::eval_static(2 * p - 1);
m + s * z
}
}
#[derive(Debug)]
pub struct Geompdf;
#[cfg_attr(feature = "numerical", builtin)]
impl Geompdf {
pub fn eval_static(p: Float, n: Integer) -> Float {
if n <= *ZERO {
return float(&*ZERO);
}
let q = float(&*ONE - &p);
p * q.pow(n - 1)
}
}
#[derive(Debug)]
pub struct Geomcdf;
#[cfg_attr(feature = "numerical", builtin)]
impl Geomcdf {
pub fn eval_static(p: Float, n: Integer) -> Float {
if n <= *ZERO {
return float(&*ZERO);
}
let q = float(&*ONE - &p);
&*ONE - q.pow(n)
}
}
#[derive(Debug)]
pub struct Binompdf;
#[cfg_attr(feature = "numerical", builtin)]
impl Binompdf {
pub fn eval_static(n: Integer, p: Float, x: Integer) -> Float {
if x < *ZERO || x > n {
return float(&*ZERO);
}
let q = float(&*ONE - &p);
let c = q.pow(n.clone() - &x);
let b = p.pow(&x);
let a = super::combinatoric::Ncr::eval_static(n, x);
a * b * c
}
}
#[derive(Debug)]
pub struct Binomcdf;
#[cfg_attr(feature = "numerical", builtin)]
impl Binomcdf {
pub fn eval_static(n: Integer, p: Float, mut x: Integer) -> Float {
if x < *ZERO {
return float(&*ZERO);
} else if x >= n {
return float(&*ONE);
}
let mut sum = float(&*ZERO);
while x >= *ZERO {
sum += Binompdf::eval_static(n.clone(), p.clone(), x.clone());
x -= 1;
}
sum
}
}
#[derive(Debug)]
pub struct Poisspdf;
#[cfg_attr(feature = "numerical", builtin)]
impl Poisspdf {
pub fn eval_static(k: Integer, l: Float) -> Float {
if k < *ZERO {
return float(&*ZERO);
}
let b = float(&*E).pow(&*l.as_neg());
let a = l.pow(&k);
let c = partial_factorial(k, int(1));
a * b / c
}
}
#[derive(Debug)]
pub struct Poisscdf;
#[cfg_attr(feature = "numerical", builtin)]
impl Poisscdf {
pub fn eval_static(k: Integer, l: Float) -> Float {
if k < *ZERO {
return float(&*ZERO);
}
fn reg_gamma(s: Float, x: Float) -> Float {
float(s.gamma_inc_ref(&x)) / s.gamma()
}
reg_gamma(float(k + 1), l)
}
}