thermite_special/specialized/generic/
gamma.rs1use thermite::{
2 element::FloatElementWithBits,
3 mask::GenericMask,
4 math::{
5 CoreMathWithPolicy as _, TranscendentalMathWithPolicy as _,
6 policy::{DenormalBehavior, Policy, PrecisionPolicy, policies::ExtraPrecision},
7 },
8 prelude::*,
9};
10
11use crate::specialized::SpecializedSpecialMath;
12use crate::tables::gamma::Lanczos;
13
14#[inline(always)]
24pub fn tgamma_impl<P, E, V, const N: usize>(z_in: V, l: &Lanczos<E, N>, int_cap: E, ln_max: E) -> V
25where
26 P: Policy,
27 E: FloatElementWithBits,
28 V: FloatVectorWithBits<Element = E> + SpecializedSpecialMath<E>,
29{
30 let mut z = z_in.flush_denormals_p::<P>();
31
32 let orig_z = z;
33
34 let is_negative = z.is_negative();
35 let mut reflected = GenericMask::FALSY;
36
37 let mut res = V::ONE;
38
39 if const { P::POLICY.avoid_branching } || is_negative.any() {
42 reflected = is_negative;
43 let refl_res = z * z.sin_pi_p::<P>(); res = reflected.select(refl_res, res);
45 z = z.abs();
46 }
47
48 let is_neg_int = is_negative & orig_z.cmp_eq(orig_z.floor()) & orig_z.cmp_ne(V::ZERO);
50 let is_zero = orig_z.cmp_eq(V::ZERO);
51
52 if const { P::POLICY.precision.ge(PrecisionPolicy::Best) } {
56 let needs_shift = z.cmp_lt(V::ONE) & z.cmp_ge(V::SQRT_EPSILON);
57 res = needs_shift.select(res / z, res);
58 z = needs_shift.select(z + V::ONE, z);
59 }
60
61 let mut is_int = GenericMask::FALSY;
64 let mut int_res = V::ONE;
65
66 if const { P::POLICY.precision.ge(PrecisionPolicy::Best) } {
67 let zf = z.floor();
68 is_int = zf.cmp_eq(z) & zf.cmp_lt(V::splat(int_cap)) & !is_neg_int & !is_zero;
70
71 if thermite::unlikely(is_int.any()) {
72 let mut j = V::ONE;
73 let mut k = j.cmp_lt(zf) & is_int;
75
76 while k.any() {
77 V::_loop_hint();
78
79 int_res = k.select(int_res * j, int_res);
80 j += V::ONE;
81 k = j.cmp_lt(zf) & is_int;
82 }
83
84 if thermite::unlikely(is_int.all()) {
85 return int_res;
86 }
87 }
88 }
89
90 let gh = V::splat(l.g) - V::HALF;
93
94 let lanczos_sum = z.poly_rev_n_p::<P, _>(&l.p_rev) / z.poly_rev_n_p::<P, _>(&l.q_rev);
96
97 let zgh = z + gh;
98 let lzgh = zgh.ln_p::<P>();
99
100 let very_large = (z * lzgh).cmp_gt(V::splat(ln_max));
102
103 let h = zgh.powf_p::<P>(very_large.select(z.mul_sube(V::HALF, V::splat(E::from_f64(0.25))), z - V::HALF));
105
106 let denom = if const { P::POLICY.precision.ge(PrecisionPolicy::Best) } {
108 lanczos_sum / zgh.exp_p::<P>()
109 } else {
110 lanczos_sum * (-zgh).exp_p::<P>()
111 };
112
113 let hd = h * denom;
119 let mut normal_res = very_large.select(hd * h, hd);
120
121 if const { P::POLICY.check_overflow } {
122 normal_res = z.cmp_ge(V::splat(int_cap)).select(V::INFINITY, normal_res);
137 }
138
139 if const { P::POLICY.precision.ge(PrecisionPolicy::Best) } {
141 let is_tiny = z.cmp_lt(V::SQRT_EPSILON);
142 let tiny_res = z.approx_reciprocal_p::<P>() - V::EULER_GAMMA;
143 res *= is_tiny.select(tiny_res, normal_res);
144 } else {
145 res *= normal_res;
146 }
147
148 let zero_res = is_negative.select(V::NEG_INFINITY, V::INFINITY);
150 let result = reflected.select(-V::PI / res, is_int.select(int_res, res));
151 let mut result = is_neg_int.select(V::NAN, result);
152
153 if const {
154 P::POLICY.precision.ge(PrecisionPolicy::Best)
155 && matches!(P::POLICY.denormal_behavior, DenormalBehavior::Preserve)
156 } {
157 let is_subnormal = z.is_subnormal();
158
159 if thermite::unlikely(is_subnormal.any()) {
160 result = is_subnormal.select(V::ONE / orig_z, result);
161 }
162 }
163
164 is_zero.select(zero_res, result)
165}
166
167#[inline(always)]
173pub fn lgamma_r_impl<P, E, V, const N: usize>(z_in: V, l: &Lanczos<E, N>) -> (V, V)
174where
175 P: Policy,
176 E: FloatElementWithBits,
177 V: FloatVectorWithBits<Element = E> + SpecializedSpecialMath<E>,
178{
179 let mut z = z_in.flush_denormals_p::<P>();
180 let mut signum = V::ONE;
181
182 let reflect = z.is_negative();
183
184 let mut t = V::ONE;
185
186 if const { P::POLICY.avoid_branching } || reflect.any() {
187 let pix = z * z.sin_pi_p::<P>(); signum |= reflect.select(pix.signed_zero(), signum);
190
191 t = reflect.select(pix.abs(), t);
192 z = z.abs();
193 }
194
195 let b = z - V::HALF;
196 let g = V::splat(l.g);
197
198 let mut lanczos_sum = z.poly_rational_n_p::<P, _, _>(&l.p_expg_scaled, &l.q);
199
200 let mut a = (b + g).ln_p::<P>() - V::ONE;
202
203 if const { P::POLICY.precision.gt(PrecisionPolicy::Average) } {
205 let is_not_tiny = z.cmp_ge(V::SQRT_EPSILON);
206
207 lanczos_sum = is_not_tiny.select(lanczos_sum, z.approx_reciprocal_p::<P>() - V::EULER_GAMMA);
209
210 a = a.zz(is_not_tiny);
213 }
214
215 let c = (lanczos_sum * t).ln_p::<P>();
216
217 let res = a.mul_adde(b, c);
218
219 let y = reflect.select(V::LN_PI - res, res);
220
221 (y, signum)
222}
223
224#[inline(always)]
232pub fn beta_impl<P, E, V, const N: usize>(a: V, b: V, l: &Lanczos<E, N>) -> V
233where
234 P: Policy,
235 E: FloatElementWithBits,
236 V: FloatVectorWithBits<Element = E> + SpecializedSpecialMath<E>,
237{
238 let (a, b) = (a.flush_denormals_p::<P>(), b.flush_denormals_p::<P>());
239
240 let is_valid = a.cmp_gt(V::ZERO) & b.cmp_gt(V::ZERO);
241
242 if const { P::POLICY.check_overflow && !P::POLICY.avoid_branching } && is_valid.none() {
243 return V::NAN;
244 }
245
246 let c = a + b;
247
248 let (a, b) = (a.max(b), a.min(b));
250
251 let mut result = a.poly_rational_n_p::<P, _, _>(&l.p_expg_scaled, &l.q)
252 * (b.poly_rational_n_p::<P, _, _>(&l.p_expg_scaled, &l.q)
253 / c.poly_rational_n_p::<P, _, _>(&l.p_expg_scaled, &l.q));
254
255 let gh = V::splat(l.g) - V::HALF;
256
257 let agh = a + gh;
258 let bgh = b + gh;
259 let cgh = c + gh;
260
261 let agh_d_cgh = agh / cgh;
262 let bgh_d_cgh = bgh / cgh;
263 let agh_p_bgh = agh * bgh;
264 let cgh_p_cgh = cgh * cgh;
265
266 let base = cgh
267 .cmp_gt(V::splat(E::from_f64(1e10)))
268 .select(agh_d_cgh * bgh_d_cgh, agh_p_bgh / cgh_p_cgh);
269
270 let denom = if const { P::POLICY.precision.gt(PrecisionPolicy::Average) } {
271 V::SQRT_E / bgh.sqrt()
272 } else {
273 V::SQRT_E * bgh.inverse_sqrt_p::<ExtraPrecision<P>>()
275 };
276
277 result *= agh_d_cgh.powf_p::<P>(a - V::HALF - b) * (base.powf_p::<P>(b) * denom);
279
280 if const { P::POLICY.check_overflow } {
281 result = is_valid.select(result, V::NAN);
282 }
283
284 result
285}