1use crate::distribution::{Continuous, ContinuousCDF};
2use crate::function::beta;
3use crate::statistics::*;
4use std::f64;
5
6#[derive(Debug, Copy, Clone, PartialEq)]
22pub struct FisherSnedecor {
23 freedom_1: f64,
24 freedom_2: f64,
25}
26
27#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
29#[non_exhaustive]
30pub enum FisherSnedecorError {
31 Freedom1Invalid,
33
34 Freedom2Invalid,
36}
37
38impl std::fmt::Display for FisherSnedecorError {
39 #[cfg_attr(coverage_nightly, coverage(off))]
40 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
41 match self {
42 FisherSnedecorError::Freedom1Invalid => {
43 write!(f, "freedom_1 is NaN, infinite, zero or less than zero.")
44 }
45 FisherSnedecorError::Freedom2Invalid => {
46 write!(f, "freedom_2 is NaN, infinite, zero or less than zero.")
47 }
48 }
49 }
50}
51
52impl std::error::Error for FisherSnedecorError {}
53
54impl FisherSnedecor {
55 pub fn new(freedom_1: f64, freedom_2: f64) -> Result<FisherSnedecor, FisherSnedecorError> {
75 if !freedom_1.is_finite() || freedom_1 <= 0.0 {
76 return Err(FisherSnedecorError::Freedom1Invalid);
77 }
78
79 if !freedom_2.is_finite() || freedom_2 <= 0.0 {
80 return Err(FisherSnedecorError::Freedom2Invalid);
81 }
82
83 Ok(FisherSnedecor {
84 freedom_1,
85 freedom_2,
86 })
87 }
88
89 pub fn freedom_1(&self) -> f64 {
101 self.freedom_1
102 }
103
104 pub fn freedom_2(&self) -> f64 {
116 self.freedom_2
117 }
118}
119
120impl std::fmt::Display for FisherSnedecor {
121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 write!(f, "F({},{})", self.freedom_1, self.freedom_2)
123 }
124}
125
126#[cfg(feature = "rand")]
127#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
128impl ::rand::distributions::Distribution<f64> for FisherSnedecor {
129 fn sample<R: ::rand::Rng + ?Sized>(&self, rng: &mut R) -> f64 {
130 (super::gamma::sample_unchecked(rng, self.freedom_1 / 2.0, 0.5) * self.freedom_2)
131 / (super::gamma::sample_unchecked(rng, self.freedom_2 / 2.0, 0.5) * self.freedom_1)
132 }
133}
134
135impl ContinuousCDF<f64, f64> for FisherSnedecor {
136 fn cdf(&self, x: f64) -> f64 {
150 if x < 0.0 {
151 0.0
152 } else if x.is_infinite() {
153 1.0
154 } else {
155 beta::beta_reg(
156 self.freedom_1 / 2.0,
157 self.freedom_2 / 2.0,
158 self.freedom_1 * x / (self.freedom_1 * x + self.freedom_2),
159 )
160 }
161 }
162
163 fn sf(&self, x: f64) -> f64 {
176 if x < 0.0 {
177 1.0
178 } else if x.is_infinite() {
179 0.0
180 } else {
181 beta::beta_reg(
182 self.freedom_2 / 2.0,
183 self.freedom_1 / 2.0,
184 1. - ((self.freedom_1 * x) / (self.freedom_1 * x + self.freedom_2)),
185 )
186 }
187 }
188
189 fn inverse_cdf(&self, x: f64) -> f64 {
203 if !(0.0..=1.0).contains(&x) {
204 panic!("x must be in [0, 1]");
205 } else {
206 let z = beta::inv_beta_reg(self.freedom_1 / 2.0, self.freedom_2 / 2.0, x);
207 self.freedom_2 / (self.freedom_1 * (1.0 / z - 1.0))
208 }
209 }
210}
211
212impl Min<f64> for FisherSnedecor {
213 fn min(&self) -> f64 {
223 0.0
224 }
225}
226
227impl Max<f64> for FisherSnedecor {
228 fn max(&self) -> f64 {
238 f64::INFINITY
239 }
240}
241
242impl Distribution<f64> for FisherSnedecor {
243 fn mean(&self) -> Option<f64> {
261 if self.freedom_2 <= 2.0 {
262 None
263 } else {
264 Some(self.freedom_2 / (self.freedom_2 - 2.0))
265 }
266 }
267
268 fn variance(&self) -> Option<f64> {
287 if self.freedom_2 <= 4.0 {
288 None
289 } else {
290 let val =
291 (2.0 * self.freedom_2 * self.freedom_2 * (self.freedom_1 + self.freedom_2 - 2.0))
292 / (self.freedom_1
293 * (self.freedom_2 - 2.0)
294 * (self.freedom_2 - 2.0)
295 * (self.freedom_2 - 4.0));
296 Some(val)
297 }
298 }
299
300 fn skewness(&self) -> Option<f64> {
320 if self.freedom_2 <= 6.0 {
321 None
322 } else {
323 let val = ((2.0 * self.freedom_1 + self.freedom_2 - 2.0)
324 * (8.0 * (self.freedom_2 - 4.0)).sqrt())
325 / ((self.freedom_2 - 6.0)
326 * (self.freedom_1 * (self.freedom_1 + self.freedom_2 - 2.0)).sqrt());
327 Some(val)
328 }
329 }
330}
331
332impl Mode<Option<f64>> for FisherSnedecor {
333 fn mode(&self) -> Option<f64> {
352 if self.freedom_1 <= 2.0 {
353 None
354 } else {
355 let val = (self.freedom_2 * (self.freedom_1 - 2.0))
356 / (self.freedom_1 * (self.freedom_2 + 2.0));
357 Some(val)
358 }
359 }
360}
361
362impl Continuous<f64, f64> for FisherSnedecor {
363 fn pdf(&self, x: f64) -> f64 {
382 if x.is_infinite() || x <= 0.0 {
383 0.0
384 } else {
385 ((self.freedom_1 * x).powf(self.freedom_1) * self.freedom_2.powf(self.freedom_2)
386 / (self.freedom_1 * x + self.freedom_2).powf(self.freedom_1 + self.freedom_2))
387 .sqrt()
388 / (x * beta::beta(self.freedom_1 / 2.0, self.freedom_2 / 2.0))
389 }
390 }
391
392 fn ln_pdf(&self, x: f64) -> f64 {
411 self.pdf(x).ln()
412 }
413}
414
415#[rustfmt::skip]
416#[cfg(test)]
417mod tests {
418 use super::*;
419 use crate::distribution::internal::*;
420 use crate::testing_boiler;
421
422 testing_boiler!(freedom_1: f64, freedom_2: f64; FisherSnedecor; FisherSnedecorError);
423
424 #[test]
425 fn test_create() {
426 create_ok(0.1, 0.1);
427 create_ok(1.0, 0.1);
428 create_ok(10.0, 0.1);
429 create_ok(0.1, 1.0);
430 create_ok(1.0, 1.0);
431 create_ok(10.0, 1.0);
432 }
433
434 #[test]
435 fn test_bad_create() {
436 test_create_err(f64::INFINITY, 0.1, FisherSnedecorError::Freedom1Invalid);
437 test_create_err(0.1, f64::INFINITY, FisherSnedecorError::Freedom2Invalid);
438
439 create_err(f64::NAN, f64::NAN);
440 create_err(0.0, f64::NAN);
441 create_err(-1.0, f64::NAN);
442 create_err(-10.0, f64::NAN);
443 create_err(f64::NAN, 0.0);
444 create_err(0.0, 0.0);
445 create_err(-1.0, 0.0);
446 create_err(-10.0, 0.0);
447 create_err(f64::NAN, -1.0);
448 create_err(0.0, -1.0);
449 create_err(-1.0, -1.0);
450 create_err(-10.0, -1.0);
451 create_err(f64::NAN, -10.0);
452 create_err(0.0, -10.0);
453 create_err(-1.0, -10.0);
454 create_err(-10.0, -10.0);
455 create_err(f64::INFINITY, f64::INFINITY);
456 }
457
458 #[test]
459 fn test_mean() {
460 let mean = |x: FisherSnedecor| x.mean().unwrap();
461 test_exact(0.1, 10.0, 1.25, mean);
462 test_exact(1.0, 10.0, 1.25, mean);
463 test_exact(10.0, 10.0, 1.25, mean);
464 }
465
466 #[test]
467 fn test_mean_with_low_d2() {
468 test_none(0.1, 0.1, |dist| dist.mean());
469 }
470
471 #[test]
472 fn test_variance() {
473 let variance = |x: FisherSnedecor| x.variance().unwrap();
474 test_absolute(0.1, 10.0, 42.1875, 1e-14, variance);
475 test_exact(1.0, 10.0, 4.6875, variance);
476 test_exact(10.0, 10.0, 0.9375, variance);
477 }
478
479 #[test]
480 fn test_variance_with_low_d2() {
481 test_none(0.1, 0.1, |dist| dist.variance());
482 }
483
484 #[test]
485 fn test_skewness() {
486 let skewness = |x: FisherSnedecor| x.skewness().unwrap();
487 test_absolute(0.1, 10.0, 15.78090735784977089658, 1e-14, skewness);
488 test_exact(1.0, 10.0, 5.773502691896257645091, skewness);
489 test_exact(10.0, 10.0, 3.614784456460255759501, skewness);
490 }
491
492 #[test]
493 fn test_skewness_with_low_d2() {
494 test_none(0.1, 0.1, |dist| dist.skewness());
495 }
496
497 #[test]
498 fn test_mode() {
499 let mode = |x: FisherSnedecor| x.mode().unwrap();
500 test_exact(10.0, 0.1, 0.0380952380952380952381, mode);
501 test_exact(10.0, 1.0, 4.0 / 15.0, mode);
502 test_exact(10.0, 10.0, 2.0 / 3.0, mode);
503 }
504
505 #[test]
506 fn test_mode_with_low_d1() {
507 test_none(0.1, 0.1, |dist| dist.mode());
508 }
509
510 #[test]
511 fn test_min_max() {
512 let min = |x: FisherSnedecor| x.min();
513 let max = |x: FisherSnedecor| x.max();
514 test_exact(1.0, 1.0, 0.0, min);
515 test_exact(1.0, 1.0, f64::INFINITY, max);
516 }
517
518 #[test]
519 fn test_pdf() {
520 let pdf = |arg: f64| move |x: FisherSnedecor| x.pdf(arg);
521 test_absolute(0.1, 0.1, 0.0234154207226588982471, 1e-16, pdf(1.0));
522 test_absolute(1.0, 0.1, 0.0396064560910663979961, 1e-16, pdf(1.0));
523 test_absolute(10.0, 0.1, 0.0418440630400545297349, 1e-14, pdf(1.0));
524 test_absolute(0.1, 1.0, 0.0396064560910663979961, 1e-16, pdf(1.0));
525 test_absolute(1.0, 1.0, 0.1591549430918953357689, 1e-16, pdf(1.0));
526 test_absolute(10.0, 1.0, 0.230361989229138647108, 1e-16, pdf(1.0));
527 test_absolute(0.1, 0.1, 0.00221546909694001013517, 1e-18, pdf(10.0));
528 test_absolute(1.0, 0.1, 0.00369960370387922619592, 1e-17, pdf(10.0));
529 test_absolute(10.0, 0.1, 0.00390179721174142927402, 1e-15, pdf(10.0));
530 test_absolute(0.1, 1.0, 0.00319864073359931548273, 1e-17, pdf(10.0));
531 test_absolute(1.0, 1.0, 0.009150765837179460915678, 1e-17, pdf(10.0));
532 test_absolute(10.0, 1.0, 0.0116493859171442148446, 1e-17, pdf(10.0));
533 test_absolute(0.1, 10.0, 0.00305087016058573989694, 1e-15, pdf(10.0));
534 test_absolute(1.0, 10.0, 0.00271897749113479577864, 1e-17, pdf(10.0));
535 test_absolute(10.0, 10.0, 2.4289227234060500084E-4, 1e-18, pdf(10.0));
536 }
537
538 #[test]
539 fn test_ln_pdf() {
540 let ln_pdf = |arg: f64| move |x: FisherSnedecor| x.ln_pdf(arg);
541 test_absolute(0.1, 0.1, 0.0234154207226588982471f64.ln(), 1e-15, ln_pdf(1.0));
542 test_absolute(1.0, 0.1, 0.0396064560910663979961f64.ln(), 1e-15, ln_pdf(1.0));
543 test_absolute(10.0, 0.1, 0.0418440630400545297349f64.ln(), 1e-13, ln_pdf(1.0));
544 test_absolute(0.1, 1.0, 0.0396064560910663979961f64.ln(), 1e-15, ln_pdf(1.0));
545 test_absolute(1.0, 1.0, 0.1591549430918953357689f64.ln(), 1e-15, ln_pdf(1.0));
546 test_absolute(10.0, 1.0, 0.230361989229138647108f64.ln(), 1e-15, ln_pdf(1.0));
547 test_exact(0.1, 0.1, 0.00221546909694001013517f64.ln(), ln_pdf(10.0));
548 test_absolute(1.0, 0.1, 0.00369960370387922619592f64.ln(), 1e-15, ln_pdf(10.0));
549 test_absolute(10.0, 0.1, 0.00390179721174142927402f64.ln(), 1e-13, ln_pdf(10.0));
550 test_absolute(0.1, 1.0, 0.00319864073359931548273f64.ln(), 1e-15, ln_pdf(10.0));
551 test_absolute(1.0, 1.0, 0.009150765837179460915678f64.ln(), 1e-15, ln_pdf(10.0));
552 test_exact(10.0, 1.0, 0.0116493859171442148446f64.ln(), ln_pdf(10.0));
553 test_absolute(0.1, 10.0, 0.00305087016058573989694f64.ln(), 1e-13, ln_pdf(10.0));
554 test_exact(1.0, 10.0, 0.00271897749113479577864f64.ln(), ln_pdf(10.0));
555 test_absolute(10.0, 10.0, 2.4289227234060500084E-4f64.ln(), 1e-14, ln_pdf(10.0));
556 }
557
558 #[test]
559 fn test_cdf() {
560 let cdf = |arg: f64| move |x: FisherSnedecor| x.cdf(arg);
561 test_absolute(0.1, 0.1, 0.44712986033425140335, 1e-15, cdf(0.1));
562 test_absolute(1.0, 0.1, 0.08156522095104674015, 1e-15, cdf(0.1));
563 test_absolute(10.0, 0.1, 0.033184005716276536322, 1e-13, cdf(0.1));
564 test_absolute(0.1, 1.0, 0.74378710917986379989, 1e-15, cdf(0.1));
565 test_absolute(1.0, 1.0, 0.1949822290421366451595, 1e-16, cdf(0.1));
566 test_absolute(10.0, 1.0, 0.0101195597354337146205, 1e-17, cdf(0.1));
567 test_absolute(0.1, 0.1, 0.5, 1e-15, cdf(1.0));
568 test_absolute(1.0, 0.1, 0.16734351500944271141, 1e-14, cdf(1.0));
569 test_absolute(10.0, 0.1, 0.12207560664741704938, 1e-13, cdf(1.0));
570 test_absolute(0.1, 1.0, 0.83265648499055728859, 1e-15, cdf(1.0));
571 test_absolute(1.0, 1.0, 0.5, 1e-15, cdf(1.0));
572 test_absolute(10.0, 1.0, 0.340893132302059872675, 1e-15, cdf(1.0));
573 }
574
575 #[test]
576 fn test_cdf_lower_bound() {
577 let cdf = |arg: f64| move |x: FisherSnedecor| x.cdf(arg);
578 test_exact(0.1, 0.1, 0.0, cdf(-1.0));
579 }
580
581 #[test]
582 fn test_sf() {
583 let sf = |arg: f64| move |x: FisherSnedecor| x.sf(arg);
584 test_absolute(0.1, 0.1, 0.5528701396657489, 1e-12, sf(0.1));
585 test_absolute(1.0, 0.1, 0.9184347790489533, 1e-12, sf(0.1));
586 test_absolute(10.0, 0.1, 0.9668159942836896, 1e-12, sf(0.1));
587 test_absolute(0.1, 1.0, 0.25621289082013654, 1e-12, sf(0.1));
588 test_absolute(1.0, 1.0, 0.8050177709578634, 1e-12, sf(0.1));
589 test_absolute(10.0, 1.0, 0.9898804402645662, 1e-12, sf(0.1));
590 test_absolute(0.1, 0.1, 0.5, 1e-15, sf(1.0));
591 test_absolute(1.0, 0.1, 0.8326564849905562, 1e-12, sf(1.0));
592 test_absolute(10.0, 0.1, 0.8779243933525519, 1e-12, sf(1.0));
593 test_absolute(0.1, 1.0, 0.16734351500944344, 1e-12, sf(1.0));
594 test_absolute(1.0, 1.0, 0.5, 1e-12, sf(1.0));
595 test_absolute(10.0, 1.0, 0.65910686769794, 1e-12, sf(1.0));
596 }
597
598 #[test]
599 fn test_inverse_cdf() {
600 let func = |arg: f64| move |x: FisherSnedecor| x.inverse_cdf(x.cdf(arg));
601 test_absolute(0.1, 0.1, 0.1, 1e-12, func(0.1));
602 test_absolute(1.0, 0.1, 0.1, 1e-12, func(0.1));
603 test_absolute(10.0, 0.1, 0.1, 1e-12, func(0.1));
604 test_absolute(0.1, 1.0, 0.1, 1e-12, func(0.1));
605 test_absolute(1.0, 1.0, 0.1, 1e-12, func(0.1));
606 test_absolute(10.0, 1.0, 0.1, 1e-12, func(0.1));
607 test_absolute(0.1, 0.1, 1.0, 1e-13, func(1.0));
608 test_absolute(1.0, 0.1, 1.0, 1e-12, func(1.0));
609 test_absolute(10.0, 0.1, 1.0, 1e-12, func(1.0));
610 test_absolute(0.1, 1.0, 1.0, 1e-12, func(1.0));
611 test_absolute(1.0, 1.0, 1.0, 1e-12, func(1.0));
612 test_absolute(10.0, 1.0, 1.0, 1e-12, func(1.0));
613 }
614
615 #[test]
616 fn test_sf_lower_bound() {
617 let sf = |arg: f64| move |x: FisherSnedecor| x.sf(arg);
618 test_exact(0.1, 0.1, 1.0, sf(-1.0));
619 }
620
621 #[test]
622 fn test_continuous() {
623 test::check_continuous_distribution(&create_ok(10.0, 10.0), 0.0, 10.0);
624 }
625}