qualia_core_db/solvers/statistics/distributions/
students_t.rs1use super::special::{betai, ln_gamma};
6
7pub fn pdf(t: f64, nu: f64) -> f64 {
9 debug_assert!(nu > 0.0);
10 let c = (ln_gamma((nu + 1.0) / 2.0) - ln_gamma(nu / 2.0)).exp()
11 / (nu * std::f64::consts::PI).sqrt();
12 c * (1.0 + t * t / nu).powf(-(nu + 1.0) / 2.0)
13}
14
15pub fn cdf(t: f64, nu: f64) -> f64 {
18 debug_assert!(nu > 0.0);
19 let x = nu / (nu + t * t);
20 let ib = 0.5 * betai(nu / 2.0, 0.5, x);
21 if t >= 0.0 {
22 1.0 - ib
23 } else {
24 ib
25 }
26}
27
28pub fn two_sided_p(t: f64, nu: f64) -> f64 {
30 let x = nu / (nu + t * t);
31 betai(nu / 2.0, 0.5, x)
32}
33
34pub fn upper_p(t: f64, nu: f64) -> f64 {
36 1.0 - cdf(t, nu)
37}
38
39pub fn quantile(p: f64, nu: f64) -> f64 {
41 super::invert_cdf(p, None, |t| cdf(t, nu))
42}
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47
48 #[test]
49 fn cdf_symmetry_and_center() {
50 assert!((cdf(0.0, 5.0) - 0.5).abs() < 1e-12);
51 for &(t, nu) in &[(1.3, 7.0), (2.5, 12.0), (0.8, 3.0)] {
53 assert!((cdf(-t, nu) - (1.0 - cdf(t, nu))).abs() < 1e-10);
54 }
55 }
56
57 #[test]
58 fn known_critical_values() {
59 assert!((quantile(0.975, 10.0) - 2.228_138_851).abs() < 1e-4);
61 assert!((quantile(0.975, 1.0) - 12.706_204_736).abs() < 1e-3); assert!((quantile(0.975, 1_000_000.0) - 1.959_963_98).abs() < 1e-3);
63 }
64
65 #[test]
66 fn two_sided_p_matches_tail() {
67 assert!((two_sided_p(2.228_138_851, 10.0) - 0.05).abs() < 1e-4);
69 assert!((two_sided_p(0.0, 5.0) - 1.0).abs() < 1e-12);
70 }
71
72 #[test]
73 fn quantile_inverts_cdf() {
74 for &p in &[0.01, 0.05, 0.5, 0.9, 0.99] {
75 let t = quantile(p, 8.0);
76 assert!((cdf(t, 8.0) - p).abs() < 1e-8, "p={p}");
77 }
78 }
79}