1use crate::distribution::{Continuous, ContinuousCDF};
2use crate::function::beta;
3use crate::statistics::*;
4use crate::{Result, StatsError};
5use rand::Rng;
6use std::f64;
7
8#[derive(Debug, Copy, Clone, PartialEq)]
24pub struct FisherSnedecor {
25 freedom_1: f64,
26 freedom_2: f64,
27}
28
29impl FisherSnedecor {
30 pub fn new(freedom_1: f64, freedom_2: f64) -> Result<FisherSnedecor> {
50 if !freedom_1.is_finite() || freedom_1 <= 0.0 || !freedom_2.is_finite() || freedom_2 <= 0.0
51 {
52 Err(StatsError::BadParams)
53 } else {
54 Ok(FisherSnedecor {
55 freedom_1,
56 freedom_2,
57 })
58 }
59 }
60
61 pub fn freedom_1(&self) -> f64 {
73 self.freedom_1
74 }
75
76 pub fn freedom_2(&self) -> f64 {
88 self.freedom_2
89 }
90}
91
92impl ::rand::distributions::Distribution<f64> for FisherSnedecor {
93 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 {
94 (super::gamma::sample_unchecked(rng, self.freedom_1 / 2.0, 0.5) * self.freedom_2)
95 / (super::gamma::sample_unchecked(rng, self.freedom_2 / 2.0, 0.5) * self.freedom_1)
96 }
97}
98
99impl ContinuousCDF<f64, f64> for FisherSnedecor {
100 fn cdf(&self, x: f64) -> f64 {
114 if x < 0.0 {
115 0.0
116 } else if x.is_infinite() {
117 1.0
118 } else {
119 beta::beta_reg(
120 self.freedom_1 / 2.0,
121 self.freedom_2 / 2.0,
122 self.freedom_1 * x / (self.freedom_1 * x + self.freedom_2),
123 )
124 }
125 }
126
127 fn sf(&self, x: f64) -> f64 {
140 if x < 0.0 {
141 1.0
142 } else if x.is_infinite() {
143 0.0
144 } else {
145 beta::beta_reg(
146 self.freedom_2 / 2.0,
147 self.freedom_1 / 2.0,
148 1. - ((self.freedom_1 * x) / (self.freedom_1 * x + self.freedom_2))
149 )
150 }
151 }
152}
153
154impl Min<f64> for FisherSnedecor {
155 fn min(&self) -> f64 {
165 0.0
166 }
167}
168
169impl Max<f64> for FisherSnedecor {
170 fn max(&self) -> f64 {
180 f64::INFINITY
181 }
182}
183
184impl Distribution<f64> for FisherSnedecor {
185 fn mean(&self) -> Option<f64> {
203 if self.freedom_2 <= 2.0 {
204 None
205 } else {
206 Some(self.freedom_2 / (self.freedom_2 - 2.0))
207 }
208 }
209 fn variance(&self) -> Option<f64> {
228 if self.freedom_2 <= 4.0 {
229 None
230 } else {
231 let val =
232 (2.0 * self.freedom_2 * self.freedom_2 * (self.freedom_1 + self.freedom_2 - 2.0))
233 / (self.freedom_1
234 * (self.freedom_2 - 2.0)
235 * (self.freedom_2 - 2.0)
236 * (self.freedom_2 - 4.0));
237 Some(val)
238 }
239 }
240 fn skewness(&self) -> Option<f64> {
260 if self.freedom_2 <= 6.0 {
261 None
262 } else {
263 let val = ((2.0 * self.freedom_1 + self.freedom_2 - 2.0)
264 * (8.0 * (self.freedom_2 - 4.0)).sqrt())
265 / ((self.freedom_2 - 6.0)
266 * (self.freedom_1 * (self.freedom_1 + self.freedom_2 - 2.0)).sqrt());
267 Some(val)
268 }
269 }
270}
271
272impl Mode<Option<f64>> for FisherSnedecor {
273 fn mode(&self) -> Option<f64> {
292 if self.freedom_1 <= 2.0 {
293 None
294 } else {
295 let val = (self.freedom_2 * (self.freedom_1 - 2.0))
296 / (self.freedom_1 * (self.freedom_2 + 2.0));
297 Some(val)
298 }
299 }
300}
301
302impl Continuous<f64, f64> for FisherSnedecor {
303 fn pdf(&self, x: f64) -> f64 {
322 if x.is_infinite() || x <= 0.0 {
323 0.0
324 } else {
325 ((self.freedom_1 * x).powf(self.freedom_1) * self.freedom_2.powf(self.freedom_2)
326 / (self.freedom_1 * x + self.freedom_2).powf(self.freedom_1 + self.freedom_2))
327 .sqrt()
328 / (x * beta::beta(self.freedom_1 / 2.0, self.freedom_2 / 2.0))
329 }
330 }
331
332 fn ln_pdf(&self, x: f64) -> f64 {
351 self.pdf(x).ln()
352 }
353}
354
355#[rustfmt::skip]
356#[cfg(all(test, feature = "nightly"))]
357mod tests {
358 use crate::statistics::*;
359 use crate::distribution::{ContinuousCDF, Continuous, FisherSnedecor};
360 use crate::distribution::internal::*;
361 use crate::consts::ACC;
362
363 fn try_create(freedom_1: f64, freedom_2: f64) -> FisherSnedecor {
364 let n = FisherSnedecor::new(freedom_1, freedom_2);
365 assert!(n.is_ok());
366 n.unwrap()
367 }
368
369 fn create_case(freedom_1: f64, freedom_2: f64) {
370 let n = try_create(freedom_1, freedom_2);
371 assert_eq!(freedom_1, n.freedom_1());
372 assert_eq!(freedom_2, n.freedom_2());
373 }
374
375 fn bad_create_case(freedom_1: f64, freedom_2: f64) {
376 let n = FisherSnedecor::new(freedom_1, freedom_2);
377 assert!(n.is_err());
378 }
379
380 fn get_value<F>(freedom_1: f64, freedom_2: f64, eval: F) -> f64
381 where F: Fn(FisherSnedecor) -> f64
382 {
383 let n = try_create(freedom_1, freedom_2);
384 eval(n)
385 }
386
387 fn test_case<F>(freedom_1: f64, freedom_2: f64, expected: f64, eval: F)
388 where F: Fn(FisherSnedecor) -> f64
389 {
390 let x = get_value(freedom_1, freedom_2, eval);
391 assert_eq!(expected, x);
392 }
393
394 fn test_almost<F>(freedom_1: f64, freedom_2: f64, expected: f64, acc: f64, eval: F)
395 where F: Fn(FisherSnedecor) -> f64
396 {
397 let x = get_value(freedom_1, freedom_2, eval);
398 assert_almost_eq!(expected, x, acc);
399 }
400
401 #[test]
402 fn test_create() {
403 create_case(0.1, 0.1);
404 create_case(1.0, 0.1);
405 create_case(10.0, 0.1);
406 create_case(0.1, 1.0);
407 create_case(1.0, 1.0);
408 create_case(10.0, 1.0);
409 }
410
411 #[test]
412 fn test_bad_create() {
413 bad_create_case(f64::NAN, f64::NAN);
414 bad_create_case(0.0, f64::NAN);
415 bad_create_case(-1.0, f64::NAN);
416 bad_create_case(-10.0, f64::NAN);
417 bad_create_case(f64::NAN, 0.0);
418 bad_create_case(0.0, 0.0);
419 bad_create_case(-1.0, 0.0);
420 bad_create_case(-10.0, 0.0);
421 bad_create_case(f64::NAN, -1.0);
422 bad_create_case(0.0, -1.0);
423 bad_create_case(-1.0, -1.0);
424 bad_create_case(-10.0, -1.0);
425 bad_create_case(f64::NAN, -10.0);
426 bad_create_case(0.0, -10.0);
427 bad_create_case(-1.0, -10.0);
428 bad_create_case(-10.0, -10.0);
429 bad_create_case(f64::INFINITY, 0.1);
430 bad_create_case(0.1, f64::INFINITY);
431 bad_create_case(f64::INFINITY, f64::INFINITY);
432 }
433
434 #[test]
435 fn test_mean() {
436 let mean = |x: FisherSnedecor| x.mean().unwrap();
437 test_case(0.1, 10.0, 1.25, mean);
438 test_case(1.0, 10.0, 1.25, mean);
439 test_case(10.0, 10.0, 1.25, mean);
440 }
441
442 #[test]
443 #[should_panic]
444 fn test_mean_with_low_d2() {
445 let mean = |x: FisherSnedecor| x.mean().unwrap();
446 get_value(0.1, 0.1, mean);
447 }
448
449 #[test]
450 fn test_variance() {
451 let variance = |x: FisherSnedecor| x.variance().unwrap();
452 test_almost(0.1, 10.0, 42.1875, 1e-14, variance);
453 test_case(1.0, 10.0, 4.6875, variance);
454 test_case(10.0, 10.0, 0.9375, variance);
455 }
456
457 #[test]
458 #[should_panic]
459 fn test_variance_with_low_d2() {
460 let variance = |x: FisherSnedecor| x.variance().unwrap();
461 get_value(0.1, 0.1, variance);
462 }
463
464 #[test]
465 fn test_skewness() {
466 let skewness = |x: FisherSnedecor| x.skewness().unwrap();
467 test_almost(0.1, 10.0, 15.78090735784977089658, 1e-14, skewness);
468 test_case(1.0, 10.0, 5.773502691896257645091, skewness);
469 test_case(10.0, 10.0, 3.614784456460255759501, skewness);
470 }
471
472 #[test]
473 #[should_panic]
474 fn test_skewness_with_low_d2() {
475 let skewness = |x: FisherSnedecor| x.skewness().unwrap();
476 get_value(0.1, 0.1, skewness);
477 }
478
479 #[test]
480 fn test_mode() {
481 let mode = |x: FisherSnedecor| x.mode().unwrap();
482 test_case(10.0, 0.1, 0.0380952380952380952381, mode);
483 test_case(10.0, 1.0, 4.0 / 15.0, mode);
484 test_case(10.0, 10.0, 2.0 / 3.0, mode);
485 }
486
487 #[test]
488 #[should_panic]
489 fn test_mode_with_low_d1() {
490 let mode = |x: FisherSnedecor| x.mode().unwrap();
491 get_value(0.1, 0.1, mode);
492 }
493
494 #[test]
495 fn test_min_max() {
496 let min = |x: FisherSnedecor| x.min();
497 let max = |x: FisherSnedecor| x.max();
498 test_case(1.0, 1.0, 0.0, min);
499 test_case(1.0, 1.0, f64::INFINITY, max);
500 }
501
502 #[test]
503 fn test_pdf() {
504 let pdf = |arg: f64| move |x: FisherSnedecor| x.pdf(arg);
505 test_almost(0.1, 0.1, 0.0234154207226588982471, 1e-16, pdf(1.0));
506 test_almost(1.0, 0.1, 0.0396064560910663979961, 1e-16, pdf(1.0));
507 test_almost(10.0, 0.1, 0.0418440630400545297349, 1e-14, pdf(1.0));
508 test_almost(0.1, 1.0, 0.0396064560910663979961, 1e-16, pdf(1.0));
509 test_almost(1.0, 1.0, 0.1591549430918953357689, 1e-16, pdf(1.0));
510 test_almost(10.0, 1.0, 0.230361989229138647108, 1e-16, pdf(1.0));
511 test_almost(0.1, 0.1, 0.00221546909694001013517, 1e-18, pdf(10.0));
512 test_almost(1.0, 0.1, 0.00369960370387922619592, 1e-17, pdf(10.0));
513 test_almost(10.0, 0.1, 0.00390179721174142927402, 1e-15, pdf(10.0));
514 test_almost(0.1, 1.0, 0.00319864073359931548273, 1e-17, pdf(10.0));
515 test_almost(1.0, 1.0, 0.009150765837179460915678, 1e-17, pdf(10.0));
516 test_almost(10.0, 1.0, 0.0116493859171442148446, 1e-17, pdf(10.0));
517 test_almost(0.1, 10.0, 0.00305087016058573989694, 1e-15, pdf(10.0));
518 test_almost(1.0, 10.0, 0.00271897749113479577864, 1e-17, pdf(10.0));
519 test_almost(10.0, 10.0, 2.4289227234060500084E-4, 1e-18, pdf(10.0));
520 }
521
522 #[test]
523 fn test_ln_pdf() {
524 let ln_pdf = |arg: f64| move |x: FisherSnedecor| x.ln_pdf(arg);
525 test_almost(0.1, 0.1, 0.0234154207226588982471f64.ln(), 1e-15, ln_pdf(1.0));
526 test_almost(1.0, 0.1, 0.0396064560910663979961f64.ln(), 1e-15, ln_pdf(1.0));
527 test_almost(10.0, 0.1, 0.0418440630400545297349f64.ln(), 1e-13, ln_pdf(1.0));
528 test_almost(0.1, 1.0, 0.0396064560910663979961f64.ln(), 1e-15, ln_pdf(1.0));
529 test_almost(1.0, 1.0, 0.1591549430918953357689f64.ln(), 1e-15, ln_pdf(1.0));
530 test_almost(10.0, 1.0, 0.230361989229138647108f64.ln(), 1e-15, ln_pdf(1.0));
531 test_case(0.1, 0.1, 0.00221546909694001013517f64.ln(), ln_pdf(10.0));
532 test_almost(1.0, 0.1, 0.00369960370387922619592f64.ln(), 1e-15, ln_pdf(10.0));
533 test_almost(10.0, 0.1, 0.00390179721174142927402f64.ln(), 1e-13, ln_pdf(10.0));
534 test_almost(0.1, 1.0, 0.00319864073359931548273f64.ln(), 1e-15, ln_pdf(10.0));
535 test_almost(1.0, 1.0, 0.009150765837179460915678f64.ln(), 1e-15, ln_pdf(10.0));
536 test_case(10.0, 1.0, 0.0116493859171442148446f64.ln(), ln_pdf(10.0));
537 test_almost(0.1, 10.0, 0.00305087016058573989694f64.ln(), 1e-13, ln_pdf(10.0));
538 test_case(1.0, 10.0, 0.00271897749113479577864f64.ln(), ln_pdf(10.0));
539 test_almost(10.0, 10.0, 2.4289227234060500084E-4f64.ln(), 1e-14, ln_pdf(10.0));
540 }
541
542 #[test]
543 fn test_cdf() {
544 let cdf = |arg: f64| move |x: FisherSnedecor| x.cdf(arg);
545 test_almost(0.1, 0.1, 0.44712986033425140335, 1e-15, cdf(0.1));
546 test_almost(1.0, 0.1, 0.08156522095104674015, 1e-15, cdf(0.1));
547 test_almost(10.0, 0.1, 0.033184005716276536322, 1e-13, cdf(0.1));
548 test_almost(0.1, 1.0, 0.74378710917986379989, 1e-15, cdf(0.1));
549 test_almost(1.0, 1.0, 0.1949822290421366451595, 1e-16, cdf(0.1));
550 test_almost(10.0, 1.0, 0.0101195597354337146205, 1e-17, cdf(0.1));
551 test_almost(0.1, 0.1, 0.5, 1e-15, cdf(1.0));
552 test_almost(1.0, 0.1, 0.16734351500944271141, 1e-14, cdf(1.0));
553 test_almost(10.0, 0.1, 0.12207560664741704938, 1e-13, cdf(1.0));
554 test_almost(0.1, 1.0, 0.83265648499055728859, 1e-15, cdf(1.0));
555 test_almost(1.0, 1.0, 0.5, 1e-15, cdf(1.0));
556 test_almost(10.0, 1.0, 0.340893132302059872675, 1e-15, cdf(1.0));
557 }
558
559 #[test]
560 fn test_cdf_lower_bound() {
561 let cdf = |arg: f64| move |x: FisherSnedecor| x.cdf(arg);
562 test_case(0.1, 0.1, 0.0, cdf(-1.0));
563 }
564
565 #[test]
566 fn test_sf() {
567 let sf = |arg: f64| move |x: FisherSnedecor| x.sf(arg);
568 test_almost(0.1, 0.1, 0.5528701396657489, 1e-12, sf(0.1));
569 test_almost(1.0, 0.1, 0.9184347790489533, 1e-12, sf(0.1));
570 test_almost(10.0, 0.1, 0.9668159942836896, 1e-12, sf(0.1));
571 test_almost(0.1, 1.0, 0.25621289082013654, 1e-12, sf(0.1));
572 test_almost(1.0, 1.0, 0.8050177709578634, 1e-12, sf(0.1));
573 test_almost(10.0, 1.0, 0.9898804402645662, 1e-12, sf(0.1));
574 test_almost(0.1, 0.1, 0.5, 1e-15, sf(1.0));
575 test_almost(1.0, 0.1, 0.8326564849905562, 1e-12, sf(1.0));
576 test_almost(10.0, 0.1, 0.8779243933525519, 1e-12, sf(1.0));
577 test_almost(0.1, 1.0, 0.16734351500944344, 1e-12, sf(1.0));
578 test_almost(1.0, 1.0, 0.5, 1e-12, sf(1.0));
579 test_almost(10.0, 1.0, 0.65910686769794, 1e-12, sf(1.0));
580 }
581
582 #[test]
583 fn test_sf_lower_bound() {
584 let sf = |arg: f64| move |x: FisherSnedecor| x.sf(arg);
585 test_case(0.1, 0.1, 1.0, sf(-1.0));
586 }
587
588 #[test]
589 fn test_continuous() {
590 test::check_continuous_distribution(&try_create(10.0, 10.0), 0.0, 10.0);
591 }
592}