1use crate::{ChiSquared, Distribution, Exp1, Open01, StandardNormal, chi_squared};
13use core::fmt;
14use num_traits::Float;
15use rand::Rng;
16#[cfg(feature = "serde")]
17use serde::{Deserialize, Serialize};
18
19#[derive(Clone, Copy, Debug, PartialEq)]
41#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
42pub struct FisherF<F>
43where
44 F: Float,
45 StandardNormal: Distribution<F>,
46 Exp1: Distribution<F>,
47 Open01: Distribution<F>,
48{
49 numer: ChiSquared<F>,
50 denom: ChiSquared<F>,
51 dof_ratio: F,
54}
55
56#[derive(Clone, Copy, Debug, PartialEq, Eq)]
58#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
59pub enum Error {
60 MTooSmall,
62 NTooSmall,
64}
65
66impl fmt::Display for Error {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 f.write_str(match self {
69 Error::MTooSmall => "m is not positive in Fisher F distribution",
70 Error::NTooSmall => "n is not positive in Fisher F distribution",
71 })
72 }
73}
74
75#[cfg(feature = "std")]
76impl std::error::Error for Error {}
77
78impl<F> FisherF<F>
79where
80 F: Float,
81 StandardNormal: Distribution<F>,
82 Exp1: Distribution<F>,
83 Open01: Distribution<F>,
84{
85 pub fn new(m: F, n: F) -> Result<FisherF<F>, Error> {
87 Ok(FisherF {
88 numer: ChiSquared::new(m).map_err(|x| match x {
89 chi_squared::Error::DoFTooSmall => Error::MTooSmall,
90 })?,
91 denom: ChiSquared::new(n).map_err(|x| match x {
92 chi_squared::Error::DoFTooSmall => Error::NTooSmall,
93 })?,
94 dof_ratio: n / m,
95 })
96 }
97}
98impl<F> Distribution<F> for FisherF<F>
99where
100 F: Float,
101 StandardNormal: Distribution<F>,
102 Exp1: Distribution<F>,
103 Open01: Distribution<F>,
104{
105 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> F {
106 self.numer.sample(rng) / self.denom.sample(rng) * self.dof_ratio
107 }
108}
109
110#[cfg(test)]
111mod test {
112 use super::*;
113
114 #[test]
115 fn test_f() {
116 let f = FisherF::new(2.0, 32.0).unwrap();
117 let mut rng = crate::test::rng(204);
118 for _ in 0..1000 {
119 f.sample(&mut rng);
120 }
121 }
122
123 #[test]
124 fn fisher_f_distributions_can_be_compared() {
125 assert_eq!(FisherF::new(1.0, 2.0), FisherF::new(1.0, 2.0));
126 }
127}