1use crate::bessel::i0_exp;
30use crate::double_double::DoubleDouble;
31use crate::gamma::lgamma::lgamma_core;
32use crate::logs::fast_log_d_to_dd;
33
34fn core_gamma_p(a: f64, x: f64) -> (DoubleDouble, Option<f64>) {
35 const BIG: f64 = 4503599627370496.0;
36 const BIG_INV: f64 = 2.22044604925031308085e-16;
37
38 const EPS: f64 = f64::EPSILON;
39
40 let da = a;
41 let dx = x;
42
43 let r = DoubleDouble::full_add_f64(-lgamma_core(a).0, -dx);
44 let ax = DoubleDouble::mul_f64_add(fast_log_d_to_dd(x), da, r).to_f64();
45
46 if ax <= -709.78271289338399 {
47 if a < x {
48 return (DoubleDouble::default(), Some(1.0));
49 }
50 return (DoubleDouble::default(), Some(0.0));
51 }
52 if ax >= 709.783 {
53 return (DoubleDouble::default(), Some(f64::INFINITY));
54 }
55
56 if x <= 1.0 || x <= a {
57 let mut r2 = DoubleDouble::new(0., da);
58 let mut c2 = DoubleDouble::new(0., 1.0);
59 let mut ans2 = DoubleDouble::new(0., 1.0);
60 let v_e = i0_exp(ax);
61 for _ in 0..200 {
62 r2 = DoubleDouble::full_add_f64(r2, 1.0);
63 c2 = DoubleDouble::quick_mult(DoubleDouble::from_f64_div_dd(dx, r2), c2);
64 c2 = DoubleDouble::from_exact_add(c2.hi, c2.lo);
65 ans2 = DoubleDouble::add(ans2, c2);
66
67 if c2.hi / ans2.hi <= EPS {
68 break;
69 }
70 }
71 let v0 = DoubleDouble::quick_mult(v_e, ans2);
72 return (DoubleDouble::div_dd_f64(v0, da), None);
73 }
74
75 let v_e = i0_exp(ax);
76
77 let mut y = 1.0 - da;
78 let mut z = dx + y + 1.0;
79 let mut c = 0i32;
80
81 let mut p3 = 1.0;
82 let mut q3 = dx;
83 let mut p2 = dx + 1.0;
84 let mut q2 = z * dx;
85 let mut ans = p2 / q2;
86
87 for _ in 0..200 {
88 y += 1.0;
89 z += 2.0;
90 c += 1;
91 let yc = y * c as f64;
92
93 let p = p2 * z - p3 * yc;
94 let q = q2 * z - q3 * yc;
95
96 p3 = p2;
97 p2 = p;
98 q3 = q2;
99 q2 = q;
100
101 if p.abs() > BIG {
102 p3 *= BIG_INV;
103 p2 *= BIG_INV;
104 q3 *= BIG_INV;
105 q2 *= BIG_INV;
106 }
107
108 if q != 0.0 {
109 let nextans = p / q;
110 let error = ((ans - nextans) / nextans).abs();
111 ans = nextans;
112
113 if error <= EPS {
114 break;
115 }
116 }
117 }
118
119 (DoubleDouble::mul_f64_add_f64(-v_e, ans, 1.0), None)
120}
121
122pub fn f_gamma_q(a: f64, x: f64) -> f64 {
124 let aa = a.to_bits();
125 let ax = x.to_bits();
126
127 if aa >= 0x7ffu64 << 52 || aa == 0 || ax >= 0x7ffu64 << 52 || ax == 0 {
128 if (aa >> 63) != 0 || (ax >> 63) != 0 {
129 return f64::NAN;
131 }
132 if aa.wrapping_shl(1) == 0 {
133 return 1.0;
135 }
136 if ax.wrapping_shl(1) == 0 {
137 return 0.;
139 }
140 if a.is_infinite() {
141 return f64::INFINITY;
143 }
144 if x.is_infinite() {
145 return f64::INFINITY;
147 }
148 return a + f64::NAN;
149 }
150
151 const EPS: f64 = f64::EPSILON;
152
153 const BIG: f64 = 4503599627370496.0;
154 const BIG_INV: f64 = 2.22044604925031308085e-16;
155
156 if x < 1.0 || x <= a {
157 let gamma_p = core_gamma_p(a, x);
158 return match gamma_p.1 {
159 None => {
160 let z = DoubleDouble::full_add_f64(-gamma_p.0, 1.);
161 z.to_f64()
162 }
163 Some(v) => v,
164 };
165 }
166
167 let da = a;
168 let dx = x;
169
170 let r = DoubleDouble::full_add_f64(-lgamma_core(a).0, -dx);
171 let ax = DoubleDouble::mul_f64_add(fast_log_d_to_dd(x), da, r).to_f64();
172
173 if ax <= -709.78271289338399 {
174 if a < x {
175 return 1.0;
176 }
177 return 0.0;
178 }
179 if ax >= 709.783 {
180 return f64::INFINITY;
181 }
182
183 let mut y = 1.0 - da;
184 let mut z = dx + y + 1.0;
185 let mut c = 0.0;
186 let mut pkm2 = 1.0;
187 let mut qkm2 = dx;
188 let mut pkm1 = dx + 1.0;
189 let mut qkm1 = z * dx;
190 let mut ans = pkm1 / qkm1;
191 for _ in 0..200 {
192 y += 1.0;
193 z += 2.0;
194 c += 1.0;
195 let yc = y * c;
196 let pk = pkm1 * z - pkm2 * yc;
197 let qk = qkm1 * z - qkm2 * yc;
198
199 pkm2 = pkm1;
200 pkm1 = pk;
201 qkm2 = qkm1;
202 qkm1 = qk;
203
204 if pk.abs() > BIG {
205 pkm2 *= BIG_INV;
206 pkm1 *= BIG_INV;
207 qkm2 *= BIG_INV;
208 qkm1 *= BIG_INV;
209 }
210
211 if qk != 0.0 {
212 let r = pk / qk;
213 let t = ((ans - r) / r).abs();
214 ans = r;
215
216 if t <= EPS {
217 break;
218 }
219 }
220 }
221 let v_exp = i0_exp(ax);
222 DoubleDouble::quick_mult_f64(v_exp, ans).to_f64()
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228 #[test]
229 fn test_f_beta_pf() {
230 assert_eq!(f_gamma_q(1., f64::INFINITY), f64::INFINITY);
231 assert_eq!(f_gamma_q(23.421, 41.), 0.0011305253882165434);
232 assert_eq!(f_gamma_q(0.764, 0.432123), 0.5224700360458718);
233 assert_eq!(f_gamma_q(0.421, 1.), 0.12721313819176905);
234 assert!(f_gamma_q(-1., 12.).is_nan());
235 assert!(f_gamma_q(1., -12.).is_nan());
236 assert!(f_gamma_q(f64::NAN, 12.).is_nan());
237 assert!(f_gamma_q(1., f64::NAN).is_nan());
238 assert_eq!(f_gamma_q(f64::INFINITY, f64::INFINITY), f64::INFINITY);
239 assert_eq!(f_gamma_q(f64::INFINITY, 5.32), f64::INFINITY);
240 }
241}