use crate::error::{Error, Result};
use crate::math::gamma::{gammaf, lowergammaf};
use crate::random::randf;
pub fn randgammaf(alpha: f32, beta: f32) -> Result<f32> {
if alpha <= 0.0 {
return Err(Error::Config("alpha must be greater than zero".to_string()));
} else if beta <= 0.0 {
return Err(Error::Config("beta must be greater than zero".to_string()));
}
let n = alpha.floor() as usize;
let delta = alpha - n as f32;
let mut x_n = 0.0;
for _ in 0..n {
let u = randf();
x_n -= u.ln();
}
let x_delta = randgammaf_delta(delta)?;
Ok(beta * (x_delta + x_n))
}
pub fn randgammaf_pdf(x: f32, alpha: f32, beta: f32) -> Result<f32> {
if alpha <= 0.0 {
return Err(Error::Config("alpha must be greater than zero".to_string()));
} else if beta <= 0.0 {
return Err(Error::Config("beta must be greater than zero".to_string()));
}
if x <= 0.0 {
return Ok(0.0);
}
let t0 = x.powf(alpha - 1.0);
let t1 = (-x / beta).exp();
let t2 = gammaf(alpha);
let t3 = beta.powf(alpha);
Ok((t0 * t1) / (t2 * t3))
}
pub fn randgammaf_cdf(x: f32, alpha: f32, beta: f32) -> Result<f32> {
if alpha <= 0.0 {
return Err(Error::Config("alpha must be greater than zero".to_string()));
} else if beta <= 0.0 {
return Err(Error::Config("beta must be greater than zero".to_string()));
}
if x <= 0.0 {
return Ok(0.0);
}
Ok(lowergammaf(alpha, x / beta) / gammaf(alpha))
}
fn randgammaf_delta(delta: f32) -> Result<f32> {
if delta < 0.0 || delta >= 1.0 {
return Err(Error::Config("delta must be in [0,1)".to_string()));
}
let delta_inv = 1.0 / delta;
let e = std::f32::consts::E;
let v0_ = e / (e + delta);
loop {
let v0 = randf();
let v1 = randf();
let v2 = randf();
let (xi, eta) = if v2 <= v0_ {
let xi = v1.powf(delta_inv);
let eta = v0 * xi.powf(delta - 1.0);
(xi, eta)
} else {
let xi = 1.0 - v1.ln();
let eta = v0 * (-xi).exp();
(xi, eta)
};
if eta <= xi.powf(delta - 1.0) * (-xi).exp() {
return Ok(xi);
}
}
}