use crate::{ChiSquared, Distribution, Exp1, Open01, StandardNormal, chi_squared};
use core::fmt;
use num_traits::Float;
use rand::Rng;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FisherF<F>
where
F: Float,
StandardNormal: Distribution<F>,
Exp1: Distribution<F>,
Open01: Distribution<F>,
{
numer: ChiSquared<F>,
denom: ChiSquared<F>,
dof_ratio: F,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Error {
MTooSmall,
NTooSmall,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Error::MTooSmall => "m is not positive in Fisher F distribution",
Error::NTooSmall => "n is not positive in Fisher F distribution",
})
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
impl<F> FisherF<F>
where
F: Float,
StandardNormal: Distribution<F>,
Exp1: Distribution<F>,
Open01: Distribution<F>,
{
pub fn new(m: F, n: F) -> Result<FisherF<F>, Error> {
Ok(FisherF {
numer: ChiSquared::new(m).map_err(|x| match x {
chi_squared::Error::DoFTooSmall => Error::MTooSmall,
})?,
denom: ChiSquared::new(n).map_err(|x| match x {
chi_squared::Error::DoFTooSmall => Error::NTooSmall,
})?,
dof_ratio: n / m,
})
}
}
impl<F> Distribution<F> for FisherF<F>
where
F: Float,
StandardNormal: Distribution<F>,
Exp1: Distribution<F>,
Open01: Distribution<F>,
{
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> F {
self.numer.sample(rng) / self.denom.sample(rng) * self.dof_ratio
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_f() {
let f = FisherF::new(2.0, 32.0).unwrap();
let mut rng = crate::test::rng(204);
for _ in 0..1000 {
f.sample(&mut rng);
}
}
#[test]
fn fisher_f_distributions_can_be_compared() {
assert_eq!(FisherF::new(1.0, 2.0), FisherF::new(1.0, 2.0));
}
}