use libm::{erff, lgammaf};
use rand::Rng;
use rand::rngs::ThreadRng;
use rand_distr::StandardNormal;
use std::f32;
pub fn logistic(x: f32) -> f32 {
1.0 / (1.0 + (-x).exp())
}
pub fn odds_to_prob(q: f32) -> f32 {
q / (1.0 + q)
}
const LN_SQRT_TWO_PI: f32 = 0.918_938_5_f32;
pub fn rand_crt(rng: &mut ThreadRng, n: u32, r: f32) -> u32 {
(0..n)
.map(|t| rng.random_bool(r as f64 / (r as f64 + t as f64)) as u32)
.sum()
}
pub fn negbin_logpmf(r: f32, lgamma_r: f32, p: f32, k: u32) -> f32 {
const MINP: f32 = 0.999999_f32;
let p = p.min(MINP);
if k == 0 {
r * (-p).ln_1p()
} else {
let k_ln_factorial = lgammaf(k as f32 + 1.0);
let lgamma_rpk = lgammaf(r + k as f32);
lgamma_rpk - lgamma_r - k_ln_factorial + (k as f32) * p.ln() + r * (-p).ln_1p()
}
}
pub fn normal_logpdf(μ: f32, σ: f32, x: f32) -> f32 {
-LN_SQRT_TWO_PI - σ.ln() - ((x - μ) / σ).powi(2) / 2.0
}
pub fn randn(rng: &mut ThreadRng) -> f32 {
rng.sample::<f32, StandardNormal>(StandardNormal)
}
pub fn halfnormal_logpdf(σ: f32, x: f32) -> f32 {
-LN_SQRT_TWO_PI - σ.ln() - x.powi(2) / (2.0 * σ.powi(2))
}
fn erfint(span: f32, σ: f32) -> f32 {
-span * erff(span / (f32::consts::SQRT_2 * σ))
- (f32::consts::SQRT_2 * f32::consts::FRAC_2_SQRT_PI / 2.0)
* σ
* (-span.powi(2) / (2.0 * σ.powi(2))).exp()
}
pub fn uniformly_imprecise_normal_prob(a: f32, b: f32, a0: f32, b0: f32, σ: f32) -> f32 {
0.5 * (b0 - a0).recip()
* (b - a).recip()
* (erfint(b - b0, σ) + erfint(a - a0, σ) - erfint(b - a0, σ) - erfint(a - b0, σ))
}