use crate::distributions::*;
use crate::functions::gamma;
#[derive(Debug, Clone, Copy)]
pub struct ChiSquared {
dof: usize,
sampler: Gamma,
}
impl ChiSquared {
pub fn new(dof: usize) -> Self {
assert!(dof > 0, "Degrees of freedom must be positive.");
ChiSquared {
dof,
sampler: Gamma::new((dof as f64) / 2., 0.5),
}
}
pub fn set_dof(&mut self, dof: usize) -> &mut Self {
assert!(dof > 0, "Degrees of freedom must be positive.");
self.dof = dof;
self
}
}
impl Default for ChiSquared {
fn default() -> Self {
Self::new(1)
}
}
impl Distribution for ChiSquared {
type Output = f64;
fn sample(&self) -> f64 {
self.sampler.sample()
}
}
impl Distribution1D for ChiSquared {
fn update(&mut self, params: &[f64]) {
self.set_dof(params[0] as usize);
}
}
impl Continuous for ChiSquared {
type PDFType = f64;
fn pdf(&self, x: f64) -> f64 {
if (self.dof == 1 && x <= 0.) || (x < 0.) {
return 0.;
}
let half_k = (self.dof as f64) / 2.;
1. / (2_f64.powf(half_k) * gamma(half_k)) * x.powf(half_k - 1.) * (-x / 2.).exp()
}
}
impl Mean for ChiSquared {
type MeanType = f64;
fn mean(&self) -> f64 {
self.dof as f64
}
}
impl Variance for ChiSquared {
type VarianceType = f64;
fn var(&self) -> f64 {
self.mean() * 2.
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::statistics::{mean, var};
use approx_eq::assert_approx_eq;
#[test]
fn test_moments() {
let data1 = ChiSquared::new(2).sample_n(1e6 as usize);
assert_approx_eq!(2., mean(&data1), 1e-2);
assert_approx_eq!(4., var(&data1), 1e-2);
let data2 = ChiSquared::new(5).sample_n(1e6 as usize);
assert_approx_eq!(5., mean(&data2), 1e-2);
assert_approx_eq!(10., var(&data2), 1e-2);
}
}