use super::super::{Cdf, Moments, Pdf, Quantile, Sample, bisection_quantile};
use super::gamma::marsaglia_tsang;
use crate::distributions::BetaDistribution;
use crate::rng::SplitMix64;
use crate::special::{betai, ln_beta};
impl Pdf for BetaDistribution {
fn pdf(&self, x: f64) -> f64 {
if !(0.0..=1.0).contains(&x) {
return 0.0;
}
let (a, b) = (self.alpha_parameter, self.beta_parameter);
let ln_density = (a - 1.0).mul_add(x.ln(), (b - 1.0) * (1.0 - x).ln()) - ln_beta(a, b);
ln_density.exp()
}
}
impl Cdf for BetaDistribution {
fn cdf(&self, x: f64) -> f64 {
betai(self.alpha_parameter, self.beta_parameter, x)
}
}
impl Quantile for BetaDistribution {
fn quantile(&self, p: f64) -> f64 {
bisection_quantile(p, 0.0, 1.0, |x| self.cdf(x))
}
}
impl Moments for BetaDistribution {
fn mean(&self) -> Option<f64> {
let (a, b) = (self.alpha_parameter, self.beta_parameter);
Some(a / (a + b))
}
fn variance(&self) -> Option<f64> {
let (a, b) = (self.alpha_parameter, self.beta_parameter);
let s = a + b;
let denom = s * s * (s + 1.0);
Some((a * b) / denom)
}
}
impl Sample for BetaDistribution {
fn sample(&self, rng: &mut SplitMix64) -> f64 {
let x = marsaglia_tsang(self.alpha_parameter, rng);
let y = marsaglia_tsang(self.beta_parameter, rng);
x / (x + y)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn beta_one_one_is_uniform() {
let d = BetaDistribution {
alpha_parameter: 1.0,
beta_parameter: 1.0,
..Default::default()
};
assert!((d.pdf(0.3) - 1.0).abs() < 1e-12, "was {}", d.pdf(0.3));
assert!((d.cdf(0.4) - 0.4).abs() < 1e-12);
}
#[test]
fn mean_is_a_over_a_plus_b() {
let d = BetaDistribution {
alpha_parameter: 2.0,
beta_parameter: 5.0,
..Default::default()
};
assert!(matches!(d.mean(), Some(m) if (m - 2.0 / 7.0).abs() < 1e-12));
}
}