1use crate::{ChiSquared, Distribution, Exp1, Open01, StandardNormal};
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 let zero = F::zero();
88 if !(m > zero) {
89 return Err(Error::MTooSmall);
90 }
91 if !(n > zero) {
92 return Err(Error::NTooSmall);
93 }
94
95 Ok(FisherF {
96 numer: ChiSquared::new(m).unwrap(),
97 denom: ChiSquared::new(n).unwrap(),
98 dof_ratio: n / m,
99 })
100 }
101}
102impl<F> Distribution<F> for FisherF<F>
103where
104 F: Float,
105 StandardNormal: Distribution<F>,
106 Exp1: Distribution<F>,
107 Open01: Distribution<F>,
108{
109 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> F {
110 self.numer.sample(rng) / self.denom.sample(rng) * self.dof_ratio
111 }
112}
113
114#[cfg(test)]
115mod test {
116 use super::*;
117
118 #[test]
119 fn test_f() {
120 let f = FisherF::new(2.0, 32.0).unwrap();
121 let mut rng = crate::test::rng(204);
122 for _ in 0..1000 {
123 f.sample(&mut rng);
124 }
125 }
126
127 #[test]
128 fn fisher_f_distributions_can_be_compared() {
129 assert_eq!(FisherF::new(1.0, 2.0), FisherF::new(1.0, 2.0));
130 }
131}