1pub fn t_upper(t: f64, df: f64) -> f64 {
5 if t.is_nan() || df.is_nan() || df <= 0.0 {
6 return f64::NAN;
7 }
8 if t.is_infinite() {
9 return if t > 0.0 { 0.0 } else { 1.0 };
10 }
11 let x = df / (df + t * t);
12 let half = 0.5 * incomplete_beta(0.5 * df, 0.5, x);
13 if t > 0.0 { half } else { 1.0 - half }
14}
15
16pub fn incomplete_beta(a: f64, b: f64, x: f64) -> f64 {
19 if x <= 0.0 {
20 return 0.0;
21 }
22 if x >= 1.0 {
23 return 1.0;
24 }
25 let front =
26 (ln_gamma(a + b) - ln_gamma(a) - ln_gamma(b) + a * x.ln() + b * (1.0 - x).ln()).exp();
27 if x < (a + 1.0) / (a + b + 2.0) {
28 front * continued_fraction(a, b, x) / a
29 } else {
30 1.0 - front * continued_fraction(b, a, 1.0 - x) / b
31 }
32}
33
34fn continued_fraction(a: f64, b: f64, x: f64) -> f64 {
35 const TINY: f64 = 1e-300;
36 let mut c = 1.0;
37 let mut d = 1.0 - (a + b) * x / (a + 1.0);
38 if d.abs() < TINY {
39 d = TINY;
40 }
41 d = 1.0 / d;
42 let mut h = d;
43 for m in 1..=1000 {
44 let m = m as f64;
45 for numerator in [
46 m * (b - m) * x / ((a + 2.0 * m - 1.0) * (a + 2.0 * m)),
47 -(a + m) * (a + b + m) * x / ((a + 2.0 * m) * (a + 2.0 * m + 1.0)),
48 ] {
49 d = 1.0 + numerator * d;
50 if d.abs() < TINY {
51 d = TINY;
52 }
53 c = 1.0 + numerator / c;
54 if c.abs() < TINY {
55 c = TINY;
56 }
57 d = 1.0 / d;
58 h *= d * c;
59 }
60 if (d * c - 1.0).abs() < 1e-15 {
61 break;
62 }
63 }
64 h
65}
66
67fn ln_gamma(x: f64) -> f64 {
69 const G: [f64; 9] = [
70 0.999_999_999_999_809_9,
71 676.520_368_121_885_1,
72 -1_259.139_216_722_402_8,
73 771.323_428_777_653_1,
74 -176.615_029_162_140_6,
75 12.507_343_278_686_905,
76 -0.138_571_095_265_720_12,
77 9.984_369_578_019_572e-6,
78 1.505_632_735_149_311_6e-7,
79 ];
80 if x < 0.5 {
81 let pi = std::f64::consts::PI;
82 return (pi / (pi * x).sin()).ln() - ln_gamma(1.0 - x);
83 }
84 let x = x - 1.0;
85 let t = x + 7.5;
86 let series = G[1..]
87 .iter()
88 .enumerate()
89 .fold(G[0], |s, (i, g)| s + g / (x + i as f64 + 1.0));
90 0.5 * (2.0 * std::f64::consts::PI).ln() + (x + 0.5) * t.ln() - t + series.ln()
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[test]
98 fn known_values() {
99 assert!((ln_gamma(5.0) - 24f64.ln()).abs() < 1e-12);
100 assert!((ln_gamma(0.5) - std::f64::consts::PI.sqrt().ln()).abs() < 1e-12);
101 assert!(
102 (ln_gamma(0.25) - 3.625_609_908_221_908f64.ln()).abs() < 1e-12,
103 "reflection below 0.5"
104 );
105 assert_eq!(
106 (
107 incomplete_beta(2.0, 3.0, 0.0),
108 incomplete_beta(2.0, 3.0, 1.0)
109 ),
110 (0.0, 1.0)
111 );
112 assert!((incomplete_beta(1.0, 1.0, 0.3) - 0.3).abs() < 1e-14);
114 assert!((incomplete_beta(3.0, 1.0, 0.6) - 0.216).abs() < 1e-14);
115 assert!((t_upper(0.0, 7.0) - 0.5).abs() < 1e-15);
116 assert_eq!(
117 (t_upper(f64::INFINITY, 3.0), t_upper(f64::NEG_INFINITY, 3.0)),
118 (0.0, 1.0)
119 );
120 assert!(t_upper(f64::NAN, 3.0).is_nan() && t_upper(1.0, 0.0).is_nan());
121 assert!((t_upper(1.0, 1.0) - 0.25).abs() < 1e-14);
123 assert!((t_upper(-1.0, 1.0) - 0.75).abs() < 1e-14);
124 }
125}