use crate::FloatScalar;
use crate::special::{lgamma, gamma_inc};
use super::{ContinuousDistribution, StatsError, quantile_newton, normal_quantile_standard};
#[derive(Debug, Clone, Copy)]
pub struct Gamma<T> {
shape: T, rate: T, }
impl<T: FloatScalar> Gamma<T> {
pub fn new(shape: T, rate: T) -> Result<Self, StatsError> {
if shape <= T::zero() || rate <= T::zero() {
return Err(StatsError::InvalidParameter);
}
Ok(Self { shape, rate })
}
}
impl<T: FloatScalar> Gamma<T> {
pub fn sample(&self, rng: &mut super::Rng) -> T {
rng.next_gamma(self.shape) / self.rate
}
pub fn sample_array<const K: usize>(&self, rng: &mut super::Rng) -> [T; K] {
let mut out = [T::zero(); K];
for v in out.iter_mut() {
*v = self.sample(rng);
}
out
}
}
impl<T: FloatScalar> ContinuousDistribution<T> for Gamma<T> {
fn pdf(&self, x: T) -> T {
if x < T::zero() {
return T::zero();
}
if x == T::zero() {
let one = T::one();
if self.shape == one {
return self.rate; } else if self.shape > one {
return T::zero(); } else {
return T::infinity(); }
}
self.ln_pdf(x).exp()
}
fn ln_pdf(&self, x: T) -> T {
if x < T::zero() {
return T::neg_infinity();
}
if x == T::zero() {
return self.pdf(x).ln();
}
let one = T::one();
self.shape * self.rate.ln() - lgamma(self.shape)
+ (self.shape - one) * x.ln()
- self.rate * x
}
fn cdf(&self, x: T) -> T {
if x <= T::zero() {
return T::zero();
}
gamma_inc(self.shape, self.rate * x).unwrap_or(T::nan())
}
fn quantile(&self, p: T) -> T {
let mean = self.mean();
let std = self.variance().sqrt();
let x0 = if self.shape >= T::one() {
let nine = T::from(9.0).unwrap();
let z = normal_quantile_standard(p);
let v = T::one() - T::one() / (nine * self.shape)
+ z / (nine * self.shape).sqrt();
let wh = self.shape / self.rate * v * v * v;
if wh > T::zero() { wh } else { mean }
} else {
mean
};
let hi = mean + T::from(40.0).unwrap() * std;
quantile_newton(|x| self.cdf(x), |x| self.pdf(x), p, x0, T::zero(), hi)
}
fn mean(&self) -> T {
self.shape / self.rate
}
fn variance(&self) -> T {
self.shape / (self.rate * self.rate)
}
}