use std::f64;
use rand::Rng;
use rand::distributions::{Sample, IndependentSample};
use statistics::*;
use distribution::{Univariate, Continuous, Distribution, Gamma};
use result::Result;
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct ChiSquared {
freedom: f64,
g: Gamma,
}
impl ChiSquared {
pub fn new(freedom: f64) -> Result<ChiSquared> {
Gamma::new(freedom / 2.0, 0.5).map(|g| {
ChiSquared {
freedom: freedom,
g: g,
}
})
}
pub fn freedom(&self) -> f64 {
self.freedom
}
pub fn shape(&self) -> f64 {
self.g.shape()
}
pub fn rate(&self) -> f64 {
self.g.rate()
}
}
impl Sample<f64> for ChiSquared {
fn sample<R: Rng>(&mut self, r: &mut R) -> f64 {
super::Distribution::sample(self, r)
}
}
impl IndependentSample<f64> for ChiSquared {
fn ind_sample<R: Rng>(&self, r: &mut R) -> f64 {
super::Distribution::sample(self, r)
}
}
impl Distribution<f64> for ChiSquared {
fn sample<R: Rng>(&self, r: &mut R) -> f64 {
self.g.sample(r)
}
}
impl Univariate<f64, f64> for ChiSquared {
fn cdf(&self, x: f64) -> f64 {
self.g.cdf(x)
}
}
impl Min<f64> for ChiSquared {
fn min(&self) -> f64 {
0.0
}
}
impl Max<f64> for ChiSquared {
fn max(&self) -> f64 {
f64::INFINITY
}
}
impl Mean<f64> for ChiSquared {
fn mean(&self) -> f64 {
self.g.mean()
}
}
impl Variance<f64> for ChiSquared {
fn variance(&self) -> f64 {
self.g.variance()
}
fn std_dev(&self) -> f64 {
self.g.std_dev()
}
}
impl Entropy<f64> for ChiSquared {
fn entropy(&self) -> f64 {
self.g.entropy()
}
}
impl Skewness<f64> for ChiSquared {
fn skewness(&self) -> f64 {
self.g.skewness()
}
}
impl Median<f64> for ChiSquared {
fn median(&self) -> f64 {
if self.freedom < 1.0 {
self.freedom - 2.0 / 3.0 + 12.0 / (81.0 * self.freedom) -
8.0 / (729.0 * self.freedom * self.freedom)
} else {
self.freedom - 2.0 / 3.0
}
}
}
impl Mode<f64> for ChiSquared {
fn mode(&self) -> f64 {
self.g.mode()
}
}
impl Continuous<f64, f64> for ChiSquared {
fn pdf(&self, x: f64) -> f64 {
self.g.pdf(x)
}
fn ln_pdf(&self, x: f64) -> f64 {
self.g.ln_pdf(x)
}
}
#[cfg_attr(rustfmt, rustfmt_skip)]
#[cfg(test)]
mod test {
use std::f64;
use statistics::Median;
use distribution::ChiSquared;
fn try_create(freedom: f64) -> ChiSquared {
let n = ChiSquared::new(freedom);
assert!(n.is_ok());
n.unwrap()
}
fn test_case<F>(freedom: f64, expected: f64, eval: F)
where F: Fn(ChiSquared) -> f64
{
let n = try_create(freedom);
let x = eval(n);
assert_eq!(expected, x);
}
fn test_almost<F>(freedom: f64, expected: f64, acc: f64, eval: F)
where F: Fn(ChiSquared) -> f64
{
let n = try_create(freedom);
let x = eval(n);
assert_almost_eq!(expected, x, acc);
}
#[test]
fn test_median() {
test_almost(0.5, 0.0857338820301783264746, 1e-16, |x| x.median());
test_case(1.0, 1.0 - 2.0 / 3.0, |x| x.median());
test_case(2.0, 2.0 - 2.0 / 3.0, |x| x.median());
test_case(2.5, 2.5 - 2.0 / 3.0, |x| x.median());
test_case(3.0, 3.0 - 2.0 / 3.0, |x| x.median());
}
}