use crate::betaln;
pub fn betapdf(x: f64, a: f64, b: f64) -> f64 {
if a == 1.0 && x == 0.0 {
return b;
}
if b == 1.0 && x == 1.0 {
return a;
}
if (a < 1.0 && x == 0.0) || (b < 1.0 && x == 1.0) {
return f64::INFINITY;
}
if a <= 0.0 || b <= 0.0 || a.is_nan() || b.is_nan() || x.is_nan() {
return f64::NAN;
}
if a > 0.0 && b > 0.0 && x > 0.0 && x < 1.0 {
let smallx = x < 0.1;
let loga = (a - 1.0) * x.ln();
let logb = if smallx {
(b - 1.0) * f64::ln_1p(-x)
} else {
(b - 1.0) * f64::ln(1.0 - x)
};
return f64::exp(loga + logb - betaln(a, b));
}
0.0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_betapdf_uniform() {
assert_eq!(betapdf(0.1, 1.0, 1.0), 1.0);
assert_eq!(betapdf(0.5, 1.0, 1.0), 1.0);
assert_eq!(betapdf(0.9, 1.0, 1.0), 1.0);
}
#[test]
fn test_betapdf_known_values() {
let tol = 1e-14;
assert!((betapdf(0.2, 2.0, 3.0) - 1.536).abs() < tol);
assert!((betapdf(0.5, 0.5, 0.5) - 2.0 / std::f64::consts::PI).abs() < tol);
}
#[test]
fn test_betapdf_symmetry() {
let x = 0.3;
let a = 2.5;
let b = 1.5;
assert!((betapdf(x, a, b) - betapdf(1.0 - x, b, a)).abs() < 1e-14);
}
#[test]
fn test_betapdf_poles_and_boundaries() {
assert_eq!(betapdf(0.0, 0.5, 2.0), f64::INFINITY);
assert_eq!(betapdf(1.0, 2.0, 0.5), f64::INFINITY);
assert_eq!(betapdf(0.0, 1.0, 5.0), 5.0);
assert_eq!(betapdf(1.0, 5.0, 1.0), 5.0);
assert_eq!(betapdf(-0.1, 2.0, 2.0), 0.0);
assert_eq!(betapdf(1.1, 2.0, 2.0), 0.0);
}
#[test]
fn test_betapdf_invalid_params() {
assert!(betapdf(0.5, 0.0, 1.0).is_nan());
assert!(betapdf(0.5, -1.0, 1.0).is_nan());
assert!(betapdf(0.5, 1.0, 0.0).is_nan());
assert!(betapdf(0.5, 1.0, -1.0).is_nan());
assert!(betapdf(f64::NAN, 1.0, 1.0).is_nan());
assert!(betapdf(0.5, f64::NAN, 1.0).is_nan());
}
}