1use thermite::{
2 math::{
3 TranscendentalMathWithPolicy,
4 policy::{
5 DenormalBehavior, PrecisionPolicy,
6 policies::{CheckOverflow, ExtraPrecision, MediumPrecision, WorstPrecision},
7 },
8 specialized::SpecializedTranscendentalMath,
9 specialized::reference::{is_reference, map1, map1x2, map2},
10 },
11 prelude::*,
12 register::NativeCapability,
13};
14
15use crate::RealSpecialMathWithPolicy as _;
16
17use super::*;
18
19impl<V: FloatVectorWithBits<Element = f32>> SpecializedSpecialMath<f32> for V
20where
21 V: TranscendentalMathWithPolicy<Element = f32>,
22 V: SpecializedTranscendentalMath<f32>,
23 V: thermite::math::PrimalProjection<Primal = V>,
27 V: thermite::math::RealMathWithPolicy<Element = f32>,
28{
29 #[inline(always)]
31 fn exp_two_sum(a: Self, b: Self) -> (Self, Self) {
32 a.two_sum(b)
33 }
34
35 #[inline(always)]
38 fn hermite_function_n<P: Policy, const N: usize>(mut x: Self) -> Self {
39 #[cfg(not(target_arch = "spirv"))]
40 if let Some(new_x) = thermite::math::specialized::FlushDenormals::<P>::flush_denormals([x]) {
41 x = new_x[0];
42 }
43
44 generic::hermite::hermite_function_n::<P, _, _, N, true>(x)
45 }
46
47 #[inline(always)]
48 fn hermite_function<P: Policy>(mut x: Self, n: u32) -> Self {
49 #[cfg(not(target_arch = "spirv"))]
50 if let Some(new_x) = thermite::math::specialized::FlushDenormals::<P>::flush_denormals([x]) {
51 x = new_x[0];
52 }
53
54 generic::hermite::hermite_function::<P, _, _, true>(x, n)
55 }
56
57 #[inline(always)]
58 fn hermite_function_series_n<P: Policy, const N: usize>(self, coeffs: &[Self::Element; N]) -> Self {
59 generic::hermite::hermite_function_series::<P, _, _, N, true>(self, coeffs)
60 }
61
62 #[inline(always)]
63 fn hermite_function_series<P: Policy>(self, coeffs: &[Self::Element]) -> Self {
64 generic::hermite::hermite_function_series_slice::<P, _, _, true>(self, coeffs)
65 }
66
67 #[inline(always)]
68 fn zetac<P: Policy>(self) -> Self {
69 generic::zeta::zeta_impl::<P, _, _, true>(self)
70 }
71
72 #[inline(always)]
73 fn polylog<P: Policy>(self, order: crate::PolylogOrder<f32, i32>) -> Self {
74 generic::polylog::polylog_impl::<P, f32, Self>(self, order)
75 }
76
77 #[inline(always)]
78 fn zeta<P: Policy>(self) -> Self {
79 generic::zeta::zeta_impl::<P, _, _, false>(self)
80 }
81
82 #[inline(always)]
83 fn zeta_with_deriv<P: Policy, const ZETAC: bool>(self) -> (Self, Self) {
84 generic::zeta::zeta_core::<P, _, _, ZETAC, true>(self)
85 }
86
87 #[inline(always)]
88 fn bessel_i<P: Policy, const N: i32>(self) -> Self {
89 bessel_i_dispatch::<P, Self, N, false>(self)
90 }
91
92 #[inline(always)]
93 fn bessel_i_scaled<P: Policy, const N: i32>(self) -> Self {
94 bessel_i_dispatch::<P, Self, N, true>(self)
95 }
96
97 #[inline(always)]
98 fn bessel_k<P: Policy, const N: i32>(self) -> Self {
99 bessel_k_dispatch::<P, Self, N, false>(self)
100 }
101
102 #[inline(always)]
103 fn bessel_k_scaled<P: Policy, const N: i32>(self) -> Self {
104 bessel_k_dispatch::<P, Self, N, true>(self)
105 }
106
107 #[inline(always)]
108 fn bessel_j<P: Policy, const N: i32>(self) -> Self {
109 use crate::tables::bessel::jy::{BESSEL_J0_F32, BESSEL_J1_F32};
110 if const { is_reference::<P>() } {
114 let v = if const { N == 0 } {
115 map1(self, libm::j0f)
116 } else if const { N.unsigned_abs() == 1 } {
117 map1(self, libm::j1f)
118 } else {
119 map1(self, |v| libm::jnf(N.abs(), v))
120 };
121 return if const { bessel_reflect_negates(N) } { -v } else { v };
124 }
125 let v = if const { N == 0 } {
126 generic::bessel::jy::bessel_j0_impl::<P, f32, _, _, _, _>(self, &BESSEL_J0_F32)
127 } else if const { N.unsigned_abs() == 1 } {
128 generic::bessel::jy::bessel_j1_impl::<P, f32, _, _, _, _>(self, &BESSEL_J1_F32)
129 } else {
130 generic::bessel::jy::bessel_jn_pair_impl::<P, f32, _, _, _, _, _, _, _, N>(
131 self,
132 &BESSEL_J0_F32,
133 &BESSEL_J1_F32,
134 )
135 .1
136 };
137 if const { bessel_reflect_negates(N) } { -v } else { v }
139 }
140
141 #[inline(always)]
142 fn bessel_y<P: Policy, const N: i32>(self) -> Self {
143 use crate::tables::bessel::jy::{BESSEL_J0_F32, BESSEL_J1_F32, BESSEL_Y0_F32, BESSEL_Y1_F32};
144 if const { is_reference::<P>() } {
145 let v = if const { N == 0 } {
146 map1(self, libm::y0f)
147 } else if const { N.unsigned_abs() == 1 } {
148 map1(self, libm::y1f)
149 } else {
150 map1(self, |v| libm::ynf(N.abs(), v))
151 };
152 return if const { bessel_reflect_negates(N) } { -v } else { v };
153 }
154 let v = if const { N.unsigned_abs() >= 2 } {
155 let y0 = generic::bessel::jy::bessel_y_impl::<P, f32, _, _, _, _, _, _, _, _, false>(
158 self,
159 &BESSEL_Y0_F32,
160 &BESSEL_J0_F32,
161 );
162 let y1 = generic::bessel::jy::bessel_y_impl::<P, f32, _, _, _, _, _, _, _, _, true>(
163 self,
164 &BESSEL_Y1_F32,
165 &BESSEL_J1_F32,
166 );
167 generic::bessel::jy::bessel_yn_recur::<f32, _, N>(self, y0, y1).1
168 } else if const { N == 0 } {
169 generic::bessel::jy::bessel_y_impl::<P, f32, _, _, _, _, _, _, _, _, false>(
170 self,
171 &BESSEL_Y0_F32,
172 &BESSEL_J0_F32,
173 )
174 } else {
175 generic::bessel::jy::bessel_y_impl::<P, f32, _, _, _, _, _, _, _, _, true>(
176 self,
177 &BESSEL_Y1_F32,
178 &BESSEL_J1_F32,
179 )
180 };
181 if const { bessel_reflect_negates(N) } { -v } else { v }
183 }
184
185 #[inline(always)]
186 fn bessel_i_with_deriv<P: Policy, const N: i32, const SCALED: bool>(self) -> (Self, Self) {
187 bessel_i_deriv_dispatch::<P, Self, N, SCALED>(self)
188 }
189
190 #[inline(always)]
191 fn bessel_k_with_deriv<P: Policy, const N: i32, const SCALED: bool>(self) -> (Self, Self) {
192 bessel_k_deriv_dispatch::<P, Self, N, SCALED>(self)
193 }
194
195 #[inline(always)]
196 fn bessel_iv<P: Policy, const SCALED: bool>(self, order: crate::BesselOrder<Self, Self::Signed>) -> Self {
197 let order = order.simplify();
200 if let crate::BesselOrder::HalfInteger(k) = order {
201 return generic::bessel::half::bessel_ik_half::<P, f32, _, SCALED>(
202 Self::from_signed_integer(k) * Self::HALF,
203 self,
204 crate::tables::bessel::BESSEL_I0_F32.far_threshold,
205 )
206 .0;
207 }
208 let Some(n) = order.as_integer() else {
210 return generic::bessel::ik_real::bessel_ik_real::<P, f32, _, _, 11, 11, SCALED, true>(
211 order.to_real(),
212 self,
213 &crate::tables::lgamma1p::LGAMMA1P_F32,
214 crate::tables::bessel::BESSEL_I0_F32.far_threshold,
215 )
216 .0;
217 };
218 let nf = Self::from_signed_integer(n).abs();
221 let v = generic::bessel::ik::bessel_iv_impl::<P, f32, _, _, _, _, SCALED>(
222 self,
223 nf,
224 &crate::tables::bessel::BESSEL_I0_F32,
225 );
226 let i1 =
230 generic::bessel::ik::bessel_i1_impl::<P, _, _, _, _, SCALED>(self, &crate::tables::bessel::BESSEL_I1_F32);
231 nf.cmp_le(Self::ONE).select(
232 nf.cmp_le(Self::ZERO).select(
233 generic::bessel::ik::bessel_i0_impl::<P, _, _, _, _, SCALED>(
234 self,
235 &crate::tables::bessel::BESSEL_I0_F32,
236 ),
237 i1,
238 ),
239 v,
240 )
241 }
242
243 #[inline(always)]
244 fn bessel_kv<P: Policy, const SCALED: bool>(self, order: crate::BesselOrder<Self, Self::Signed>) -> Self {
245 let order = order.simplify();
247 if let crate::BesselOrder::HalfInteger(k) = order {
248 return generic::bessel::half::bessel_ik_half::<P, f32, _, SCALED>(
249 Self::from_signed_integer(k) * Self::HALF,
250 self,
251 crate::tables::bessel::BESSEL_I0_F32.far_threshold,
252 )
253 .1;
254 }
255 let Some(n) = order.as_integer() else {
257 return generic::bessel::ik_real::bessel_ik_real::<P, f32, _, _, 11, 11, SCALED, false>(
260 order.to_real(),
261 self,
262 &crate::tables::lgamma1p::LGAMMA1P_F32,
263 crate::tables::bessel::BESSEL_I0_F32.far_threshold,
264 )
265 .1;
266 };
267 let nf = Self::from_signed_integer(n).abs();
269 generic::bessel::ik::bessel_kv_impl::<P, f32, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, SCALED>(
270 self,
271 nf,
272 &crate::tables::bessel::BESSEL_K0_F32,
273 &crate::tables::bessel::BESSEL_K1_F32,
274 &crate::tables::bessel::BESSEL_I0_F32,
275 &crate::tables::bessel::BESSEL_I1_F32,
276 )
277 }
278
279 #[inline(always)]
280 fn bessel_jv<P: Policy>(self, order: crate::BesselOrder<Self, Self::Signed>) -> Self {
281 let order = order.simplify();
285 if let crate::BesselOrder::HalfInteger(k) = order {
286 return generic::bessel::half::bessel_jy_half::<P, f32, _>(Self::from_signed_integer(k) * Self::HALF, self)
287 .0;
288 }
289 let Some(n) = order.as_integer() else {
293 let nu = order.to_real();
294 return generic::bessel::jy_real::bessel_jy_real::<P, f32, _, 5, 11, 11, 13, 2>(
295 nu,
296 self,
297 Self::ZERO,
298 &crate::tables::lgamma1p::LGAMMA1P_F32,
299 )
300 .0;
301 };
302 if const { is_reference::<P>() } {
305 let mut out = self;
306 let mut i = 0;
307 while i < Self::LANES {
308 let k = n.extractv(i);
311 let r = libm::jnf(k.unsigned_abs() as i32, self.extractv(i));
312 out = out.insertv(i, if k < 0 && k % 2 != 0 { -r } else { r });
313 i += 1;
314 }
315 return out;
316 }
317 let (nf, flip) = bessel_reflect_v(Self::from_signed_integer(n));
318 generic::bessel::jy::bessel_jv_impl::<P, f32, _, _, _, _, _, _, _>(
319 self,
320 nf,
321 &crate::tables::bessel::jy::BESSEL_J0_F32,
322 &crate::tables::bessel::jy::BESSEL_J1_F32,
323 )
324 .neg_c(flip)
325 }
326
327 #[inline(always)]
328 fn bessel_yv<P: Policy>(self, order: crate::BesselOrder<Self, Self::Signed>) -> Self {
329 let order = order.simplify();
331 if let crate::BesselOrder::HalfInteger(k) = order {
332 return generic::bessel::half::bessel_jy_half::<P, f32, _>(Self::from_signed_integer(k) * Self::HALF, self)
333 .1;
334 }
335 let Some(n) = order.as_integer() else {
336 let nu = order.to_real();
337 return generic::bessel::jy_real::bessel_jy_real::<P, f32, _, 5, 11, 11, 13, 2>(
338 nu,
339 self,
340 Self::ZERO,
341 &crate::tables::lgamma1p::LGAMMA1P_F32,
342 )
343 .1;
344 };
345 if const { is_reference::<P>() } {
346 let mut out = self;
347 let mut i = 0;
348 while i < Self::LANES {
349 let k = n.extractv(i);
350 let r = libm::ynf(k.unsigned_abs() as i32, self.extractv(i));
351 out = out.insertv(i, if k < 0 && k % 2 != 0 { -r } else { r });
352 i += 1;
353 }
354 return out;
355 }
356 let (nf, flip) = bessel_reflect_v(Self::from_signed_integer(n));
357 generic::bessel::jy::bessel_yv_impl::<P, f32, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _>(
358 self,
359 nf,
360 &crate::tables::bessel::jy::BESSEL_Y0_F32,
361 &crate::tables::bessel::jy::BESSEL_Y1_F32,
362 &crate::tables::bessel::jy::BESSEL_J0_F32,
363 &crate::tables::bessel::jy::BESSEL_J1_F32,
364 )
365 .neg_c(flip)
366 }
367
368 impl_sph_bessel_entries!(f32, crate::tables::bessel::BESSEL_I0_F32);
369
370 impl_airy_entries!(
371 f32,
372 5,
373 11,
374 11,
375 13,
376 2,
377 &crate::tables::lgamma1p::LGAMMA1P_F32,
378 &crate::tables::bessel::airy::AIRY_ZERO_F32,
379 crate::tables::bessel::BESSEL_I0_F32
380 );
381
382 #[inline(always)]
383 fn bessel_j_with_deriv<P: Policy, const N: i32>(self) -> (Self, Self) {
384 let (prev, v) = if const { N == 0 } {
387 (-Self::bessel_j::<P, 1>(self), Self::bessel_j::<P, 0>(self))
389 } else if const { N.unsigned_abs() == 1 } {
390 (Self::bessel_j::<P, 0>(self), Self::bessel_j::<P, 1>(self))
391 } else {
392 generic::bessel::jy::bessel_jn_pair_impl::<P, f32, _, _, _, _, _, _, _, N>(
393 self,
394 &crate::tables::bessel::jy::BESSEL_J0_F32,
395 &crate::tables::bessel::jy::BESSEL_J1_F32,
396 )
397 };
398 let d = if const { N == 0 } {
399 prev
400 } else {
401 prev - v * (Self::splat(N.unsigned_abs() as f32) / self)
402 };
403 if const { bessel_reflect_negates(N) } {
406 (-v, -d)
407 } else {
408 (v, d)
409 }
410 }
411
412 #[inline(always)]
413 fn bessel_y_with_deriv<P: Policy, const N: i32>(self) -> (Self, Self) {
414 let (prev, v) = if const { N == 0 } {
415 (-Self::bessel_y::<P, 1>(self), Self::bessel_y::<P, 0>(self))
416 } else if const { N.unsigned_abs() == 1 } {
417 (Self::bessel_y::<P, 0>(self), Self::bessel_y::<P, 1>(self))
418 } else {
419 let y0 = generic::bessel::jy::bessel_y_impl::<P, f32, _, _, _, _, _, _, _, _, false>(
420 self,
421 &crate::tables::bessel::jy::BESSEL_Y0_F32,
422 &crate::tables::bessel::jy::BESSEL_J0_F32,
423 );
424 let y1 = generic::bessel::jy::bessel_y_impl::<P, f32, _, _, _, _, _, _, _, _, true>(
425 self,
426 &crate::tables::bessel::jy::BESSEL_Y1_F32,
427 &crate::tables::bessel::jy::BESSEL_J1_F32,
428 );
429 generic::bessel::jy::bessel_yn_recur::<f32, _, N>(self, y0, y1)
430 };
431 let d = if const { N == 0 } {
432 prev
433 } else {
434 prev - v * (Self::splat(N.unsigned_abs() as f32) / self)
435 };
436 if const { bessel_reflect_negates(N) } {
439 (-v, -d)
440 } else {
441 (v, d)
442 }
443 }
444
445 type ExpIntDetails = Self;
446 const LAGUERRE_PRODUCT_SEED_CAP: i32 = 29;
447
448 #[inline(always)]
449 fn chebyshev_n<P: Policy, const K: usize, const N: usize>(self, coeffs: &[f32; N]) -> Self {
450 const {
453 assert!(N >= 1, "chebyshev_n: N must be at least 1");
454 }
455
456 generic::chebyshev::chebyshev_series::<P, _, _, K, N, true>(self, coeffs)
459 }
460
461 #[inline(always)]
462 fn chebyshev<P: Policy, const K: usize>(self, coeffs: &[f32]) -> Self {
463 generic::chebyshev::chebyshev_series::<P, _, _, K, 0, true>(self, coeffs)
465 }
466
467 #[inline(always)]
479 fn lambert_w<P: Policy>(self) -> (Self, Self) {
480 type Approx<P> = WorstPrecision<CheckOverflow<P, false>>;
497
498 let x = self;
499
500 let p0 = x.mul_adde(Self::E, Self::ONE); let p = (p0 + p0).sqrt(); let puiseux_numer = p * p.mul_adde(
522 p.mul_adde(
523 thermite::const_splat!(f32: 11.0 / 72.0),
524 thermite::const_splat!(f32: -1.0 / 3.0),
525 ),
526 Self::ONE,
527 );
528
529 let puiseux_denom = p0.mul_adde(p * thermite::const_splat!(f32: 0.12991546098765432), Self::ONE);
531
532 let puiseux = puiseux_numer / puiseux_denom;
533
534 let w0_branch = puiseux + Self::NEG_ONE;
536 let wm1_branch = Self::NEG_ONE - puiseux;
537
538 let ex = x * Self::E;
540 let w0_mid = ex / (Self::TWO + ex);
541
542 let lnx = x.abs().ln_p::<Approx<P>>();
544
545 let l2 = lnx.ln_p::<Approx<P>>();
549
550 let w0_asymptotic = if const { P::POLICY.precision.le(PrecisionPolicy::Average) && V::HAS_APPROX_RCP } {
551 l2.mul_adde(lnx.approx_reciprocal_p::<Approx<P>>(), lnx - l2)
552 } else {
553 (lnx - l2) + (l2 / lnx)
554 };
555
556 let wm1_asymptotic = lnx - (-lnx).ln_p::<Approx<P>>();
560
561 let near_branch = x.cmp_lt(thermite::const_splat!(f32: -0.1));
563 let large = x.cmp_gt(Self::E);
564
565 let mut w0 = near_branch.select(w0_branch, large.select(w0_asymptotic, w0_mid));
566
567 let near_branch_m1 = x.cmp_lt(thermite::const_splat!(f32: -0.25));
568 let mut wm1 = near_branch_m1.select(wm1_branch, wm1_asymptotic);
569
570 #[inline(always)]
572 fn halley_step<P: Policy, W>(w: W, x: W) -> W
573 where
574 W: FloatVectorWithBits<Element = f32> + SpecializedTranscendentalMath<f32>,
575 {
576 let enw = (-w).exp_p::<P>();
580
581 let wp1 = w + W::ONE;
582 let q = wp1.mul_adde(wp1, W::ONE); let wp2h_x = wp1.mul_adde(x, x); let g = x.nmul_adde(enw, w); let d = wp2h_x.mul_adde(enw, q); (wp1 + wp1).nmul_adde(g / d, w)
587 }
588
589 #[rustfmt::skip]
590 let num_iters = if const { P::POLICY.precision.ge(PrecisionPolicy::Best) } { 2 } else { 1 };
591
592 w0 = halley_step::<Approx<P>, Self>(w0, x);
595 wm1 = halley_step::<Approx<P>, Self>(wm1, x);
596
597 let mut _iter = 0usize;
598 while _iter < num_iters {
599 _iter += 1;
600 w0 = halley_step::<CheckOverflow<P, false>, Self>(w0, x);
603 wm1 = halley_step::<CheckOverflow<P, false>, Self>(wm1, x);
604 }
605
606 if const { P::POLICY.precision.ge(PrecisionPolicy::Average) } {
608 let x_is_zero = x.is_zero();
609
610 w0 = x.cmp_eq(Self::FRAC_NEG_1_E).select(Self::NEG_ONE, w0);
612 w0 = w0.nz(x_is_zero); wm1 = x.cmp_eq(Self::FRAC_NEG_1_E).select(Self::NEG_ONE, wm1);
617 wm1 = x_is_zero.select(Self::NEG_INFINITY, wm1); }
619
620 if const { matches!(P::POLICY.denormal_behavior, DenormalBehavior::Preserve) } {
621 w0 = x.is_subnormal().select(x, w0);
623
624 }
630
631 if const { P::POLICY.check_overflow } {
632 let in_domain = x.cmp_ge(Self::FRAC_NEG_1_E);
633
634 w0 = in_domain.select(w0, Self::NAN);
636 w0 = x.cmp_eq(Self::INFINITY).select(Self::INFINITY, w0);
637
638 wm1 = in_domain.select(wm1, Self::NAN);
640 wm1 = x.cmp_gt(Self::ZERO).select(Self::NAN, wm1);
641 }
642
643 (w0, wm1)
644 }
645
646 #[inline(always)]
647 #[allow(const_item_mutation)]
648 fn erf<P: Policy>(self) -> Self {
649 if const { is_reference::<P>() } {
650 return map1(self, libm::erff);
651 }
652
653 erf_f_internal::<Self, P, false, false>(self, &mut V::EMPTY)
654 }
655
656 #[inline(always)]
657 #[allow(const_item_mutation)]
658 fn erfc<P: Policy>(self) -> Self {
659 if const { is_reference::<P>() } {
660 return map1(self, libm::erfcf);
661 }
662
663 erf_f_internal::<Self, P, true, false>(self, &mut V::EMPTY)
664 }
665
666 #[inline(always)]
667 fn erfcx<P: Policy>(self) -> Self {
668 super::generic::erfcx::erfcx_internal::<Self, f32, P>(self)
671 }
672
673 #[inline(always)]
674 fn logistic_sigmoid<P: Policy>(self) -> Self {
675 if const { is_reference::<P>() } {
676 return map1(self, |x| (1.0 / (1.0 + libm::exp(-(x as f64)))) as f32);
677 }
678
679 if const { P::POLICY.precision.gt(PrecisionPolicy::Average) } {
680 let is_pos = self.is_positive();
681 let x = self.neg_c(is_pos); let e = x.exp_p::<P>();
683
684 let n = is_pos.select(Self::ONE, e);
685 let d = Self::ONE + e;
686
687 return n / d;
688 }
689
690 (Self::ONE + (-self).exp_p::<P>()).approx_reciprocal_p::<ExtraPrecision<P>>()
691 }
692
693 #[inline(always)]
718 fn lgamma<P: Policy>(self) -> Self {
719 if const { is_reference::<P>() } {
720 return map1(self, libm::lgammaf);
721 }
722
723 Self::lgamma_r::<P>(self).0
724 }
725
726 #[inline(always)]
727 fn tgamma<P: Policy>(self) -> Self {
728 if const { is_reference::<P>() } {
729 return map1(self, libm::tgammaf);
730 }
731
732 let z = self;
733
734 if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
735 let (lgamma, sign) = z.lgamma_r_p::<P>();
737
738 return lgamma.exp_p::<ExtraPrecision<P>>() * sign;
741 }
742
743 generic::gamma::tgamma_impl::<P, _, _, _>(
745 z,
746 &crate::tables::gamma::LANCZOS_F32,
747 36.0,
748 crate::tables::gamma::LN_MAX_F32,
749 )
750 }
751
752 #[inline(always)]
753 fn trigamma<P: Policy>(self) -> Self {
754 generic::trigamma::trigamma_impl::<P, _, _>(self, &crate::tables::gamma::TRIGAMMA_F32)
755 }
756
757 #[inline(always)]
758 fn polygamma<P: Policy>(self, n: u32) -> Self {
759 generic::polygamma::polygamma_impl::<P, _, _>(self, n)
760 }
761
762 #[inline(always)]
763 fn digamma<P: Policy>(self) -> Self {
764 generic::digamma::digamma_impl::<P, _, _, _, _, _, _>(self, &crate::tables::gamma::DIGAMMA_F32)
765 }
766
767 #[inline(always)]
768 fn beta<P: Policy>(a: Self, b: Self) -> Self {
769 if const { is_reference::<P>() } {
770 return map2(a, b, |a, b| {
771 let (a, b) = (a as f64, b as f64);
772 let (la, sa) = libm::lgamma_r(a);
773 let (lb, sb) = libm::lgamma_r(b);
774 let (lab, sab) = libm::lgamma_r(a + b);
775 (libm::exp(la + lb - lab) * ((sa * sb * sab) as f64)) as f32
776 });
777 }
778
779 generic::gamma::beta_impl::<P, _, _, _>(a, b, &crate::tables::gamma::LANCZOS_F32)
780 }
781
782 #[inline(always)]
783 fn expint_n<P: Policy, const N: usize>(self) -> Self {
784 generic::expint::expint_double_n::<P, f32, Self, N>(self)
785 }
786
787 #[inline(always)]
788 fn expint_primal_n<P: Policy, const N: usize>(self) -> (Self, Self) {
789 generic::expint::expint_double_primal_n::<P, f32, Self, N>(self)
790 }
791
792 #[inline(always)]
793 fn phi_n<P: Policy, const N: usize>(self) -> Self {
794 let terms = const {
797 let needed = super::generic::phi::phi_series_terms(
798 N,
799 f32::EPSILON as f64 * P::POLICY.precision.tolerance() as f64 / 32.0,
800 );
801 if needed < P::POLICY.max_iterations {
802 needed
803 } else {
804 P::POLICY.max_iterations
805 }
806 };
807 super::generic::phi::phi_internal_n::<Self, f32, P, N, false>(self, terms)
808 }
809
810 #[inline(always)]
811 fn expint<P: Policy>(self, n: u32) -> Self {
812 generic::expint::expint_double::<P, f32, Self>(self, n)
813 }
814
815 #[inline(always)]
816 fn expint_primal<P: Policy>(self, n: u32) -> (Self, Self) {
817 generic::expint::expint_double_primal::<P, f32, Self>(self, n)
818 }
819
820 #[inline(always)]
821 fn phi<P: Policy>(self, n: u32) -> Self {
822 const EPS_SCALE: f64 = 1.0 / 32.0;
824 let table = const {
825 super::generic::phi::phi_terms_table(
826 f32::EPSILON as f64 * P::POLICY.precision.tolerance() as f64 * EPS_SCALE,
827 P::POLICY.max_iterations,
828 )
829 };
830 let terms = match table.get(n as usize) {
831 Some(&t) => t,
832 None => {
833 let needed = super::generic::phi::phi_series_terms(
834 n as usize,
835 f32::EPSILON as f64 * P::POLICY.precision.tolerance() as f64 * EPS_SCALE,
836 );
837 if needed < P::POLICY.max_iterations {
838 needed
839 } else {
840 P::POLICY.max_iterations
841 }
842 }
843 };
844 super::generic::phi::phi_internal::<Self, f32, P, false>(self, n, terms)
845 }
846}
847
848#[allow(dead_code)]
852#[inline(always)]
853fn bessel_j0_pqzero<V, P: Policy>(x: V, ix: V::Bits) -> (V, V)
854where
855 V: FloatVectorWithBits<Element = f32> + SpecializedSpecialMath<f32>,
856{
857 const PR8: [f32; 6] = [
867 -5.2530439453e+03, -2.4852163086e+03, -2.5706311035e+02, -8.0816707611e+00, -7.0312500000e-02, 0.0000000000e+00, ];
875 const PS8: [f32; 5] = [
876 4.7627726562e+04, 1.1675296875e+05, 4.0597855469e+04, 3.8337448730e+03, 1.1653436279e+02, ];
882 const PR5: [f32; 6] = [
883 -3.4643338013e+02, -3.3123129272e+02, -6.7674766541e+01, -4.1596107483e+00, -7.0312492549e-02, -1.1412546255e-11, ];
891 const PS5: [f32; 5] = [
892 2.4060581055e+03, 9.6254453125e+03, 5.9789707031e+03, 1.0512523193e+03, 6.0753936768e+01, ];
898
899 const PR3: [f32; 6] = [
900 -3.1447946548e+01, -5.8079170227e+01, -2.1965976715e+01, -2.4090321064e+00, -7.0311963558e-02, -2.5470459075e-09, ];
908 const PS3: [f32; 5] = [
909 1.7358093262e+02, 1.1279968262e+03, 1.1936077881e+03, 3.6151397705e+02, 3.5856033325e+01, ];
915
916 const PR2: [f32; 6] = [
917 -3.2336456776e+00, -1.1193166733e+01, -7.6356959343e+00, -1.4507384300e+00, -7.0303097367e-02, -8.8753431271e-08, ];
925 const PS2: [f32; 5] = [
926 1.4657617569e+01, 1.5387539673e+02, 2.7047027588e+02, 1.3620678711e+02, 2.2220300674e+01, ];
932
933 const QR8: [f32; 6] = [
943 3.7014625000e+04, 8.8591972656e+03, 5.5767340088e+02, 1.1768206596e+01, 7.3242187500e-02, 0.0000000000e+00, ];
951 const QS8: [f32; 6] = [
952 -3.4389928125e+05, 8.4050156250e+05, 8.0330925000e+05, 1.4253829688e+05, 8.0983447266e+03, 1.6377603149e+02, ];
959
960 const QR5: [f32; 6] = [
961 1.9899779053e+03, 1.0272437744e+03, 1.3511157227e+02, 5.8356351852e+00, 7.3242180049e-02, 1.8408595828e-11, ];
969 const QS5: [f32; 6] = [
970 -5.3543427734e+03, 3.5976753906e+04, 5.6751113281e+04, 1.8847289062e+04, 2.0778142090e+03, 8.2776611328e+01, ];
977
978 const QR3: [f32; 6] = [
979 1.6673394775e+02, 1.7080809021e+02, 4.2621845245e+01, 3.3442313671e+00, 7.3241114616e-02, 4.3774099900e-09, ];
987 const QS3: [f32; 6] = [
988 -1.4924745178e+02, 2.5163337402e+03, 6.4604252930e+03, 3.7041481934e+03, 7.0968920898e+02, 4.8758872986e+01, ];
995
996 const QR2: [f32; 6] = [
997 1.6252708435e+01, 3.1666231155e+01, 1.4495602608e+01, 1.9981917143e+00, 7.3223426938e-02, 1.5044444979e-07, ];
1005 const QS2: [f32; 6] = [
1006 -5.3109550476e+00, 2.1266638184e+02, 8.8293585205e+02, 8.4478375244e+02, 2.6934811401e+02, 3.0365585327e+01, ];
1013
1014 let z = x.approx_reciprocal_p::<P>();
1015 let z2 = z * z;
1016
1017 let m8 = ix.cmp_ge(thermite::const_splat!(u32: 0x41000000)); let m5 = ix.cmp_ge(thermite::const_splat!(u32: 0x409173eb)); let m3 = ix.cmp_ge(thermite::const_splat!(u32: 0x4036d917)); let pn8 = z2.poly_rev_n_p::<P, _>(&PR8);
1024 let pn5 = z2.poly_rev_n_p::<P, _>(&PR5);
1025 let pn3 = z2.poly_rev_n_p::<P, _>(&PR3);
1026 let pn2 = z2.poly_rev_n_p::<P, _>(&PR2);
1027
1028 let pd8 = z2.poly_rev_n_p::<P, _>(&PS8);
1029 let pd5 = z2.poly_rev_n_p::<P, _>(&PS5);
1030 let pd3 = z2.poly_rev_n_p::<P, _>(&PS3);
1031 let pd2 = z2.poly_rev_n_p::<P, _>(&PS2);
1032
1033 let pn = m3.select(m5.select(m8.select(pn8, pn5), pn3), pn2);
1034 let pd = m3.select(m5.select(m8.select(pd8, pd5), pd3), pd2);
1035
1036 let qn8 = z2.poly_rev_n_p::<P, _>(&QR8);
1037 let qn5 = z2.poly_rev_n_p::<P, _>(&QR5);
1038 let qn3 = z2.poly_rev_n_p::<P, _>(&QR3);
1039 let qn2 = z2.poly_rev_n_p::<P, _>(&QR2);
1040
1041 let qd8 = z2.poly_rev_n_p::<P, _>(&QS8);
1042 let qd5 = z2.poly_rev_n_p::<P, _>(&QS5);
1043 let qd3 = z2.poly_rev_n_p::<P, _>(&QS3);
1044 let qd2 = z2.poly_rev_n_p::<P, _>(&QS2);
1045
1046 let qn = m3.select(m5.select(m8.select(qn8, qn5), qn3), qn2);
1047 let qd = m3.select(m5.select(m8.select(qd8, qd5), qd3), qd2);
1048
1049 let pzero = V::ONE + pn / pd.mul_adde(z2, V::ONE);
1050 let mut qzero = qn / qd.mul_adde(z2, V::ONE);
1051
1052 let neg_eighth: V = thermite::const_splat!(f32: -0.125);
1053
1054 if const { matches!(V::HAS_NATIVE_FMA, thermite::tribool::True) } {
1055 qzero = qzero.mul_add(z, z * neg_eighth);
1060 } else {
1061 qzero = (qzero + neg_eighth) * z;
1062 }
1063
1064 (pzero, qzero)
1065}
1066
1067#[cfg(any())]
1069#[inline(always)]
1070fn bessel_j0<V, P: Policy>(x: V) -> V
1071where
1072 V: FloatVectorWithBits<Element = f32> + SpecializedSpecialMath<f32>,
1073{
1074 let ax = x.abs().flush_denormals_p::<P>();
1075 let ix: V::Bits = ax.into_bits();
1076 let large = ix.cmp_ge(thermite::const_splat!(u32: 0x40000000)); let z = x * x;
1084
1085 let r = z * z.poly_rev_n_p::<P, _>(&[
1087 -4.6183270541e-09, 1.8295404516e-06, -1.8997929874e-04, 1.5625000000e-02, ]);
1092
1093 let s = z.poly_rev_n_p::<P, _>(&[
1094 1.1661400734e-09, 5.1354652442e-07, 1.1692678527e-04, 1.5619102865e-02, ]);
1099
1100 let s = s.mul_adde(z, V::ONE);
1101
1102 let mut y = ax
1103 .mul_adde(V::HALF, V::ONE)
1104 .mul_adde(ax.nmul_adde(V::HALF, V::ONE), z * (r / s));
1105
1106 if large.any() {
1115 let (sinx, cosx) = ax.sin_cos_p::<P>();
1116
1117 let neg_cos2x = if const { P::POLICY.precision.ge(PrecisionPolicy::Best) } {
1122 -(ax + ax).cos_p::<P>()
1123 } else {
1124 (cosx * cosx).nmul_adde(V::TWO, V::ONE)
1125 };
1126
1127 let cc_raw = sinx + cosx;
1132 let ss_raw = sinx - cosx;
1133
1134 let fix_cc = (sinx * cosx).is_negative();
1135 let ratio = neg_cos2x / fix_cc.select(ss_raw, cc_raw);
1136 let cc = fix_cc.select(ratio, cc_raw);
1137 let ss = fix_cc.select(ss_raw, ratio);
1138
1139 let (pz, qz) = bessel_j0_pqzero::<V, P>(ax, ix);
1141
1142 let yl = V::FRAC_1_SQRT_PI * (pz * cc - qz * ss) / ax.sqrt();
1143
1144 y = large.select(yl, y);
1145
1146 if const { P::POLICY.precision.ge(PrecisionPolicy::Best) } {
1147 let very_large = ix.cmp_ge(thermite::const_splat!(u32: 0x7f800000));
1148
1149 y = very_large.select(ax.square().approx_reciprocal_p::<P>(), y);
1150 }
1151 }
1152
1153 y
1154}
1155
1156impl<V: FloatVectorWithBits<Element = f32>> SpecializedRealSpecialMath<f32> for V
1157where
1158 V: TranscendentalMathWithPolicy<Element = f32>,
1159 V: SpecializedTranscendentalMath<f32>,
1160 V: thermite::math::PrimalProjection<Primal = V>,
1163{
1164 #[inline(always)]
1165 fn fresnel<P: Policy>(self) -> (Self, Self) {
1166 use crate::tables::fresnel as t;
1167 generic::fresnel::fresnel_with::<P, _, _, _, _, _, _>(
1168 self,
1169 t::X0_F32,
1170 t::MAP_F32,
1171 t::CUTOFF_F32,
1172 &t::CHEB_C_F32,
1173 &t::CHEB_S_F32,
1174 &t::AUX_P_F32,
1175 &t::AUX_Q_F32,
1176 )
1177 }
1178
1179 #[inline(always)]
1180 fn sici<P: Policy>(self) -> (Self, Self) {
1181 use crate::tables::sici as t;
1182 generic::sici::sici_with::<P, _, _, _, _, _, _>(
1183 self,
1184 t::X0_F32,
1185 t::MAP_F32,
1186 t::CUTOFF_F32,
1187 &t::CHEB_SI_F32,
1188 &t::CHEB_CIN_F32,
1189 &t::AUX_P_F32,
1190 &t::AUX_Q_F32,
1191 )
1192 }
1193
1194 #[inline(always)]
1203 fn spherical_harmonics<P: Policy, const L: usize, const N: usize, const CS: bool>(
1204 x: Self,
1205 y: Self,
1206 z: Self,
1207 out: &mut [Self; N],
1208 ) {
1209 sh_impl::<P, f32, Self, L, N, CS>(x, y, z, out);
1213 }
1214
1215 #[inline(always)]
1216 fn spherical_harmonics_table<P: Policy, const L: usize, const N: usize, const CS: bool>(
1217 table: &mut ShTable<Self, N>,
1218 ) {
1219 if const { L <= MAX_SH_DEGREE } {
1220 let src = &<f32 as ShConsts<L, N, CS>>::TABLE;
1224
1225 let mut i = 0;
1226 while i < N {
1227 table.qmm[i] = Self::splat(src.qmm[i]);
1228 table.em[i] = Self::splat(src.em[i]);
1229 table.a[i] = Self::splat(src.a[i]);
1230 table.nb[i] = Self::splat(src.nb[i]);
1231 table.f[i] = Self::splat(src.f[i]);
1232 table.mf[i] = Self::splat(src.mf[i]);
1233 i += 1;
1234 }
1235 } else {
1236 sh_table_impl::<Self, L, N, CS>(table);
1237 }
1238 }
1239
1240 #[inline(always)]
1241 fn bessel_i_ratio<P: Policy>(self, nu: Self) -> Self {
1242 generic::bessel::ratio::bessel_i_ratio_impl::<P, f32, Self>(self, nu)
1243 }
1244
1245 #[inline(always)]
1246 fn inv_bessel_i_ratio<P: Policy>(self, nu: Self) -> Self {
1247 generic::bessel::ratio::inv_bessel_i_ratio_impl::<P, f32, Self>(self, nu)
1248 }
1249
1250 #[inline(always)]
1251 fn bessel_i_ratio_1m<P: Policy>(self, nu: Self) -> Self {
1252 generic::bessel::ratio::bessel_i_ratio_1m_impl::<P, f32, Self>(self, nu)
1253 }
1254
1255 #[inline(always)]
1256 fn inv_bessel_i_ratio_1m<P: Policy>(self, nu: Self) -> Self {
1257 generic::bessel::ratio::inv_bessel_i_ratio_1m_impl::<P, f32, Self>(self, nu)
1258 }
1259
1260 #[inline(always)]
1261 fn erfinv<P: Policy>(self) -> Self {
1262 let x = self.flush_denormals_p::<P>().clamp(
1264 thermite::const_splat!(f32: -0.99999),
1265 thermite::const_splat!(f32: 0.99999),
1266 );
1267
1268 let w = -x.nmul_adde(x, V::ONE).ln_p::<P>();
1269
1270 let ge5 = w.cmp_ge(thermite::const_splat!(f32: 5.0));
1271
1272 let w0 = w - thermite::const_splat!(f32: 2.5);
1273 let mut p0 = w0.poly_rev_n_p::<P, _>(&[
1274 2.81022636e-08,
1275 3.43273939e-07,
1276 -3.5233877e-06,
1277 -4.39150654e-06,
1278 0.00021858087,
1279 -0.00125372503,
1280 -0.00417768164,
1281 0.246640727,
1282 1.50140941,
1283 ]);
1284
1285 if const { P::POLICY.avoid_branching } || thermite::unlikely(ge5.any()) {
1286 let w1 = w.sqrt() - thermite::const_splat!(f32: 3.0);
1287 let p1 = w1.poly_rev_n_p::<P, _>(&[
1288 -0.000200214257,
1289 0.000100950558,
1290 0.00134934322,
1291 -0.00367342844,
1292 0.00573950773,
1293 -0.0076224613,
1294 0.00943887047,
1295 1.00167406,
1296 2.83297682,
1297 ]);
1298
1299 p0 = ge5.select(p1, p0);
1300 }
1301
1302 p0 * x
1303 }
1304
1305 fn probit<P: Policy>(self) -> Self {
1308 const A: [f32; 6] = [
1309 2.506628277459239e+00,
1310 -3.066479806614716e+01,
1311 1.383577518672690e+02,
1312 -2.759285104469687e+02,
1313 2.209460984245205e+02,
1314 -3.969683028665376e+01,
1315 ];
1316
1317 const B: [f32; 6] = [
1318 1.0,
1319 -1.328068155288572e+01,
1320 6.680131188771972e+01,
1321 -1.556989798598866e+02,
1322 1.615858368580409e+02,
1323 -5.447609879822406e+01,
1324 ];
1325
1326 const C: [f32; 6] = [
1327 2.938163982698783e+00,
1328 4.374664141464968e+00,
1329 -2.549732539343734e+00,
1330 -2.400758277161838e+00,
1331 -3.223964580411365e-01,
1332 -7.784894002430293e-03,
1333 ];
1334
1335 const D: [f32; 5] = [
1336 1.0,
1337 3.754408661907416e+00,
1338 2.445134137142996e+00,
1339 3.224671290700398e-01,
1340 7.784695709041462e-03,
1341 ];
1342
1343 generic::probit::probit_acklam::<P, _, _, false>(self, &A, &B, &C, &D)
1345 }
1346
1347 #[inline(always)]
1348 fn langevin<P: Policy>(self) -> Self {
1349 if const { P::POLICY.precision.le(PrecisionPolicy::Medium) } {
1351 generic::langevin::langevin_primal::<P, _, _, 5, false>(self, &LANGEVIN_SMALL_F32_LO).0
1352 } else {
1353 generic::langevin::langevin_primal::<P, _, _, 8, false>(self, &LANGEVIN_SMALL_F32).0
1354 }
1355 }
1356
1357 #[inline(always)]
1358 fn langevin_1m<P: Policy>(self) -> Self {
1359 if const { P::POLICY.precision.le(PrecisionPolicy::Medium) } {
1361 generic::langevin::langevin_primal::<P, _, _, 5, true>(self, &LANGEVIN_SMALL_F32_LO).0
1362 } else {
1363 generic::langevin::langevin_primal::<P, _, _, 8, true>(self, &LANGEVIN_SMALL_F32).0
1364 }
1365 }
1366
1367 #[inline(always)]
1369 fn inv_langevin<P: Policy>(self) -> Self {
1370 generic::langevin::inv_langevin::<P, _, _, 8, 5, false, false>(self, &LANGEVIN_SMALL_F32, &LANGEVIN_SEED_F32)
1371 }
1372
1373 #[inline(always)]
1374 fn inv_langevin_1m<P: Policy>(self) -> Self {
1375 generic::langevin::inv_langevin::<P, _, _, 8, 5, false, true>(self, &LANGEVIN_SMALL_F32, &LANGEVIN_SEED_F32)
1376 }
1377
1378 #[inline(always)]
1379 fn lgamma_r<P: Policy>(self) -> (Self, Self) {
1380 if const { is_reference::<P>() } {
1381 return map1x2(self, |x| {
1382 let (v, s) = libm::lgammaf_r(x);
1383 (v, s as f32)
1384 });
1385 }
1386
1387 let z = self.flush_denormals_p::<P>();
1388 let mut signum = Self::ONE;
1389
1390 let reflect = z.is_negative();
1391
1392 if const { P::POLICY.precision.le(PrecisionPolicy::Medium) } {
1395 let x = reflect.select(Self::ONE - z, z);
1396 let w = Self::ONE / x; let t = x.mul_adde(Self::HALF, Self::NEG_ONE);
1417
1418 let mut y = t.poly_n_p::<P, _>(&[
1419 6.931471825e-01,
1420 1.845574498e+00,
1421 7.898645401e-01,
1422 -2.056447715e-01,
1423 7.939288765e-02,
1424 -3.510471061e-02,
1425 1.755452715e-02,
1426 -1.377308462e-02,
1427 7.950476371e-03,
1428 2.705342602e-03,
1429 -1.809931011e-03,
1430 -4.729579668e-03,
1431 2.926796675e-03,
1432 ]);
1433
1434 let big = x.cmp_ge(thermite::const_splat!(f32: 4.0));
1457
1458 let mut e = x;
1463
1464 if const { P::POLICY.avoid_branching } || thermite::unlikely(reflect.any()) {
1466 let pix = (z * Self::PI).sin_p::<P>();
1467
1468 signum |= reflect.select(pix.signed_zero(), signum);
1469
1470 e = reflect.select(pix.abs() / x, x);
1471 }
1472
1473 let ln_e = e.ln_p::<P>();
1474
1475 let mut lnx = ln_e;
1482
1483 if const { P::POLICY.avoid_branching } || thermite::unlikely((reflect & big).any()) {
1484 lnx = reflect.select(x.ln_p::<P>(), ln_e);
1485 }
1486
1487 let c = (w * w).mul_adde(
1488 thermite::const_splat!(f32: -1.0 / 360.0),
1489 thermite::const_splat!(f32: 1.0 / 12.0),
1490 );
1491 let stirling = (x + Self::HALF).mul_adde(lnx, w.mul_adde(c, Self::FRAC_LN_TAU_2 - x));
1492
1493 y = big.select(stirling, y);
1494 y = reflect.select(Self::LN_PI - y, y);
1495 y -= ln_e;
1496
1497 if const { P::POLICY.check_overflow } {
1498 y = z.is_infinite().select(Self::INFINITY, y);
1503 }
1504
1505 return (y, signum);
1506 }
1507
1508 generic::gamma::lgamma_r_impl::<P, _, _, _>(z, &crate::tables::gamma::LANCZOS_F32)
1509 }
1510
1511 #[inline(always)]
1512 fn gelu<P: Policy>(self, alpha: Self) -> Self {
1513 if const { is_reference::<P>() } {
1514 return map2(self, alpha, |x, a| {
1515 let (x, a) = (x as f64, a as f64);
1516 (0.5 * x * libm::erfc(-a * x * core::f64::consts::FRAC_1_SQRT_2)) as f32
1517 });
1518 }
1519
1520 let x = self;
1521
1522 let alpha_x = alpha * x;
1523
1524 let mut unused = Self::EMPTY;
1527 let c = erf_f_internal::<V, P, true, false>(alpha_x * -Self::FRAC_1_SQRT_2, &mut unused);
1528
1529 (x * Self::HALF) * c
1530 }
1531}
1532
1533impl<V: FloatVectorWithBits<Element = f32>> SpecializedRealPrimalMath<f32> for V
1534where
1535 V: thermite::math::PrimalProjection<Primal = V>,
1536{
1537 #[inline(always)]
1538 fn langevin_d<P: Policy>(self) -> (Self, Self) {
1539 generic::langevin::langevin_primal::<P, _, _, 8, false>(self, &LANGEVIN_SMALL_F32)
1540 }
1541
1542 #[inline(always)]
1543 #[allow(clippy::too_many_arguments)]
1544 fn spherical_harmonics_d<P: Policy, const L: usize, const N: usize, const CS: bool>(
1545 x: Self,
1546 y: Self,
1547 z: Self,
1548 out: &mut [Self; N],
1549 ddx: &mut [Self; N],
1550 ddy: &mut [Self; N],
1551 ddz: &mut [Self; N],
1552 ) {
1553 sh_d_impl::<P, f32, Self, L, N, CS>(x, y, z, out, ddx, ddy, ddz);
1554 }
1555
1556 #[inline(always)]
1557 fn gelu_d<P: Policy>(self, alpha: Self) -> (Self, Self) {
1558 let x = self;
1559
1560 let alpha_x = alpha * x;
1561
1562 let mut exp_neg_ax2 = Self::EMPTY;
1564 let c = erf_f_internal::<V, P, true, true>(alpha_x * -Self::FRAC_1_SQRT_2, &mut exp_neg_ax2);
1565
1566 let half_c = c * Self::HALF; let alpha_x_scaled = x.scale(FloatConsts::FRAC_1_SQRT_TAU);
1568
1569 let y = x * half_c;
1570 let dy = if matches!(V::HAS_NATIVE_FMA, thermite::tribool::True) {
1571 alpha_x_scaled.mul_add(exp_neg_ax2, half_c)
1572 } else {
1573 half_c + alpha_x_scaled * exp_neg_ax2
1574 };
1575
1576 (y, dy)
1577 }
1578}
1579
1580#[allow(clippy::approx_constant)]
1581#[inline(always)]
1582fn erf_f_internal<V: FloatVectorWithBits<Element = f32>, P: Policy, const C: bool, const O: bool>(
1583 x0: V,
1584 out_exp_neg_x2: &mut V,
1585) -> V {
1586 let sign = x0.signed_zero();
1589 let x = (x0 ^ sign).flush_denormals_p::<P>();
1590
1591 if const {
1594 matches!(P::POLICY.precision, PrecisionPolicy::Worst | PrecisionPolicy::Medium if !V::NATIVE_CAP.has(NativeCapability::EXP))
1595 } {
1596 let x = x.min(thermite::const_splat!(f32: 4.5));
1600
1601 let tn = if const { matches!(P::POLICY.precision, PrecisionPolicy::Worst) } {
1604 let t = x.poly_rev_n_p::<P, _>(&[0.078108, 0.000972, 0.230389, 0.278393, 1.0]);
1605
1606 t.powi_p::<P>(4)
1607 } else {
1608 let t = x.poly_rev_n_p::<P, _>(&[
1609 0.0000430638,
1610 0.0002765672,
1611 0.0001520143,
1612 0.0092705272,
1613 0.0422820123,
1614 0.0705230784,
1615 1.0,
1616 ]);
1617
1618 t.powi_p::<P>(16)
1619 };
1620
1621 if const { O } {
1622 *out_exp_neg_x2 = (-x * x).exp_p::<MediumPrecision<CheckOverflow<P, false>>>();
1625 }
1626
1627 match const {
1632 (
1633 C,
1634 V::HAS_APPROX_RCP && !matches!(P::POLICY.denormal_behavior, DenormalBehavior::Preserve),
1635 )
1636 } {
1637 (false, true) => {
1638 let y = tn.rcp();
1639 y.nmul_adde(tn.nmul_adde(y, V::TWO), V::ONE) ^ sign
1640 }
1641 (false, false) => (V::ONE - tn.approx_reciprocal_p::<ExtraPrecision<P>>()) ^ sign,
1642 (true, true) => {
1643 let y = tn.rcp();
1644 let k = tn.nmul_adde(y, V::TWO);
1645
1646 if matches!(V::HAS_NATIVE_FMA, thermite::tribool::True) {
1647 sign.select_negative(y.nmul_add(k, V::TWO), y * k)
1648 } else {
1649 let erfc_pos = y * k;
1650 sign.select_negative(V::TWO - erfc_pos, erfc_pos)
1651 }
1652 }
1653 (true, false) => {
1654 let y = tn.approx_reciprocal_p::<ExtraPrecision<P>>();
1655 sign.select_negative(V::TWO - y, y)
1656 }
1657 }
1658 }
1659 else {
1661 let x = if const { P::POLICY.check_overflow } {
1666 let cap: V = thermite::const_splat!(f32: 16.0);
1667 x.cmp_gt(cap).select(cap, x)
1668 } else {
1669 x
1670 };
1671
1672 let x2 = if const { matches!(P::POLICY.denormal_behavior, DenormalBehavior::Ignore) && !P::POLICY.check_overflow }
1675 {
1676 x0 * x0
1677 } else {
1678 x * x
1679 };
1680
1681 let exp_neg_x2 = (-x2).exp_p::<P>();
1682
1683 let p1: V = thermite::const_splat!(f32: 0.406742016006509);
1685 let p2: V = thermite::const_splat!(f32: 0.0072279182302319);
1686
1687 let t = x.mul_adde(x.mul_adde(p2, p1), V::ONE).approx_reciprocal_p::<P>();
1688
1689 let m = t.poly_rev_n_p::<P, _>(&[
1690 0.0382613542530727,
1691 -0.393127715207728,
1692 1.20644903073232,
1693 -1.11694155120396,
1694 1.08680830347054,
1695 -0.138329314150635,
1696 0.316879890481381, ]);
1698
1699 if const { O } {
1700 *out_exp_neg_x2 = exp_neg_x2;
1701 }
1702
1703 let e = exp_neg_x2 * t;
1707
1708 if const { C } {
1709 if const {
1710 matches!(V::HAS_NATIVE_FMA, thermite::tribool::True) && P::POLICY.precision.lt(PrecisionPolicy::Average)
1711 } {
1712 return sign.select_negative(e.nmul_add(m, V::TWO), e * m);
1713 }
1714
1715 let mut y = e * m;
1716
1717 let is_big = x.cmp_gt(V::ONE);
1718
1719 if const { P::POLICY.precision.ge(PrecisionPolicy::Average) }
1720 && (const { P::POLICY.avoid_branching } || is_big.any())
1721 {
1722 let s = x.approx_reciprocal_p::<P>();
1723
1724 let big_y = if const { P::POLICY.precision.ge(PrecisionPolicy::Reference) } {
1725 let r = s.poly_rev_n_p::<P, _>(&[
1727 -4.8351919556e+02,
1728 -1.0250950928e+03,
1729 -6.3756646729e+02,
1730 -1.6063638306e+02,
1731 -1.7757955551e+01,
1732 -7.9928326607e-01,
1733 -9.8649431020e-03,
1734 ]);
1735
1736 let b = s.poly_rev_n_p::<P, _>(&[
1737 -2.2440952301e+01,
1738 4.7452853394e+02,
1739 2.5530502930e+03,
1740 3.1998581543e+03,
1741 1.5367296143e+03,
1742 3.2579251099e+02,
1743 3.0338060379e+01,
1744 1.0,
1745 ]);
1746
1747 let z: V = {
1748 let mut ix: V::Bits = x.into_bits();
1750 ix &= thermite::const_splat!(u32: 0xffffe000);
1751 ix.into_bits()
1752 };
1753
1754 let a = (-z * z - thermite::const_splat!(f32: 0.5625)).exp_p::<CheckOverflow<P, false>>();
1755 let b = ((z - x) * (z + x) + r / b).exp_p::<CheckOverflow<P, false>>() / x;
1756
1757 a * b
1758 } else {
1759 exp_neg_x2
1761 * s.mul_adde(
1762 thermite::const_splat!(f32: 9.0 / 4.0),
1763 thermite::const_splat!(f32: -5.0 / 4.0),
1764 )
1765 .poly_rev_n_p::<P, _>(&[
1766 -1.5849000192247331142425537109375e-5,
1767 4.057946716784499585628509521484375e-5,
1768 -2.17467240872792899608612060546875e-5,
1769 -9.03195686987601220607757568359375e-5,
1770 4.285395261831581592559814453125e-4,
1771 -1.16943917237222194671630859375e-3,
1772 1.68157299049198627471923828125e-3,
1773 3.04660876281559467315673828125e-3,
1774 -3.5686969757080078125e-2,
1775 0.18081049621105194091796875,
1776 0.278560101985931396484375,
1777 ])
1778 };
1779
1780 y = is_big.select(big_y, y);
1781 }
1782
1783 sign.select_negative(V::TWO - y, y)
1784 } else {
1785 let mut y = e.nmul_adde(m, V::ONE);
1786
1787 if const { P::POLICY.precision.ge(PrecisionPolicy::Average) } {
1788 let small = if const { P::POLICY.precision.le(PrecisionPolicy::Average) } {
1789 x * x2.poly_rev_n_p::<P, _>(&[
1791 0.00012055332981789664251,
1792 -0.00085483270234508528325,
1793 0.0052239776254421878421,
1794 -0.026866170645131251759,
1795 0.11283791670955125739,
1796 -0.37612638903183752463,
1797 1.1283791670955125739,
1798 ])
1799 } else {
1800 let n = x2.poly_rev_n_p::<P, _>(&[
1802 -2.3763017452e-05,
1803 -5.7702702470e-03,
1804 -2.8481749818e-02,
1805 -3.2504209876e-01,
1806 1.2837916613e-01,
1807 ]);
1808
1809 let d = x2.poly_rev_n_p::<P, _>(&[
1810 -3.9602282413e-06,
1811 1.3249473704e-04,
1812 5.0813062117e-03,
1813 6.5022252500e-02,
1814 3.9791721106e-01,
1815 1.0,
1816 ]);
1817
1818 x.mul_adde(n / d, x)
1819 };
1820
1821 y = x.cmp_lt(V::ONE).select(small, y);
1822 }
1823
1824 y | sign
1825 }
1826 }
1827}
1828
1829impl<V: FloatVectorWithBits<Element = f32>> super::ExpIntDetails<f32, V> for V {}
1831
1832const LANGEVIN_SMALL_F32: [f32; 8] = [
1835 0.3333333432674408,
1836 -0.022222189232707024,
1837 0.0021162214688956738,
1838 -0.0002112639049300924,
1839 2.098539516737219e-05,
1840 -1.934508873091545e-06,
1841 1.394919024733099e-07,
1842 -5.404849012791146e-09,
1843];
1844
1845const LANGEVIN_SEED_F32: [f32; 5] = [
1848 2.9997715950012207,
1849 -1.1931958198547363,
1850 -0.12431719899177551,
1851 -0.007050277199596167,
1852 0.39067453145980835,
1853];
1854
1855const LANGEVIN_SMALL_F32_LO: [f32; 5] = [
1859 0.3333316445350647,
1860 -0.02220052480697632,
1861 0.0020704844500869513,
1862 -0.00017605189350433648,
1863 8.862235517881345e-06,
1864];
1865
1866#[inline(always)]
1873fn bessel_i_dispatch<P: Policy, V, const N: i32, const SCALED: bool>(x: V) -> V
1874where
1875 V: thermite::vector::FloatVector<Element = f32> + thermite::math::TranscendentalMathWithPolicy,
1876{
1877 if const { N == 0 } {
1878 generic::bessel::ik::bessel_i0_impl::<P, _, _, _, _, SCALED>(x, &crate::tables::bessel::BESSEL_I0_F32)
1879 } else if const { N.unsigned_abs() == 1 } {
1880 generic::bessel::ik::bessel_i1_impl::<P, _, _, _, _, SCALED>(x, &crate::tables::bessel::BESSEL_I1_F32)
1881 } else {
1882 generic::bessel::ik::bessel_in_impl::<P, f32, _, _, _, _, N, SCALED>(x, &crate::tables::bessel::BESSEL_I0_F32)
1884 }
1885}
1886
1887#[inline(always)]
1894fn bessel_k_dispatch<P: Policy, V, const N: i32, const SCALED: bool>(x: V) -> V
1895where
1896 V: thermite::vector::FloatVector<Element = f32> + thermite::math::TranscendentalMathWithPolicy,
1897{
1898 use crate::tables::bessel::{BESSEL_I0_F32, BESSEL_I1_F32, BESSEL_K0_F32, BESSEL_K1_F32};
1899 if const { N == 0 } {
1900 generic::bessel::ik::bessel_k0_impl::<P, f32, _, _, _, _, _, _, _, _, SCALED>(x, &BESSEL_K0_F32, &BESSEL_I0_F32)
1901 } else if const { N.unsigned_abs() == 1 } {
1902 generic::bessel::ik::bessel_k1_impl::<P, f32, _, _, _, _, _, _, _, _, SCALED>(x, &BESSEL_K1_F32, &BESSEL_I1_F32)
1903 } else {
1904 let k0 = generic::bessel::ik::bessel_k0_impl::<P, f32, _, _, _, _, _, _, _, _, SCALED>(
1907 x,
1908 &BESSEL_K0_F32,
1909 &BESSEL_I0_F32,
1910 );
1911 let k1 = generic::bessel::ik::bessel_k1_impl::<P, f32, _, _, _, _, _, _, _, _, SCALED>(
1912 x,
1913 &BESSEL_K1_F32,
1914 &BESSEL_I1_F32,
1915 );
1916 generic::bessel::ik::bessel_kn_recur::<f32, _, N>(x, k0, k1).1
1917 }
1918}
1919
1920#[inline(always)]
1928fn bessel_i_deriv_dispatch<P: Policy, V, const N: i32, const SCALED: bool>(x: V) -> (V, V)
1929where
1930 V: thermite::vector::FloatVector<Element = f32> + thermite::math::TranscendentalMathWithPolicy,
1931{
1932 let (prev, v) = if const { N == 0 } {
1933 (
1934 bessel_i_dispatch::<P, V, 1, SCALED>(x),
1935 bessel_i_dispatch::<P, V, 0, SCALED>(x),
1936 )
1937 } else if const { N.unsigned_abs() == 1 } {
1938 (
1939 bessel_i_dispatch::<P, V, 0, SCALED>(x),
1940 bessel_i_dispatch::<P, V, 1, SCALED>(x),
1941 )
1942 } else {
1943 generic::bessel::ik::bessel_in_pair_impl::<P, f32, _, _, _, _, N, SCALED>(
1944 x,
1945 &crate::tables::bessel::BESSEL_I0_F32,
1946 )
1947 };
1948
1949 let mut d = if const { N == 0 } {
1950 prev
1951 } else {
1952 prev - v * (V::splat(N.unsigned_abs() as f32) / x)
1953 };
1954 if const { SCALED } {
1955 d -= v.copysign(x);
1956 }
1957 (v, d)
1958}
1959
1960#[inline(always)]
1965fn bessel_k_deriv_dispatch<P: Policy, V, const N: i32, const SCALED: bool>(x: V) -> (V, V)
1966where
1967 V: thermite::vector::FloatVector<Element = f32> + thermite::math::TranscendentalMathWithPolicy,
1968{
1969 let (prev, v) = if const { N == 0 } {
1970 (
1971 bessel_k_dispatch::<P, V, 1, SCALED>(x),
1972 bessel_k_dispatch::<P, V, 0, SCALED>(x),
1973 )
1974 } else if const { N.unsigned_abs() == 1 } {
1975 (
1976 bessel_k_dispatch::<P, V, 0, SCALED>(x),
1977 bessel_k_dispatch::<P, V, 1, SCALED>(x),
1978 )
1979 } else {
1980 let k0 = generic::bessel::ik::bessel_k0_impl::<P, f32, _, _, _, _, _, _, _, _, SCALED>(
1983 x,
1984 &crate::tables::bessel::BESSEL_K0_F32,
1985 &crate::tables::bessel::BESSEL_I0_F32,
1986 );
1987 let k1 = generic::bessel::ik::bessel_k1_impl::<P, f32, _, _, _, _, _, _, _, _, SCALED>(
1988 x,
1989 &crate::tables::bessel::BESSEL_K1_F32,
1990 &crate::tables::bessel::BESSEL_I1_F32,
1991 );
1992 generic::bessel::ik::bessel_kn_recur::<f32, _, N>(x, k0, k1)
1993 };
1994
1995 let mut d = if const { N == 0 } {
1996 -prev
1997 } else {
1998 -prev - v * (V::splat(N.unsigned_abs() as f32) / x)
1999 };
2000 if const { SCALED } {
2001 d += v;
2002 }
2003 (v, d)
2004}