1#![allow(clippy::excessive_precision)]
2
3use thermite::{
4 const_splat,
5 mask::GenericMask,
6 math::{
7 CoreMathWithPolicy as _, FloatConsts, PrimalProjection, TranscendentalMathWithPolicy as _,
8 policy::{
9 Policy, PrecisionPolicy,
10 policies::{CheckOverflow, ExtraPrecision, LessPrecision},
11 },
12 specialized::FlushDenormals,
13 },
14 register::{Element, FloatElement},
15 vector::{NumericVector, PartialOrdVector, SplatConst},
16};
17
18use super::SpecialMathWithPolicy as _;
19
20#[macro_use]
21mod bessel;
22pub use bessel::{BesselDetails, kernels};
23pub(crate) use bessel::{bessel_reflect_negates, bessel_reflect_v};
24
25pub mod generic;
31mod pd;
32mod ps;
33
34pub use generic::bessel::ratio::{bessel_i_ratio_deriv, bessel_i_ratio_deriv_1m};
35pub use generic::ndtr::LogTailPolicy;
36
37pub trait ExpIntDetails<E, V: thermite::vector::FloatVector<Element = E>> {
49 #[inline(always)]
55 fn use_series(z: V) -> V::Mask {
56 z.cmp_lt(V::ONE)
57 }
58
59 #[inline(always)]
65 fn invalid(z: V) -> V::Mask {
66 z.cmp_lt(V::ZERO) | z.is_nan()
67 }
68
69 #[inline(always)]
78 fn cf_tiny() -> V {
79 V::MIN_POSITIVE
80 }
81}
82
83#[inline(always)]
91fn laguerre_rcp<E: FloatElement>(k: usize) -> E {
92 E::from_ratio(1, (k + 1) as thermite::LargeInt)
93}
94
95pub trait SpecializedSpecialMath<E>: thermite::math::specialized::SpecializedTranscendentalMath<E> {
96 type ExpIntDetails: ExpIntDetails<E, Self>;
99
100 #[inline(always)]
111 fn exp_two_sum(a: Self, b: Self) -> (Self, Self) {
112 (a + b, Self::ZERO)
113 }
114
115 const LAGUERRE_PRODUCT_SEED_CAP: i32 = 0;
129
130 fn erf<P: Policy>(self) -> Self;
131
132 #[inline(always)]
133 fn erfc<P: Policy>(self) -> Self {
134 Self::ONE - self.erf_p::<P>()
135 }
136
137 #[inline(always)]
146 fn erfcx<P: Policy>(self) -> Self {
147 (self * self).exp_p::<P>() * self.erfc_p::<P>()
148 }
149
150 #[inline(always)]
152 fn expint_n<P: Policy, const N: usize>(self) -> Self {
153 self.expint_primal_n::<P, N>().0
154 }
155
156 #[inline(always)]
169 fn expint_primal_n<P: Policy, const N: usize>(self) -> (Self, Self) {
170 let x = self;
171
172 let exp_neg_x = (-x).exp_p::<P>();
177 let inv_x = x.approx_reciprocal_p::<P>();
178 let e0 = exp_neg_x * inv_x;
179
180 if const { N == 0 } {
181 let mut value = e0;
182 let mut prev = e0 * (Self::ONE + inv_x);
183
184 if const { P::POLICY.check_overflow } {
185 let x_is_zero = x.is_zero();
187 value = x_is_zero.select(Self::INFINITY, value);
188 prev = x_is_zero.select(Self::INFINITY, prev);
189
190 let bad = <Self::ExpIntDetails as ExpIntDetails<E, Self>>::invalid(x);
191 value = bad.select(Self::NAN, value);
192 prev = bad.select(Self::NAN, prev);
193 }
194
195 return (value, prev);
196 }
197
198 let mut e_n = Self::expint_e1_generic::<P>(x, exp_neg_x);
199
200 let mut e_prev = e0;
203
204 if const { N > 1 } {
207 let mut n = 1u32;
208 while n < N as u32 {
209 let nf = Self::splat(E::from_int(n as thermite::LargeInt));
210 e_prev = e_n;
211 e_n = x.nmul_adde(e_n, exp_neg_x) / nf;
212 n += 1;
213 }
214 }
215
216 if const { P::POLICY.check_overflow } {
218 let x_is_zero = x.is_zero();
220 if const { N == 1 } {
221 e_n = x_is_zero.select(Self::INFINITY, e_n);
222 } else if const { N > 1 } {
223 e_n = x_is_zero.select(Self::splat(E::ONE / E::from_int(N as thermite::LargeInt - 1)), e_n);
224 }
225
226 if const { N <= 2 } {
228 e_prev = x_is_zero.select(Self::INFINITY, e_prev);
229 } else {
230 e_prev = x_is_zero.select(Self::splat(E::ONE / E::from_int(N as thermite::LargeInt - 2)), e_prev);
231 }
232
233 let bad = <Self::ExpIntDetails as ExpIntDetails<E, Self>>::invalid(x);
234 e_n = bad.select(Self::NAN, e_n);
235 e_prev = bad.select(Self::NAN, e_prev);
236 }
237
238 (e_n, e_prev)
239 }
240
241 #[inline(always)]
243 fn expint<P: Policy>(self, n: u32) -> Self {
244 self.expint_primal::<P>(n).0
245 }
246
247 #[inline(always)]
250 fn expint_primal<P: Policy>(self, n: u32) -> (Self, Self) {
251 let x = self;
252
253 let exp_neg_x = (-x).exp_p::<P>();
254 let inv_x = x.approx_reciprocal_p::<P>();
255 let e0 = exp_neg_x * inv_x;
256
257 if n == 0 {
258 let mut value = e0;
259 let mut prev = e0 * (Self::ONE + inv_x);
260
261 if const { P::POLICY.check_overflow } {
262 let x_is_zero = x.is_zero();
263 value = x_is_zero.select(Self::INFINITY, value);
264 prev = x_is_zero.select(Self::INFINITY, prev);
265
266 let bad = <Self::ExpIntDetails as ExpIntDetails<E, Self>>::invalid(x);
267 value = bad.select(Self::NAN, value);
268 prev = bad.select(Self::NAN, prev);
269 }
270
271 return (value, prev);
272 }
273
274 let mut e_n = Self::expint_e1_generic::<P>(x, exp_neg_x);
275 let mut e_prev = e0;
276
277 let mut k = 1u32;
278 while k < n {
279 let kf = Self::splat(E::from_int(k as thermite::LargeInt));
280 e_prev = e_n;
281 e_n = x.nmul_adde(e_n, exp_neg_x) / kf;
282 k += 1;
283 }
284
285 if const { P::POLICY.check_overflow } {
286 let x_is_zero = x.is_zero();
287 if n == 1 {
288 e_n = x_is_zero.select(Self::INFINITY, e_n);
289 } else {
290 e_n = x_is_zero.select(Self::splat(E::ONE / E::from_int(n as thermite::LargeInt - 1)), e_n);
291 }
292
293 if n <= 2 {
294 e_prev = x_is_zero.select(Self::INFINITY, e_prev);
295 } else {
296 e_prev = x_is_zero.select(Self::splat(E::ONE / E::from_int(n as thermite::LargeInt - 2)), e_prev);
297 }
298
299 let bad = <Self::ExpIntDetails as ExpIntDetails<E, Self>>::invalid(x);
300 e_n = bad.select(Self::NAN, e_n);
301 e_prev = bad.select(Self::NAN, e_prev);
302 }
303
304 (e_n, e_prev)
305 }
306
307 #[doc(hidden)]
311 #[inline(always)]
312 fn expint_e1_generic<P: Policy>(x: Self, exp_neg_x: Self) -> Self {
313 let use_series = <Self::ExpIntDetails as ExpIntDetails<E, Self>>::use_series(x);
328
329 let neg_x = -x;
331 let mut s_term = neg_x; let mut s_sum = s_term; let tiny = <Self::ExpIntDetails as ExpIntDetails<E, Self>>::cf_tiny();
344
345 let mut cf_f = tiny;
347 let mut cf_c = tiny;
348
349 let mut cf_d = {
352 let b1 = x + Self::ONE;
353 let d1 = b1.approx_reciprocal_p::<P>();
355 cf_c = b1 + cf_c.approx_reciprocal_p::<P>();
357 let delta = cf_c * d1;
358 cf_f *= delta; d1
360 };
361
362 let eps = Self::splat(E::EPSILON);
364
365 let mut series_done = !use_series; let mut cf_done = use_series; let mut k = 1usize;
369 while k < const { P::POLICY.max_iterations } {
370 let kf = Self::splat(E::from_int(k as thermite::LargeInt));
371 let kp1 = Self::splat(E::from_int(k as thermite::LargeInt + 1));
372
373 if !series_done.all() {
376 s_term *= (neg_x * kf) / (kp1 * kp1);
377 s_sum = series_done.select(s_sum, s_sum + s_term);
378
379 let term_small = s_term.abs().cmp_lt(s_sum.abs() * eps);
380
381 series_done = GenericMask::ternlog::<{ thermite::ternlog_imm!(A | (B & C)) }>(
383 series_done,
384 use_series,
385 term_small,
386 );
387 }
388
389 if !cf_done.all() {
392 let neg_a_k = kf * kf; let b_k = (x + kf) + (kf + Self::ONE); let d_denom = neg_a_k.nmul_adde(cf_d, b_k); let new_d = d_denom
398 .cmp_eq(Self::ZERO)
399 .select(tiny, d_denom)
400 .approx_reciprocal_p::<P>();
401
402 let new_c = b_k - neg_a_k / cf_c;
404 let new_c = new_c.cmp_eq(Self::ZERO).select(tiny, new_c);
405
406 let delta = new_c * new_d;
407
408 cf_d = new_d;
409 cf_c = new_c;
410 cf_f = cf_done.select(cf_f, cf_f * delta);
411
412 let cf_converged = (delta - Self::ONE).abs().cmp_lt(eps);
413
414 cf_done =
415 GenericMask::ternlog::<{ thermite::ternlog_imm!(A | (!B & C)) }>(cf_done, use_series, cf_converged);
416 }
417
418 if (series_done & cf_done).all() {
419 break;
420 }
421
422 k += 1;
423 }
424
425 let mut series_result = Self::EMPTY;
429
430 let mut cf_result = Self::EMPTY;
432
433 if use_series.any() {
434 series_result = (-Self::EULER_GAMMA - s_sum) - x.ln_p::<P>();
435 }
436
437 if !use_series.all() {
438 cf_result = cf_f * exp_neg_x;
439 }
440
441 use_series.select(series_result, cf_result)
442 }
443
444 #[inline(always)]
445 fn logistic_sigmoid<P: Policy>(self) -> Self {
446 if const { P::POLICY.precision.gt(PrecisionPolicy::Average) } {
447 let is_pos = self.is_positive();
448 let x = self.neg_c(is_pos); let e = x.exp_p::<P>();
450
451 let n = is_pos.select(Self::ONE, e);
452 let d = Self::ONE + e;
453
454 return n / d;
455 }
456
457 (Self::ONE + (-self).exp_p::<P>()).approx_reciprocal_p::<ExtraPrecision<P>>()
458 }
459
460 #[inline(always)]
461 fn softplus<P: Policy>(self, k: Self, rcp_k: Self) -> Self {
462 if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
465 let k = k.scale(FloatConsts::LOG2_E);
467 let rcp_k = rcp_k.scale(FloatConsts::LN_2);
468
469 let kx = self * k;
470
471 let e = kx.abs().neg().exp2_p::<CheckOverflow<P, true>>();
473 return (Self::ONE + e).log2_p::<P>().mul_adde(rcp_k, self.max(Self::ZERO));
474 }
475
476 let kx = self * k;
477
478 let e = kx.abs().neg().exp_p::<P>();
479
480 e.ln_1p_p::<P>().mul_adde(rcp_k, self.max(Self::ZERO))
482 }
483
484 fn tgamma<P: Policy>(self) -> Self;
485 fn lgamma<P: Policy>(self) -> Self;
486 fn digamma<P: Policy>(self) -> Self;
487
488 fn trigamma<P: Policy>(self) -> Self;
498
499 fn polygamma<P: Policy>(self, n: u32) -> Self;
512
513 #[inline(always)]
518 fn zetac<P: Policy>(self) -> Self {
519 todo!("zetac is not implemented for this composite type; see the trait method's docs")
520 }
521
522 #[inline(always)]
525 fn zeta<P: Policy>(self) -> Self {
526 todo!("zeta is not implemented for this composite type; see `zetac`'s docs")
527 }
528
529 #[inline(always)]
533 fn polylog<P: Policy>(
534 self,
535 order: crate::PolylogOrder<
536 E,
537 <<Self as thermite::vector::GenericVector>::Signed as thermite::vector::GenericVector>::Element,
538 >,
539 ) -> Self {
540 let _ = order;
541 todo!("polylog is not implemented for this composite type; see the trait method's docs")
542 }
543
544 #[inline(always)]
553 fn zeta_with_deriv<P: Policy, const ZETAC: bool>(self) -> (Self, Self) {
554 todo!("zeta_with_deriv is not implemented for this composite type; see `zetac`'s docs")
555 }
556
557 #[inline(always)]
567 fn bessel_i<P: Policy, const N: i32>(self) -> Self {
568 todo!("bessel_i is not implemented for this composite type")
569 }
570
571 #[inline(always)]
575 fn bessel_i_scaled<P: Policy, const N: i32>(self) -> Self {
576 todo!("bessel_i_scaled is not implemented for this composite type")
577 }
578
579 #[inline(always)]
582 fn bessel_k<P: Policy, const N: i32>(self) -> Self {
583 todo!("bessel_k is not implemented for this composite type")
584 }
585
586 #[inline(always)]
590 fn bessel_k_scaled<P: Policy, const N: i32>(self) -> Self {
591 todo!("bessel_k_scaled is not implemented for this composite type")
592 }
593
594 #[inline(always)]
597 fn bessel_j<P: Policy, const N: i32>(self) -> Self {
598 todo!("bessel_j is not implemented for this composite type")
599 }
600
601 #[inline(always)]
603 fn bessel_y<P: Policy, const N: i32>(self) -> Self {
604 todo!("bessel_y is not implemented for this composite type")
605 }
606
607 #[inline(always)]
619 fn bessel_i_with_deriv<P: Policy, const N: i32, const SCALED: bool>(self) -> (Self, Self) {
620 todo!("bessel_i_with_deriv is not implemented for this composite type")
621 }
622
623 #[inline(always)]
625 fn bessel_k_with_deriv<P: Policy, const N: i32, const SCALED: bool>(self) -> (Self, Self) {
626 todo!("bessel_k_with_deriv is not implemented for this composite type")
627 }
628
629 #[inline(always)]
631 fn bessel_j_with_deriv<P: Policy, const N: i32>(self) -> (Self, Self) {
632 todo!("bessel_j_with_deriv is not implemented for this composite type")
633 }
634
635 #[inline(always)]
637 fn bessel_y_with_deriv<P: Policy, const N: i32>(self) -> (Self, Self) {
638 todo!("bessel_y_with_deriv is not implemented for this composite type")
639 }
640
641 #[inline(always)]
643 fn bessel_iv<P: Policy, const SCALED: bool>(self, _order: crate::BesselOrder<Self, Self::Signed>) -> Self {
644 todo!("bessel_iv is not implemented for this composite type")
645 }
646
647 #[inline(always)]
649 fn bessel_kv<P: Policy, const SCALED: bool>(self, _order: crate::BesselOrder<Self, Self::Signed>) -> Self {
650 todo!("bessel_kv is not implemented for this composite type")
651 }
652
653 #[inline(always)]
655 fn bessel_jv<P: Policy>(self, _order: crate::BesselOrder<Self, Self::Signed>) -> Self {
656 todo!("bessel_jv is not implemented for this composite type")
657 }
658
659 #[inline(always)]
661 fn bessel_yv<P: Policy>(self, _order: crate::BesselOrder<Self, Self::Signed>) -> Self {
662 todo!("bessel_yv is not implemented for this composite type")
663 }
664
665 #[inline(always)]
668 fn sph_bessel_j_n<P: Policy, const N: usize>(self) -> Self {
669 todo!("sph_bessel_j is not implemented for this composite type")
670 }
671
672 #[inline(always)]
674 fn sph_bessel_y_n<P: Policy, const N: usize>(self) -> Self {
675 todo!("sph_bessel_y is not implemented for this composite type")
676 }
677
678 #[inline(always)]
680 fn sph_bessel_i_n<P: Policy, const N: usize>(self) -> Self {
681 todo!("sph_bessel_i is not implemented for this composite type")
682 }
683
684 #[inline(always)]
686 fn sph_bessel_i_scaled_n<P: Policy, const N: usize>(self) -> Self {
687 todo!("sph_bessel_i_scaled is not implemented for this composite type")
688 }
689
690 #[inline(always)]
692 fn sph_bessel_k_n<P: Policy, const N: usize>(self) -> Self {
693 todo!("sph_bessel_k is not implemented for this composite type")
694 }
695
696 #[inline(always)]
698 fn sph_bessel_k_scaled_n<P: Policy, const N: usize>(self) -> Self {
699 todo!("sph_bessel_k_scaled is not implemented for this composite type")
700 }
701
702 #[inline(always)]
709 fn sph_bessel_j_with_deriv_n<P: Policy, const N: usize>(self) -> (Self, Self) {
710 todo!("sph_bessel_j_with_deriv is not implemented for this composite type")
711 }
712
713 #[inline(always)]
715 fn sph_bessel_y_with_deriv_n<P: Policy, const N: usize>(self) -> (Self, Self) {
716 todo!("sph_bessel_y_with_deriv is not implemented for this composite type")
717 }
718
719 #[inline(always)]
722 fn sph_bessel_i_with_deriv_n<P: Policy, const N: usize, const SCALED: bool>(self) -> (Self, Self) {
723 todo!("sph_bessel_i_with_deriv is not implemented for this composite type")
724 }
725
726 #[inline(always)]
728 fn sph_bessel_k_with_deriv_n<P: Policy, const N: usize, const SCALED: bool>(self) -> (Self, Self) {
729 todo!("sph_bessel_k_with_deriv is not implemented for this composite type")
730 }
731
732 #[inline(always)]
736 fn sph_bessel_j<P: Policy>(self, n: u32) -> Self {
737 let _ = n;
738 todo!("sph_bessel_j is not implemented for this composite type")
739 }
740
741 #[inline(always)]
743 fn sph_bessel_y<P: Policy>(self, n: u32) -> Self {
744 let _ = n;
745 todo!("sph_bessel_y is not implemented for this composite type")
746 }
747
748 #[inline(always)]
750 fn sph_bessel_i<P: Policy>(self, n: u32) -> Self {
751 let _ = n;
752 todo!("sph_bessel_i is not implemented for this composite type")
753 }
754
755 #[inline(always)]
757 fn sph_bessel_i_scaled<P: Policy>(self, n: u32) -> Self {
758 let _ = n;
759 todo!("sph_bessel_i_scaled is not implemented for this composite type")
760 }
761
762 #[inline(always)]
764 fn sph_bessel_k<P: Policy>(self, n: u32) -> Self {
765 let _ = n;
766 todo!("sph_bessel_k is not implemented for this composite type")
767 }
768
769 #[inline(always)]
771 fn sph_bessel_k_scaled<P: Policy>(self, n: u32) -> Self {
772 let _ = n;
773 todo!("sph_bessel_k_scaled is not implemented for this composite type")
774 }
775
776 #[inline(always)]
779 fn sph_bessel_j_with_deriv<P: Policy>(self, n: u32) -> (Self, Self) {
780 let _ = n;
781 todo!("sph_bessel_j_with_deriv is not implemented for this composite type")
782 }
783
784 #[inline(always)]
786 fn sph_bessel_y_with_deriv<P: Policy>(self, n: u32) -> (Self, Self) {
787 let _ = n;
788 todo!("sph_bessel_y_with_deriv is not implemented for this composite type")
789 }
790
791 #[inline(always)]
793 fn sph_bessel_i_with_deriv<P: Policy, const SCALED: bool>(self, n: u32) -> (Self, Self) {
794 let _ = n;
795 todo!("sph_bessel_i_with_deriv is not implemented for this composite type")
796 }
797
798 #[inline(always)]
800 fn sph_bessel_k_with_deriv<P: Policy, const SCALED: bool>(self, n: u32) -> (Self, Self) {
801 let _ = n;
802 todo!("sph_bessel_k_with_deriv is not implemented for this composite type")
803 }
804
805 #[inline(always)]
815 fn bessel_n<P: Policy, F: crate::bessel::BesselFamily, const N: i32>(self) -> Self {
816 F::cyl_n::<P, E, Self, N>(self)
817 }
818
819 #[inline(always)]
821 fn bessel<P: Policy, F: crate::bessel::BesselFamily>(self, order: crate::BesselOrder<Self, Self::Signed>) -> Self {
822 F::cyl_v::<P, E, Self>(self, order)
823 }
824
825 #[inline(always)]
827 fn sph_bessel_n<P: Policy, F: crate::bessel::BesselFamily, const N: usize>(self) -> Self {
828 F::sph_n::<P, E, Self, N>(self)
829 }
830
831 #[inline(always)]
833 fn sph_bessel<P: Policy, F: crate::bessel::BesselFamily>(self, n: u32) -> Self {
834 F::sph_v::<P, E, Self>(self, n)
835 }
836
837 #[inline(always)]
839 fn airy<P: Policy, W: crate::bessel::AiryFn>(self) -> Self {
840 W::eval::<P, E, Self, false>(self)
841 }
842
843 #[inline(always)]
845 fn airy_all<P: Policy, const SCALED: bool>(self) -> (Self, Self, Self, Self) {
846 if const { SCALED } {
847 self.airy_tuple_scaled::<P>()
848 } else {
849 self.airy_tuple::<P>()
850 }
851 }
852
853 #[inline(always)]
856 fn bessel_jv_scaled<P: Policy>(self, order: crate::BesselOrder<Self, Self::Signed>) -> Self {
857 self.bessel_jv::<P>(order)
858 }
859
860 #[inline(always)]
862 fn bessel_yv_scaled<P: Policy>(self, order: crate::BesselOrder<Self, Self::Signed>) -> Self {
863 self.bessel_yv::<P>(order)
864 }
865
866 #[inline(always)]
877 fn airy_tuple<P: Policy>(self) -> (Self, Self, Self, Self) {
878 todo!("airy is not implemented for this composite type")
879 }
880
881 #[inline(always)]
885 fn airy_tuple_scaled<P: Policy>(self) -> (Self, Self, Self, Self) {
886 todo!("airy_scaled is not implemented for this composite type")
887 }
888
889 #[inline(always)]
892 fn airy_ai<P: Policy>(self) -> Self {
893 todo!("airy_ai is not implemented for this composite type")
894 }
895
896 #[inline(always)]
898 fn airy_ai_scaled<P: Policy>(self) -> Self {
899 todo!("airy_ai_scaled is not implemented for this composite type")
900 }
901
902 #[inline(always)]
904 fn airy_bi<P: Policy>(self) -> Self {
905 todo!("airy_bi is not implemented for this composite type")
906 }
907
908 #[inline(always)]
910 fn airy_bi_scaled<P: Policy>(self) -> Self {
911 todo!("airy_bi_scaled is not implemented for this composite type")
912 }
913
914 #[inline(always)]
916 fn airy_ai_prime<P: Policy>(self) -> Self {
917 todo!("airy_ai_prime is not implemented for this composite type")
918 }
919
920 #[inline(always)]
923 fn airy_ai_prime_scaled<P: Policy>(self) -> Self {
924 todo!("airy_ai_prime_scaled is not implemented for this composite type")
925 }
926
927 #[inline(always)]
929 fn airy_bi_prime<P: Policy>(self) -> Self {
930 todo!("airy_bi_prime is not implemented for this composite type")
931 }
932
933 #[inline(always)]
936 fn airy_bi_prime_scaled<P: Policy>(self) -> Self {
937 todo!("airy_bi_prime_scaled is not implemented for this composite type")
938 }
939
940 #[inline(always)]
941 fn hermite_n<P: Policy, const N: usize>(mut x: Self) -> Self {
942 #[cfg(not(target_arch = "spirv"))]
943 if let Some(new_x) = FlushDenormals::<P>::flush_denormals([x]) {
944 x = new_x[0];
945 }
946
947 let mut p0 = Self::ONE;
948
949 if const { N == 0 } {
950 return p0;
951 }
952
953 let mut p1 = x + x; cfg_if::cfg_if! {
956 if #[cfg(all(feature = "spirv", target_arch = "spirv"))] {
957 use crunchy::unroll;
958
959 macro_rules! unroll_poly {
960 ($($len:tt),*) => {
961 $( if const { N == $len } {
962 unroll! { for n in 0..$len {
963 (p0, p1) = (p1, p0); const cf: thermite::LargeInt = (1 + n) as thermite::LargeInt;
966 let next0 = x.mul_sube(p0, p1.scale(E::ConstInt::<{cf}>::VALUE));
967 p1 = next0 + next0; }}
969 } else )* {
970 let mut c = 1;
971 let mut cf = E::ONE;
972
973 while c < N {
974 (p0, p1) = (p1, p0); let next0 = x.mul_sube(p0, p1.scale(cf));
977 p1 = next0 + next0; c += 1;
980 cf = cf + E::ONE;
981 }
982 }
983 };
984 }
985
986 unroll_poly!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); } else {
988 let mut c = 1;
989 let mut cf = Self::ONE;
990
991 while c < N {
992 (p0, p1) = (p1, p0); let next0 = x.mul_sube(p0, cf * p1);
995 p1 = next0 + next0; c += 1;
998 cf += Self::ONE;
999 }
1000 }
1001 }
1002
1003 p1
1004 }
1005
1006 #[inline(always)]
1007 fn hermitev<P: Policy>(mut x: Self, n: Self::Unsigned) -> Self {
1008 #[cfg(not(target_arch = "spirv"))]
1009 if let Some(new_x) = FlushDenormals::<P>::flush_denormals([x]) {
1010 x = new_x[0];
1011 }
1012
1013 let i1 = Self::Unsigned::ONE;
1014 let n_is_zero = n.cmp_eq(Self::Unsigned::ZERO);
1015
1016 let mut c = i1;
1017
1018 let mut cf = Self::ONE;
1020
1021 let mut p0 = Self::ONE;
1022 let mut p1 = x + x; loop {
1025 let cont = c.cmp_lt(n);
1026
1027 if cont.none() {
1028 break;
1029 }
1030
1031 let next0 = x.mul_sube(p1, cf * p0);
1033 let next = next0 + next0; p0 = cont.select(p1, p0);
1040 p1 = cont.select(next, p1);
1041
1042 c += i1;
1043 cf += Self::ONE;
1044 }
1045
1046 n_is_zero.select(Self::ONE, p1)
1047 }
1048
1049 #[inline(always)]
1052 fn hermite<P: Policy>(self, n: u32) -> Self {
1053 Self::hermitev::<P>(
1054 self,
1055 Self::splat(E::from_int(n as thermite::LargeInt)).to_unsigned_integer(),
1056 )
1057 }
1058
1059 #[inline(always)]
1060 fn hermite_function_n<P: Policy, const N: usize>(mut x: Self) -> Self {
1061 #[cfg(not(target_arch = "spirv"))]
1062 if let Some(new_x) = FlushDenormals::<P>::flush_denormals([x]) {
1063 x = new_x[0];
1064 }
1065
1066 generic::hermite::hermite_function_n::<P, _, _, N, false>(x)
1067 }
1068
1069 #[inline(always)]
1070 fn hermite_function<P: Policy>(mut x: Self, n: u32) -> Self {
1071 #[cfg(not(target_arch = "spirv"))]
1072 if let Some(new_x) = FlushDenormals::<P>::flush_denormals([x]) {
1073 x = new_x[0];
1074 }
1075
1076 generic::hermite::hermite_function::<P, _, _, false>(x, n)
1077 }
1078
1079 #[inline(always)]
1080 fn hermite_function_series_n<P: Policy, const N: usize>(self, coeffs: &[Self::Element; N]) -> Self {
1081 generic::hermite::hermite_function_series::<P, _, _, N, false>(self, coeffs)
1082 }
1083
1084 #[inline(always)]
1085 fn hermite_function_series<P: Policy>(self, coeffs: &[Self::Element]) -> Self {
1086 generic::hermite::hermite_function_series_slice::<P, _, _, false>(self, coeffs)
1087 }
1088
1089 #[inline(always)]
1090 fn laguerre_n<P: Policy, const N: usize>(mut x: Self, mut alpha: Self) -> Self {
1091 #[cfg(not(target_arch = "spirv"))]
1092 if let Some(new) = FlushDenormals::<P>::flush_denormals([x, alpha]) {
1093 x = new[0];
1094 alpha = new[1];
1095 }
1096
1097 if const { N == 0 } {
1098 return Self::ONE;
1099 }
1100
1101 let mut p0 = Self::ONE; let mut p1 = (Self::ONE + alpha) - x; let mut k = 1;
1105 let mut kf = Self::ONE; while k < N {
1108 let b = ((kf + kf) + Self::ONE + alpha) - x;
1110 let c = kf + alpha;
1111
1112 let next = b.mul_sube(p1, c * p0) * Self::splat(laguerre_rcp::<E>(k));
1113
1114 p0 = p1;
1115 p1 = next;
1116
1117 k += 1;
1118 kf += Self::ONE;
1119 }
1120
1121 p1
1122 }
1123
1124 #[inline(always)]
1125 fn laguerrev<P: Policy>(mut x: Self, mut alpha: Self, n: Self::Unsigned) -> Self {
1126 #[cfg(not(target_arch = "spirv"))]
1127 if let Some(new) = FlushDenormals::<P>::flush_denormals([x, alpha]) {
1128 x = new[0];
1129 alpha = new[1];
1130 }
1131
1132 let i1 = Self::Unsigned::ONE;
1133 let n_is_zero = n.cmp_eq(Self::Unsigned::ZERO);
1134
1135 let mut c = i1;
1136
1137 let mut k = 1;
1138 let mut kf = Self::ONE;
1139
1140 let mut p0 = Self::ONE;
1141 let mut p1 = (Self::ONE + alpha) - x;
1142
1143 loop {
1144 let cont = c.cmp_lt(n);
1145
1146 if cont.none() {
1147 break;
1148 }
1149
1150 let b = ((kf + kf) + Self::ONE + alpha) - x;
1151 let ck = kf + alpha;
1152
1153 let next = b.mul_sube(p1, ck * p0) * Self::splat(laguerre_rcp::<E>(k));
1154
1155 p0 = cont.select(p1, p0);
1159 p1 = cont.select(next, p1);
1160
1161 c += i1;
1162 k += 1;
1163 kf += Self::ONE;
1164 }
1165
1166 n_is_zero.select(Self::ONE, p1)
1167 }
1168
1169 #[inline(always)]
1171 fn laguerre<P: Policy>(self, alpha: Self, n: u32) -> Self {
1172 Self::laguerrev::<P>(
1173 self,
1174 alpha,
1175 Self::splat(E::from_int(n as thermite::LargeInt)).to_unsigned_integer(),
1176 )
1177 }
1178
1179 #[inline(always)]
1180 fn laguerre_function_n<P: Policy, const N: usize>(mut x: Self, mut alpha: Self) -> Self {
1181 #[cfg(not(target_arch = "spirv"))]
1182 if let Some(new) = FlushDenormals::<P>::flush_denormals([x, alpha]) {
1183 x = new[0];
1184 alpha = new[1];
1185 }
1186
1187 generic::laguerre::laguerre_function_n::<P, _, _, N, false>(x, alpha, 0)
1188 }
1189
1190 #[inline(always)]
1191 fn laguerre_function<P: Policy>(mut x: Self, mut alpha: Self, n: u32) -> Self {
1192 #[cfg(not(target_arch = "spirv"))]
1193 if let Some(new) = FlushDenormals::<P>::flush_denormals([x, alpha]) {
1194 x = new[0];
1195 alpha = new[1];
1196 }
1197
1198 generic::laguerre::laguerre_function::<P, _, _, false>(x, alpha, 0, n)
1199 }
1200
1201 #[inline(always)]
1202 fn laguerre_function_i_n<P: Policy, const N: usize>(mut x: Self, alpha: i32) -> Self {
1203 #[cfg(not(target_arch = "spirv"))]
1204 if let Some(new) = FlushDenormals::<P>::flush_denormals([x]) {
1205 x = new[0];
1206 }
1207
1208 generic::laguerre::laguerre_function_n::<P, _, _, N, true>(x, Self::ZERO, alpha)
1209 }
1210
1211 #[inline(always)]
1212 fn laguerre_function_i<P: Policy>(mut x: Self, alpha: i32, n: u32) -> Self {
1213 #[cfg(not(target_arch = "spirv"))]
1214 if let Some(new) = FlushDenormals::<P>::flush_denormals([x]) {
1215 x = new[0];
1216 }
1217
1218 generic::laguerre::laguerre_function::<P, _, _, true>(x, Self::ZERO, alpha, n)
1219 }
1220
1221 #[inline(always)]
1222 fn poisson_pmf<P: Policy>(self, lambda: Self) -> Self {
1223 generic::poisson::poisson_pmf::<P, _, _, false>(self, lambda)
1224 }
1225
1226 #[inline(always)]
1227 fn poisson_log_pmf<P: Policy>(self, lambda: Self) -> Self {
1228 generic::poisson::poisson_pmf::<P, _, _, true>(self, lambda)
1229 }
1230
1231 #[inline(always)]
1232 fn laguerre_function_series_n<P: Policy, const N: usize>(self, alpha: Self, coeffs: &[Self::Element; N]) -> Self {
1233 generic::laguerre::laguerre_function_series::<P, _, _, N, false>(self, alpha, 0, coeffs)
1234 }
1235
1236 #[inline(always)]
1237 fn laguerre_function_series_i_n<P: Policy, const N: usize>(self, alpha: i32, coeffs: &[Self::Element; N]) -> Self {
1238 generic::laguerre::laguerre_function_series::<P, _, _, N, true>(self, Self::ZERO, alpha, coeffs)
1239 }
1240
1241 #[inline(always)]
1242 fn laguerre_function_series<P: Policy>(self, alpha: Self, coeffs: &[Self::Element]) -> Self {
1243 generic::laguerre::laguerre_function_series_slice::<P, _, _, false>(self, alpha, 0, coeffs)
1244 }
1245
1246 #[inline(always)]
1247 fn laguerre_function_series_i<P: Policy>(self, alpha: i32, coeffs: &[Self::Element]) -> Self {
1248 generic::laguerre::laguerre_function_series_slice::<P, _, _, true>(self, Self::ZERO, alpha, coeffs)
1249 }
1250
1251 #[inline(always)]
1252 fn chebyshev<P: Policy, const K: usize>(self, coeffs: &[Self::Element]) -> Self {
1253 generic::chebyshev::chebyshev_series::<P, _, _, K, 0, false>(self, coeffs)
1257 }
1258
1259 #[inline(always)]
1260 fn chebyshev_n<P: Policy, const K: usize, const N: usize>(self, coeffs: &[Self::Element; N]) -> Self {
1261 const {
1265 assert!(N >= 1, "chebyshev_n: N must be at least 1");
1266 }
1267
1268 generic::chebyshev::chebyshev_series::<P, _, _, K, N, false>(self, coeffs)
1273 }
1274
1275 #[inline(always)]
1276 fn jacobi<P: Policy>(mut x: Self, mut alpha: Self, mut beta: Self, mut n: u32, m: u32) -> Self {
1277 if thermite::unlikely(m > n) {
1278 return Self::ZERO;
1279 }
1280
1281 #[cfg(not(target_arch = "spirv"))]
1282 if let Some(new) = FlushDenormals::<P>::flush_denormals([x, alpha, beta]) {
1283 x = new[0];
1284 alpha = new[1];
1285 beta = new[2];
1286 }
1287
1288 let mut scale = Self::ONE;
1289
1290 if m > 0 {
1291 let mut jf = Self::ONE;
1292 let nf = Self::splat(E::from_int(n as thermite::LargeInt));
1293
1294 let t0 = Self::HALF * (nf + alpha + beta);
1295
1296 let mut _iter = 0;
1297 while _iter < m {
1298 _iter += 1;
1299 scale *= Self::HALF.mul_adde(jf, t0);
1300 jf += Self::ONE;
1301 }
1302
1303 let mf = Self::splat(E::from_int(m as thermite::LargeInt));
1304
1305 alpha += mf;
1306 beta += mf;
1307 n -= m;
1308 }
1309
1310 if thermite::unlikely(n == 0) {
1311 return scale; }
1313
1314 let mut y0 = Self::ONE;
1315
1316 let alpha_p_beta = alpha + beta;
1317 let alpha_sqr = alpha * alpha;
1318 let beta_sqr = beta * beta;
1319 let alpha1 = alpha - Self::ONE;
1320 let beta1 = beta - Self::ONE;
1321 let alpha2beta2 = alpha_sqr - beta_sqr;
1322
1323 let mut y1 = Self::HALF * (x.mul_adde(alpha, alpha) + x.mul_sube(beta, beta) + x + x);
1325
1326 let mut yk = y1;
1327 let mut k = E::ConstInt::<2>::VALUE;
1328
1329 let k_max = E::from_int(n as thermite::LargeInt) * (<E as Element>::ONE + E::EPSILON);
1330
1331 while k < k_max {
1332 let kf = Self::splat(k);
1333 let kf2 = Self::TWO * kf;
1334
1335 let k_alpha_p_beta = kf + alpha_p_beta;
1336 let k2_alpha_p_beta = kf2 + alpha_p_beta;
1337
1338 let k2_alpha_p_beta_m2 = k2_alpha_p_beta - Self::TWO;
1339
1340 let denom = kf2 * k_alpha_p_beta * k2_alpha_p_beta_m2;
1341 let t0 = x.mul_adde(k2_alpha_p_beta * k2_alpha_p_beta_m2, alpha2beta2);
1342 let gamma1 = k2_alpha_p_beta.mul_sube(t0, t0);
1343 let gamma0 = Self::TWO * (kf + alpha1) * (kf + beta1) * k2_alpha_p_beta;
1344
1345 yk = gamma1.mul_sube(y1, gamma0 * y0) / denom;
1346
1347 y0 = y1;
1348 y1 = yk;
1349
1350 k = k + <E as Element>::ONE;
1351 }
1352
1353 scale * yk
1354 }
1355
1356 #[inline(always)]
1357 fn gaussian<P: Policy>(mut x: Self, a: Self, c: Self) -> Self {
1358 #[cfg(not(target_arch = "spirv"))]
1359 if let Some(new_x) = FlushDenormals::<P>::flush_denormals([x]) {
1360 x = new_x[0];
1361 }
1362
1363 let xc = if const { P::POLICY.precision.le(PrecisionPolicy::Worst) } {
1364 x * c.approx_reciprocal_p::<P>()
1365 } else {
1366 x / c
1367 };
1368
1369 a * (-Self::HALF * xc * xc).exp_p::<P>()
1370 }
1371
1372 fn beta<P: Policy>(a: Self, b: Self) -> Self;
1373
1374 #[inline(always)]
1375 fn lbeta<P: Policy>(a: Self, b: Self) -> Self {
1376 Self::lgamma::<P>(a) + Self::lgamma::<P>(b) - Self::lgamma::<P>(a + b)
1380 }
1381
1382 #[inline(always)]
1383 fn logit<P: Policy>(self) -> Self {
1384 Self::ln::<P>(self) - Self::ln_1p::<P>(-self)
1387 }
1388
1389 #[inline(always)]
1390 fn logit_1m<P: Policy>(self) -> Self {
1391 Self::ln_1p::<P>(-self) - Self::ln::<P>(self)
1394 }
1395
1396 #[inline(always)]
1397 fn planck<P: Policy>(self) -> Self {
1398 (self * self).approx_div_p::<P>(Self::phi_n_p::<P, 1>(self))
1401 }
1402
1403 #[rustfmt::skip]
1404 #[inline(always)]
1405 fn legendre0<P: Policy, const N: u32>(x: Self, n: u32) -> Self {
1406 let x2 = x.square();
1407 let x4 = x2.square();
1408 let x8 = x4.square();
1409
1410 if const { N != 0 } {
1411 unsafe { core::hint::assert_unchecked(N == n); }
1412 }
1413
1414 match n {
1416 1 => x,
1417 2 => x2.mul_adde(const_splat!(ratio <E>: 3 / 2), const_splat!(ratio <E>: -1 / 2)),
1418 3 => x * x2.mul_adde(const_splat!(ratio <E>: 5 / 2), const_splat!(ratio <E>: -3 / 2)),
1419 4 => x4.mul_adde(const_splat!(ratio <E>: 35 / 8), x2.mul_adde(const_splat!(ratio <E>: -15 / 4), const_splat!(ratio <E>: 3 / 8))),
1420 5 => x * x4.mul_adde(const_splat!(ratio <E>: 63 / 8), x2.mul_adde(const_splat!(ratio <E>: -35 / 4), const_splat!(ratio <E>: 15 / 8))),
1421 6 => x4.mul_adde(
1422 x2.mul_adde(const_splat!(ratio <E>: 231 / 16), const_splat!(ratio <E>: -315 / 16)),
1423 x2.mul_adde(const_splat!(ratio <E>: 105 / 16), const_splat!(ratio <E>: -5 / 16)),
1424 ),
1425 7 => x * x4.mul_adde(
1426 x2.mul_adde(const_splat!(ratio <E>: 429 / 16), const_splat!(ratio <E>: -693 / 16)),
1427 x2.mul_adde(const_splat!(ratio <E>: 315 / 16), const_splat!(ratio <E>: -35 / 16)),
1428 ),
1429 8 => x8.mul_adde(const_splat!(ratio <E>: 6435 / 128), x4.mul_adde(
1430 x2.mul_adde(const_splat!(ratio <E>: -3003 / 32), const_splat!(ratio <E>: 3465 / 64)),
1431 x2.mul_adde(const_splat!(ratio <E>: -315 / 32), const_splat!(ratio <E>: 35 / 128)),
1432 )),
1433 9 => x * x8.mul_adde(const_splat!(ratio <E>: 12155 / 128), x4.mul_adde(
1434 x2.mul_adde(const_splat!(ratio <E>: -6435 / 32), const_splat!(ratio <E>: 9009 / 64)),
1435 x2.mul_adde(const_splat!(ratio <E>: -1155 / 32), const_splat!(ratio <E>: 315 / 128)),
1436 )),
1437 10 => x8.mul_adde(
1438 x2.mul_adde(const_splat!(ratio <E>: 46189 / 256), const_splat!(ratio <E>: -109395 / 256)),
1439 x4.mul_adde(
1440 x2.mul_adde(const_splat!(ratio <E>: 45045 / 128), const_splat!(ratio <E>: -15015 / 128)),
1441 x2.mul_adde(const_splat!(ratio <E>: 3465 / 256), const_splat!(ratio <E>: -63 / 256)),
1442 ),
1443 ),
1444 11 => x * x8.mul_adde(
1445 x2.mul_adde(const_splat!(ratio <E>: 88179 / 256), const_splat!(ratio <E>: -230945 / 256)),
1446 x4.mul_adde(
1447 x2.mul_adde(const_splat!(ratio <E>: 109395 / 128), const_splat!(ratio <E>: -45045 / 128)),
1448 x2.mul_adde(const_splat!(ratio <E>: 15015 / 256), const_splat!(ratio <E>: -693 / 256)),
1449 ),
1450 ),
1451 12 => x8.mul_adde(
1452 x4.mul_adde(const_splat!(ratio <E>: 676039 / 1024), x2.mul_adde(const_splat!(ratio <E>: -969969 / 512), const_splat!(ratio <E>: 2078505 / 1024))),
1453 x4.mul_adde(
1454 x2.mul_adde(const_splat!(ratio <E>: -255255 / 256), const_splat!(ratio <E>: 225225 / 1024)),
1455 x2.mul_adde(const_splat!(ratio <E>: -9009 / 512), const_splat!(ratio <E>: 231 / 1024)),
1456 ),
1457 ),
1458 13 => x * x8.mul_adde(
1459 x4.mul_adde(const_splat!(ratio <E>: 1300075 / 1024), x2.mul_adde(const_splat!(ratio <E>: -2028117 / 512), const_splat!(ratio <E>: 4849845 / 1024))),
1460 x4.mul_adde(
1461 x2.mul_adde(const_splat!(ratio <E>: -692835 / 256), const_splat!(ratio <E>: 765765 / 1024)),
1462 x2.mul_adde(const_splat!(ratio <E>: -45045 / 512), const_splat!(ratio <E>: 3003 / 1024)),
1463 ),
1464 ),
1465 _ => unsafe { core::hint::unreachable_unchecked() },
1466 }
1467 }
1468
1469 #[inline(always)]
1470 fn legendre<P: Policy>(mut x: Self, n: u32, m: u32) -> Self {
1471 #[cfg(not(target_arch = "spirv"))]
1472 if let Some(new_x) = FlushDenormals::<P>::flush_denormals([x]) {
1473 x = new_x[0];
1474 }
1475
1476 match (n, m) {
1477 (0, 0) => return Self::ONE,
1478 (n, 0) if n < 14 => return Self::legendre0::<P, 0>(x, n),
1479 (n, 0) => {
1480 let mut k = 14; let mut p0 = Self::legendre0::<P, 12>(x, 12); let mut p1 = Self::legendre0::<P, 13>(x, 13); while k <= n {
1487 let nf = Self::splat(E::from_int(k as thermite::LargeInt));
1488
1489 let tmp = p1;
1490 p1 = x.mul_sube((nf + nf).mul_sube(p1, p1), nf.mul_sube(p0, p0)) / nf;
1491 p0 = tmp;
1492
1493 k += 1;
1494 }
1495
1496 return p1;
1497 }
1498 _ => {}
1499 }
1500
1501 let jacobi = Self::jacobi::<P>(x, Self::ZERO, Self::ZERO, n, m);
1502
1503 let x12 = x.nmul_adde(x, Self::ONE); if m & 1 == 0 {
1506 jacobi * Self::powi::<P>(x12, (m >> 1) as i32)
1507 } else {
1508 -jacobi * Self::powi::<P>(x12, m as i32).sqrt()
1510 }
1511 }
1512
1513 #[inline(always)]
1514 fn legendre_series_n<P: Policy, const N: usize>(self, coeffs: &[Self::Element; N]) -> Self {
1515 generic::legendre::legendre_series::<_, _, N>(self, coeffs)
1517 }
1518
1519 #[inline(always)]
1520 fn legendre_series<P: Policy>(self, coeffs: &[Self::Element]) -> Self {
1521 generic::legendre::legendre_series_slice::<_, _>(self, coeffs)
1522 }
1523
1524 #[inline(always)]
1525 fn zernike_r<P: Policy>(mut rho: Self, n: u32, m: u32) -> Self {
1526 if thermite::unlikely(m > n || (n - m) & 1 == 1) {
1529 return Self::ZERO;
1530 }
1531
1532 #[cfg(not(target_arch = "spirv"))]
1533 if let Some(new) = FlushDenormals::<P>::flush_denormals([rho]) {
1534 rho = new[0];
1535 }
1536
1537 let radial = generic::zernike::reduced_radial_impl::<E, Self>(rho.square(), (n - m) >> 1, m);
1542
1543 if m == 0 {
1544 radial
1545 } else {
1546 radial * Self::powi::<P>(rho, m as i32)
1547 }
1548 }
1549
1550 #[inline(always)]
1551 fn zernike<P: Policy, const NORM: u8>(rho: Self, theta: Self, n: u32, m: i32) -> Self {
1552 const {
1553 assert!(
1554 NORM == crate::ZERNIKE_UNIT_PEAK || NORM == crate::ZERNIKE_ORTHONORMAL,
1555 "zernike: NORM must be ZERNIKE_UNIT_PEAK or ZERNIKE_ORTHONORMAL"
1556 );
1557 }
1558
1559 let am = m.unsigned_abs();
1560
1561 let radial = Self::zernike_r::<P>(rho, n, am);
1562
1563 let z = if m == 0 {
1564 radial } else {
1566 let (sin, cos) = Self::sin_cos::<P>(theta * Self::splat(E::from_int(am as thermite::LargeInt)));
1567
1568 radial * if m > 0 { cos } else { sin }
1569 };
1570
1571 if const { NORM == crate::ZERNIKE_UNIT_PEAK } {
1572 return z;
1573 }
1574
1575 let radicand = if m == 0 { n + 1 } else { 2 * (n + 1) };
1579
1580 z * Self::splat(FloatElement::sqrt(E::from_int(radicand as thermite::LargeInt)))
1581 }
1582
1583 #[inline(always)]
1584 fn zernike_basis<P: Policy, const L: usize, const NORM: u8, const N: usize>(x: Self, y: Self, out: &mut [Self; N]) {
1585 generic::zernike::zernike_basis_impl::<P, E, Self, L, NORM, N>(x, y, out);
1586 }
1587
1588 fn lambert_w<P: Policy>(self) -> (Self, Self);
1589
1590 #[inline(always)]
1594 fn phi_n<P: Policy, const N: usize>(self) -> Self {
1595 generic::phi::phi_internal_n::<Self, E, P, N, true>(self, P::POLICY.max_iterations)
1599 }
1600
1601 #[inline(always)]
1604 fn phi<P: Policy>(self, n: u32) -> Self {
1605 generic::phi::phi_internal::<Self, E, P, true>(self, n, P::POLICY.max_iterations)
1606 }
1607}
1608
1609pub use generic::elliptic::{
1616 CarlsonKind, CarlsonRc, CarlsonRd, CarlsonRf, CarlsonRg, CarlsonRj, EllintD, EllintDInc, EllintE, EllintEInc,
1617 EllintF, EllintK, EllintPi, EllintPiInc, EllipticConsts, EllipticKind, HeumanLambda, JacobiZeta, WrapTo,
1618};
1619
1620pub use generic::sh::{
1624 MAX_DEGREE as MAX_SH_DEGREE, ShConsts, ShTable, sh_d_impl, sh_eval_d_impl, sh_eval_impl, sh_eval_lifted_impl,
1625 sh_eval_mixed_impl, sh_impl, sh_table_impl,
1626};
1627
1628pub use generic::zernike::{MAX_DEGREE as MAX_ZERNIKE_DEGREE, zernike_basis_d_impl, zernike_basis_impl};
1631
1632pub use generic::zeta::{ZetaConsts, bernoulli_terms as zeta_bernoulli_terms};
1637
1638#[doc(hidden)]
1641pub use generic::polylog::{
1642 KMAX as POLYLOG_KMAX, PolylogElement, PolylogPlan, root_count as polylog_root_count, t1 as polylog_t1,
1643};
1644
1645pub use generic::jacobi_elliptic::{NMAX as JACOBI_NMAX, jacobi_elliptic as jacobi_elliptic_impl};
1650
1651pub use generic::fresnel::phase_half_x2 as fresnel_phase;
1656
1657pub trait SpecializedRealSpecialMath<E>: SpecializedSpecialMath<E> {
1663 fn erfinv<P: Policy>(self) -> Self;
1664 fn probit<P: Policy>(self) -> Self;
1665
1666 #[inline(always)]
1668 fn ndtr<P: Policy>(self) -> Self {
1669 generic::ndtr::ndtr_impl::<P, _, _>(self)
1670 }
1671
1672 #[inline(always)]
1677 fn log_ndtr<P: Policy>(self) -> Self {
1678 generic::ndtr::log_ndtr_impl::<P, _, _>(self)
1679 }
1680
1681 #[inline(always)]
1684 fn logerfc<P: Policy>(self) -> Self {
1685 generic::ndtr::logerfc_impl::<P, _, _>(self)
1686 }
1687
1688 #[inline(always)]
1691 fn log_ndtr_with_deriv<P: Policy>(self) -> (Self, Self) {
1692 generic::ndtr::log_ndtr_with_deriv_impl::<P, _, _, true>(self)
1693 }
1694
1695 #[inline(always)]
1697 fn inv_log_ndtr<P: Policy>(self) -> Self {
1698 generic::ndtr::inv_log_ndtr_impl::<P, _, _>(self)
1699 }
1700
1701 #[inline(always)]
1704 fn inv_digamma<P: Policy>(self) -> Self {
1705 generic::inverses::inv_digamma_impl::<P, _, _>(self)
1706 }
1707
1708 #[inline(always)]
1710 fn wright_omega<P: Policy>(self) -> Self {
1711 generic::inverses::wright_omega_impl::<P, _, _>(self)
1712 }
1713
1714 #[inline(always)]
1720 fn fresnel<P: Policy>(self) -> (Self, Self) {
1721 todo!("fresnel is not implemented for this composite type")
1722 }
1723
1724 #[inline(always)]
1729 fn sici<P: Policy>(self) -> (Self, Self) {
1730 todo!("sici is not implemented for this composite type")
1731 }
1732
1733 #[inline(always)]
1738 fn fresnel_c<P: Policy>(self) -> Self {
1739 Self::fresnel::<P>(self).1
1740 }
1741
1742 #[inline(always)]
1744 fn fresnel_s<P: Policy>(self) -> Self {
1745 Self::fresnel::<P>(self).0
1746 }
1747
1748 #[inline(always)]
1750 fn sinint<P: Policy>(self) -> Self {
1751 Self::sici::<P>(self).0
1752 }
1753
1754 #[inline(always)]
1756 fn cosint<P: Policy>(self) -> Self {
1757 Self::sici::<P>(self).1
1758 }
1759
1760 #[inline(always)]
1766 fn bessel_i_ratio<P: Policy>(self, _nu: Self) -> Self {
1767 todo!("bessel_i_ratio is not implemented for this composite type")
1768 }
1769
1770 #[inline(always)]
1773 fn inv_bessel_i_ratio<P: Policy>(self, _nu: Self) -> Self {
1774 todo!("inv_bessel_i_ratio is not implemented for this composite type")
1775 }
1776
1777 #[inline(always)]
1779 fn bessel_i_ratio_1m<P: Policy>(self, _nu: Self) -> Self {
1780 todo!("bessel_i_ratio_1m is not implemented for this composite type")
1781 }
1782
1783 #[inline(always)]
1785 fn inv_bessel_i_ratio_1m<P: Policy>(self, _nu: Self) -> Self {
1786 todo!("inv_bessel_i_ratio_1m is not implemented for this composite type")
1787 }
1788
1789 #[inline(always)]
1797 fn bessel_ratio<P: Policy, F: crate::bessel::BesselRatioFamily>(self, nu: Self) -> Self {
1798 F::ratio::<P, E, Self>(self, nu)
1799 }
1800
1801 #[inline(always)]
1803 fn inv_bessel_ratio<P: Policy, F: crate::bessel::BesselRatioFamily>(self, nu: Self) -> Self {
1804 F::inv_ratio::<P, E, Self>(self, nu)
1805 }
1806
1807 #[inline(always)]
1809 fn bessel_ratio_1m<P: Policy, F: crate::bessel::BesselRatioFamily>(self, nu: Self) -> Self {
1810 F::ratio_1m::<P, E, Self>(self, nu)
1811 }
1812
1813 #[inline(always)]
1815 fn inv_bessel_ratio_1m<P: Policy, F: crate::bessel::BesselRatioFamily>(self, nu: Self) -> Self {
1816 F::inv_ratio_1m::<P, E, Self>(self, nu)
1817 }
1818
1819 #[inline(always)]
1822 fn gauss_legendre<P: Policy>(self, n: u32) -> (Self, Self) {
1823 generic::quadrature::gauss_legendre_impl::<P, _, _>(self, n)
1824 }
1825
1826 #[inline(always)]
1828 fn gauss_hermite<P: Policy>(self, n: u32) -> (Self, Self) {
1829 generic::quadrature::gauss_hermite_impl::<P, _, _>(self, n)
1830 }
1831
1832 #[inline(always)]
1835 fn gauss_laguerre<P: Policy>(self, alpha: Self, n: u32) -> (Self, Self) {
1836 generic::quadrature::gauss_laguerre_impl::<P, _, _>(self, alpha, n)
1837 }
1838
1839 #[inline(always)]
1841 fn agm<P: Policy>(a: Self, b: Self) -> Self {
1842 generic::elliptic::agm::<P, _, _>(a, b)
1843 }
1844
1845 #[inline(always)]
1857 fn pochhammer<P: Policy>(z: Self, m: Self) -> Self {
1858 generic::pochhammer::pochhammer::<P, _, Self>(z, m)
1859 }
1860
1861 #[inline(always)]
1867 fn jacobi_elliptic<P: Policy>(u: Self, k: Self) -> (Self, Self, Self) {
1868 generic::jacobi_elliptic::jacobi_elliptic::<P, _, _>(u, k)
1869 }
1870
1871 #[inline(always)]
1886 fn boxcox<P: Policy>(self, lambda: Self) -> Self {
1887 let at_zero = lambda.is_zero();
1888
1889 if const { !P::POLICY.avoid_branching } && at_zero.all() {
1890 Self::ln::<P>(self)
1891 } else if const { !P::POLICY.avoid_branching } && at_zero.none() {
1892 Self::powf_m1::<P>(self, lambda) / lambda
1893 } else {
1894 at_zero.select(Self::ln::<P>(self), Self::powf_m1::<P>(self, lambda) / lambda)
1895 }
1896 }
1897
1898 #[inline(always)]
1904 fn boxcox_1p<P: Policy>(self, lambda: Self) -> Self {
1905 let at_zero = lambda.is_zero();
1906
1907 if const { !P::POLICY.avoid_branching } && at_zero.all() {
1908 Self::ln_1p::<P>(self)
1909 } else if const { !P::POLICY.avoid_branching } && at_zero.none() {
1910 Self::compound_m1::<P>(self, lambda) / lambda
1911 } else {
1912 at_zero.select(Self::ln_1p::<P>(self), Self::compound_m1::<P>(self, lambda) / lambda)
1913 }
1914 }
1915
1916 #[inline(always)]
1924 fn inv_boxcox<P: Policy>(self, lambda: Self) -> Self {
1925 let at_zero = lambda.is_zero();
1926
1927 if const { !P::POLICY.avoid_branching } && at_zero.all() {
1928 Self::exp::<P>(self)
1929 } else if const { !P::POLICY.avoid_branching } && at_zero.none() {
1930 Self::exp::<P>(Self::ln_1p::<P>(lambda * self) / lambda)
1931 } else {
1932 at_zero.select(
1933 Self::exp::<P>(self),
1934 Self::exp::<P>(Self::ln_1p::<P>(lambda * self) / lambda),
1935 )
1936 }
1937 }
1938
1939 #[inline(always)]
1946 fn inv_boxcox_1p<P: Policy>(self, lambda: Self) -> Self {
1947 let at_zero = lambda.is_zero();
1948
1949 if const { !P::POLICY.avoid_branching } && at_zero.all() {
1950 Self::exp_m1::<P>(self)
1951 } else if const { !P::POLICY.avoid_branching } && at_zero.none() {
1952 Self::exp_m1::<P>(Self::ln_1p::<P>(lambda * self) / lambda)
1953 } else {
1954 at_zero.select(
1955 Self::exp_m1::<P>(self),
1956 Self::exp_m1::<P>(Self::ln_1p::<P>(lambda * self) / lambda),
1957 )
1958 }
1959 }
1960
1961 #[inline(always)]
1969 fn yeo_johnson<P: Policy>(self, lambda: Self) -> Self {
1970 let neg = self.cmp_lt(Self::ZERO);
1971 let reflected = neg.select(Self::TWO - lambda, lambda);
1972 let r = Self::boxcox_1p::<P>(self.abs(), reflected);
1973
1974 r.neg_c(neg)
1975 }
1976
1977 #[inline(always)]
1983 fn inv_yeo_johnson<P: Policy>(self, lambda: Self) -> Self {
1984 let neg = self.cmp_lt(Self::ZERO);
1985 let reflected = neg.select(Self::TWO - lambda, lambda);
1986 let r = Self::inv_boxcox_1p::<P>(self.abs(), reflected);
1987
1988 r.neg_c(neg)
1989 }
1990
1991 fn langevin<P: Policy>(self) -> Self;
1992 fn inv_langevin<P: Policy>(self) -> Self;
1993 fn langevin_1m<P: Policy>(self) -> Self;
1994 fn inv_langevin_1m<P: Policy>(self) -> Self;
1995
1996 #[inline(always)]
1997 fn gelu<P: Policy>(self, alpha: Self) -> Self {
1998 let c = (-(alpha * self)).scale(FloatConsts::FRAC_1_SQRT_2).erfc_p::<P>();
2011
2012 self.scale(E::ConstRatio::<1, 2>::VALUE) * c
2013 }
2014
2015 #[inline(always)]
2016 fn swish<P: Policy>(self, beta: Self) -> Self {
2017 let x = self;
2018 let beta_x = beta * x;
2019
2020 let e = (-beta_x).exp_p::<P>();
2022 let s = (Self::ONE + e).approx_reciprocal_p::<P>();
2023
2024 x * s
2025 }
2026
2027 fn lgamma_r<P: Policy>(self) -> (Self, Self);
2028
2029 #[inline(always)]
2030 fn algebraic_sigmoid_n<P: Policy, const N: usize>(self) -> Self {
2031 if const { N == 0 } {
2032 return self; }
2034
2035 let pre_root = Self::ONE + self.abs().powi_p::<P>(N as i32); let denom = match N {
2038 1 => pre_root,
2039 2 => pre_root.sqrt(),
2040 3 => pre_root.cbrt_p::<P>(),
2041 4 if const { P::POLICY.precision.le(PrecisionPolicy::Average) } => pre_root.sqrt().sqrt(),
2042 _ => {
2043 let x = pre_root;
2045
2046 let mut y = x.powf_p::<CheckOverflow<LessPrecision<P>, false>>(Self::splat(
2048 E::ONE / E::from_int(N as thermite::LargeInt),
2049 ));
2050
2051 let y_n = y.powi_p::<P>(N as i32);
2053
2054 let np1 = Self::splat(E::from_int((N + 1) as thermite::LargeInt));
2055 let nm1 = Self::splat(E::from_int((N - 1) as thermite::LargeInt));
2056
2057 let n = y * (x - y_n); let d = y_n.mul_adde(np1, x * nm1);
2059
2060 y += (n + n) / d;
2061
2062 y
2063 }
2064 };
2065
2066 let mut y = if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
2069 self * denom.approx_reciprocal_p::<P>()
2072 } else {
2073 self / denom
2074 };
2075
2076 if const { P::POLICY.check_overflow } {
2077 y = pre_root.is_infinite().select(self.signum(), y);
2078 }
2079
2080 y
2081 }
2082
2083 #[inline(always)]
2085 fn algebraic_sigmoid<P: Policy>(self, n: u32) -> Self {
2086 if n == 0 {
2087 return self;
2088 }
2089
2090 let pre_root = Self::ONE + self.abs().powi_p::<P>(n as i32);
2091
2092 let denom = match n {
2093 1 => pre_root,
2094 2 => pre_root.sqrt(),
2095 3 => pre_root.cbrt_p::<P>(),
2096 4 if const { P::POLICY.precision.le(PrecisionPolicy::Average) } => pre_root.sqrt().sqrt(),
2097 _ => {
2098 let x = pre_root;
2099
2100 let mut y = x.powf_p::<CheckOverflow<LessPrecision<P>, false>>(Self::splat(
2101 E::ONE / E::from_int(n as thermite::LargeInt),
2102 ));
2103
2104 let y_n = y.powi_p::<P>(n as i32);
2105
2106 let np1 = Self::splat(E::from_int((n + 1) as thermite::LargeInt));
2107 let nm1 = Self::splat(E::from_int((n - 1) as thermite::LargeInt));
2108
2109 let num = y * (x - y_n);
2110 let d = y_n.mul_adde(np1, x * nm1);
2111
2112 y += (num + num) / d;
2113
2114 y
2115 }
2116 };
2117
2118 let mut y = if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
2119 self * denom.approx_reciprocal_p::<P>()
2120 } else {
2121 self / denom
2122 };
2123
2124 if const { P::POLICY.check_overflow } {
2125 y = pre_root.is_infinite().select(self.signum(), y);
2126 }
2127
2128 y
2129 }
2130
2131 #[inline(always)]
2138 fn algebraic_swish<P: Policy>(self) -> Self {
2139 let x = self;
2140
2141 if const { matches!(Self::HAS_NATIVE_FMA, thermite::tribool::True) } {
2142 if const { Self::HAS_APPROX_RSQRT } {
2150 let a = x.mul_add(x, Self::ONE);
2151 let y0 = a.rsqrt();
2152 let ay2 = a * y0 * y0;
2153 let ch = ay2.nmul_add(Self::HALF, Self::splat(<E as FloatElement>::ConstRatio::<3, 2>::VALUE));
2154 let r_inv = y0 * ch; let q = x * r_inv;
2156 let xh = Self::HALF * x;
2157 q.mul_add(xh, xh)
2158 } else {
2159 let a = x.mul_add(x, Self::ONE);
2160 let q = x / a.sqrt();
2161 let xh = x * Self::HALF;
2162 q.mul_add(xh, xh)
2163 }
2164 } else if const { Self::HAS_APPROX_RCP } {
2166 let a = x * x + Self::ONE;
2167 let y0 = a.rsqrt();
2168 let ay2 = a * y0 * y0;
2169 let c = Self::splat(<E as FloatElement>::ConstInt::<3>::VALUE) - ay2;
2170 let r_inv_2 = y0 * c; let hxy1 = Self::splat(<E as FloatElement>::ConstRatio::<1, 4>::VALUE) * (x * r_inv_2); let w = Self::HALF + hxy1; x * w
2174 } else {
2175 let a = x * x + Self::ONE;
2176 let q = x / a.sqrt();
2177 let q1 = q + Self::ONE;
2178 x * Self::HALF * q1
2179 }
2180 }
2181
2182 #[inline(always)]
2183 fn gaussian_integral<P: Policy>(x0: Self, x1: Self, a: Self, c: Self) -> Self {
2184 let common = Self::SQRT_FRAC_PI_2 * a * c;
2186 let denom = Self::SQRT_2 * c;
2187
2188 let (a1, a0) = if const { P::POLICY.precision.le(PrecisionPolicy::Medium) } {
2189 let d = denom.approx_reciprocal_p::<P>();
2190 (x1 * d, x0 * d)
2191 } else {
2192 (x1 / denom, x0 / denom)
2193 };
2194
2195 common * (a1.erf_p::<P>() - a0.erf_p::<P>())
2196 }
2197
2198 #[inline(always)]
2211 fn spherical_harmonics_table<P: Policy, const L: usize, const N: usize, const CS: bool>(
2212 table: &mut ShTable<Self::Primal, N>,
2213 ) {
2214 generic::sh::sh_table_impl::<Self::Primal, L, N, CS>(table);
2215 }
2216
2217 #[inline(always)]
2225 fn spherical_harmonics_with<P: Policy, const L: usize, const N: usize>(
2226 table: &ShTable<Self::Primal, N>,
2227 x: Self,
2228 y: Self,
2229 z: Self,
2230 out: &mut [Self; N],
2231 ) {
2232 generic::sh::sh_eval_lifted_impl::<Self, L, N>(table, x, y, z, out);
2233 }
2234
2235 #[inline(always)]
2247 fn spherical_harmonics<P: Policy, const L: usize, const N: usize, const CS: bool>(
2248 x: Self,
2249 y: Self,
2250 z: Self,
2251 out: &mut [Self; N],
2252 ) {
2253 let mut table = ShTable::<Self::Primal, N>::zeroed();
2254 Self::spherical_harmonics_table::<P, L, N, CS>(&mut table);
2255 Self::spherical_harmonics_with::<P, L, N>(&table, x, y, z, out);
2256 }
2257}
2258
2259pub trait SpecializedRealPrimalMath<E>: SpecializedRealSpecialMath<E> + PrimalProjection<Primal = Self> {
2266 #[inline(always)]
2269 #[allow(clippy::too_many_arguments)]
2270 fn spherical_harmonics_d_with<P: Policy, const L: usize, const N: usize>(
2271 table: &ShTable<Self, N>,
2272 x: Self,
2273 y: Self,
2274 z: Self,
2275 out: &mut [Self; N],
2276 ddx: &mut [Self; N],
2277 ddy: &mut [Self; N],
2278 ddz: &mut [Self; N],
2279 ) {
2280 generic::sh::sh_eval_d_impl::<Self, L, N>(table, x, y, z, out, ddx, ddy, ddz);
2281 }
2282
2283 #[inline(always)]
2286 #[allow(clippy::too_many_arguments)]
2287 fn spherical_harmonics_d<P: Policy, const L: usize, const N: usize, const CS: bool>(
2288 x: Self,
2289 y: Self,
2290 z: Self,
2291 out: &mut [Self; N],
2292 ddx: &mut [Self; N],
2293 ddy: &mut [Self; N],
2294 ddz: &mut [Self; N],
2295 ) {
2296 let mut table = ShTable::<Self, N>::zeroed();
2297 Self::spherical_harmonics_table::<P, L, N, CS>(&mut table);
2298 Self::spherical_harmonics_d_with::<P, L, N>(&table, x, y, z, out, ddx, ddy, ddz);
2299 }
2300
2301 #[inline(always)]
2304 fn zernike_basis_d<P: Policy, const L: usize, const NORM: u8, const N: usize>(
2305 x: Self,
2306 y: Self,
2307 out: &mut [Self; N],
2308 ddx: &mut [Self; N],
2309 ddy: &mut [Self; N],
2310 ) {
2311 generic::zernike::zernike_basis_d_impl::<P, E, Self, L, NORM, N>(x, y, out, ddx, ddy);
2312 }
2313
2314 #[inline(always)]
2315 fn softplus_d<P: Policy>(self, k: Self, rcp_k: Self) -> (Self, Self) {
2316 if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
2317 let k = k.scale(FloatConsts::LOG2_E);
2318 let rcp_k = rcp_k.scale(FloatConsts::LN_2);
2319
2320 let kx = self * k;
2321
2322 let e = kx.abs().neg().exp2_p::<CheckOverflow<P, true>>();
2323 let y = (Self::ONE + e).log2_p::<P>().mul_adde(rcp_k, self.max(Self::ZERO));
2324
2325 let rcp = (Self::ONE + e).approx_reciprocal_p::<P>();
2326 let dy = kx.select_negative(e * rcp, rcp);
2327
2328 return (y, dy);
2329 }
2330
2331 let kx = self * k;
2332
2333 let e = kx.abs().neg().exp_p::<P>();
2334
2335 let y = e.ln_1p_p::<P>().mul_adde(rcp_k, self.max(Self::ZERO));
2337
2338 let rcp = (e + Self::ONE).approx_reciprocal_p::<P>();
2340 let dy = kx.select_negative(e * rcp, rcp);
2341
2342 (y, dy)
2343 }
2344
2345 #[inline(always)]
2346 fn gelu_d<P: Policy>(self, alpha: Self) -> (Self, Self) {
2347 let alpha_x = alpha * self;
2348
2349 let c = (-alpha_x).scale(FloatConsts::FRAC_1_SQRT_2).erfc_p::<P>();
2351 let y = self.scale(E::ConstRatio::<1, 2>::VALUE) * c;
2352
2353 let dy = (alpha_x * alpha_x)
2354 .scale(E::ConstRatio::<{ -1 }, 2>::VALUE)
2355 .exp_p::<P>()
2356 .scale(FloatConsts::FRAC_1_SQRT_TAU);
2357
2358 (y, dy.mul_adde(alpha_x, y))
2359 }
2360
2361 #[inline(always)]
2362 fn swish_d<P: Policy>(self, beta: Self) -> (Self, Self) {
2363 let x = self;
2364 let beta_x = beta * x;
2365
2366 let e = (-beta_x).exp_p::<P>();
2367 let s = (Self::ONE + e).approx_reciprocal_p::<P>();
2368
2369 let y = x * s;
2370
2371 let dy = (beta * y).mul_adde(e * s, s);
2373
2374 (y, dy)
2375 }
2376
2377 #[inline(always)]
2378 fn algebraic_sigmoid_d_n<P: Policy, const N: usize>(self) -> (Self, Self) {
2379 if const { N == 0 } {
2380 return (self, Self::ONE); }
2382
2383 let pre_root = Self::ONE + self.abs().powi_p::<P>(N as i32); let denom = match N {
2386 1 => pre_root,
2387 2 => pre_root.sqrt(),
2388 3 => pre_root.cbrt_p::<P>(),
2389 4 if const { P::POLICY.precision.le(PrecisionPolicy::Average) } => pre_root.sqrt().sqrt(),
2390 _ => {
2391 let x = pre_root;
2392
2393 let mut y = x.powf_p::<CheckOverflow<LessPrecision<P>, false>>(Self::splat(
2394 E::ONE / E::from_int(N as thermite::LargeInt),
2395 ));
2396
2397 let y_n = y.powi_p::<P>(N as i32);
2398
2399 let np1 = Self::splat(E::from_int((N + 1) as thermite::LargeInt));
2400 let nm1 = Self::splat(E::from_int((N - 1) as thermite::LargeInt));
2401
2402 let n = y * (x - y_n); let d = y_n.mul_adde(np1, x * nm1);
2404
2405 y += (n + n) / d;
2406
2407 y
2408 }
2409 };
2410
2411 let mut y;
2413 let mut dy;
2414
2415 if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
2416 let inv_denom = denom.approx_reciprocal_p::<P>();
2417 y = self * inv_denom;
2418 dy = inv_denom / pre_root;
2419 } else {
2420 y = self / denom;
2421 dy = (pre_root * denom).approx_reciprocal_p::<P>();
2422 }
2423
2424 if const { P::POLICY.check_overflow } {
2425 let is_infinite = pre_root.is_infinite();
2426
2427 y = is_infinite.select(self.signum(), y);
2428 dy = dy.nz(is_infinite); }
2430
2431 (y, dy)
2432 }
2433
2434 #[inline(always)]
2436 fn algebraic_sigmoid_d<P: Policy>(self, n: u32) -> (Self, Self) {
2437 if n == 0 {
2438 return (self, Self::ONE);
2439 }
2440
2441 let pre_root = Self::ONE + self.abs().powi_p::<P>(n as i32);
2442
2443 let denom = match n {
2444 1 => pre_root,
2445 2 => pre_root.sqrt(),
2446 3 => pre_root.cbrt_p::<P>(),
2447 4 if const { P::POLICY.precision.le(PrecisionPolicy::Average) } => pre_root.sqrt().sqrt(),
2448 _ => {
2449 let x = pre_root;
2450
2451 let mut y = x.powf_p::<CheckOverflow<LessPrecision<P>, false>>(Self::splat(
2452 E::ONE / E::from_int(n as thermite::LargeInt),
2453 ));
2454
2455 let y_n = y.powi_p::<P>(n as i32);
2456
2457 let np1 = Self::splat(E::from_int((n + 1) as thermite::LargeInt));
2458 let nm1 = Self::splat(E::from_int((n - 1) as thermite::LargeInt));
2459
2460 let num = y * (x - y_n);
2461 let d = y_n.mul_adde(np1, x * nm1);
2462
2463 y += (num + num) / d;
2464
2465 y
2466 }
2467 };
2468
2469 let mut y;
2470 let mut dy;
2471
2472 if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
2473 let inv_denom = denom.approx_reciprocal_p::<P>();
2474 y = self * inv_denom;
2475 dy = inv_denom / pre_root;
2476 } else {
2477 y = self / denom;
2478 dy = (pre_root * denom).approx_reciprocal_p::<P>();
2479 }
2480
2481 if const { P::POLICY.check_overflow } {
2482 let is_infinite = pre_root.is_infinite();
2483
2484 y = is_infinite.select(self.signum(), y);
2485 dy = dy.nz(is_infinite);
2486 }
2487
2488 (y, dy)
2489 }
2490
2491 fn langevin_d<P: Policy>(self) -> (Self, Self);
2495
2496 #[inline(always)]
2497 fn algebraic_swish_d<P: Policy>(self) -> (Self, Self) {
2498 let x = self;
2499
2500 if const { matches!(Self::HAS_NATIVE_FMA, thermite::tribool::True) } {
2501 if const { Self::HAS_APPROX_RSQRT } {
2507 let a = x.mul_add(x, Self::ONE);
2508 let y0 = a.rsqrt();
2509 let ay2 = a * y0 * y0;
2510 let ch = ay2.nmul_add(Self::HALF, Self::splat(<E as FloatElement>::ConstRatio::<3, 2>::VALUE));
2511 let r_inv = y0 * ch; let q = x * r_inv;
2513 let xh = Self::HALF * x;
2514 let y = q.mul_add(xh, xh);
2515
2516 let inv_a = r_inv * r_inv;
2517 let qa = q.mul_add(inv_a, q); let dy = qa.mul_add(Self::HALF, Self::HALF); (y, dy)
2521 } else {
2522 let a = x.mul_add(x, Self::ONE);
2523 let q = x / a.sqrt();
2524 let xh = x * Self::HALF;
2525 let y = q.mul_add(xh, xh);
2526
2527 let inv_a = a.approx_reciprocal_p::<P>();
2528 let qa = q.mul_add(inv_a, q);
2529 let dy = qa.mul_add(Self::HALF, Self::HALF);
2530
2531 (y, dy)
2532 }
2533 } else if const { Self::HAS_APPROX_RCP } {
2535 let a = x * x + Self::ONE;
2536 let y0 = a.rsqrt();
2537 let ay2 = a * y0 * y0;
2538 let c = Self::splat(<E as FloatElement>::ConstInt::<3>::VALUE) - ay2;
2539 let r_inv_2 = y0 * c; let hxy1 = Self::splat(<E as FloatElement>::ConstRatio::<1, 4>::VALUE) * (x * r_inv_2); let w = Self::HALF + hxy1; let y = x * w;
2543
2544 let inv_a = Self::splat(<E as FloatElement>::ConstRatio::<1, 4>::VALUE) * (r_inv_2 * r_inv_2);
2545 let dy = w + hxy1 * inv_a;
2546
2547 (y, dy)
2548 } else {
2549 let a = x * x + Self::ONE;
2550 let q = x / a.sqrt();
2551 let q1 = q + Self::ONE;
2552 let y = x * Self::HALF * q1;
2553
2554 let dy = Self::HALF * (q1 + q / a);
2555
2556 (y, dy)
2557 }
2558 }
2559}