1use thermite::{
2 element::FloatElementWithBits,
3 mask::GenericMask,
4 math::{
5 CoreMathWithPolicy as _, FloatConsts, TranscendentalMathWithPolicy as _,
6 policy::{Policy, PrecisionPolicy},
7 },
8 vector::FloatVectorWithBits,
9};
10
11const AMP_CAP: f64 = 64.0;
18
19const fn recurrence_threshold(n: usize) -> f64 {
40 if n <= 1 {
41 return f64::MAX;
42 }
43
44 const fn const_powi_f64(mut x: f64, mut n: u32) -> f64 {
46 let mut r = 1.0f64;
47 while n > 0 {
48 if n & 1 == 1 {
49 r *= x;
50 }
51 n >>= 1;
52 if n > 0 {
53 x *= x;
54 }
55 }
56 r
57 }
58
59 let k = (n - 1) as u32;
60
61 let target: f64 = {
63 let mut f = AMP_CAP;
64 let mut i = 2usize;
65 while i < n {
66 f *= i as f64;
67 i += 1;
68 }
69 f
70 };
71
72 let mut lo = 0.0f64;
76 let mut hi = if k == 1 { target } else { AMP_CAP };
77
78 let mut i = 0;
79 while i < 64 {
80 let mid = (lo + hi) * 0.5;
81 if const_powi_f64(mid, k) < target {
82 lo = mid;
83 } else {
84 hi = mid;
85 }
86 i += 1;
87 }
88
89 (lo + hi) * 0.5
93}
94
95const CF_MAX_ITER: u32 = 48;
102
103pub trait ExpIntConsts<const N: usize>: FloatConsts + Sized {
106 const SMALL_N: [Self; 6];
108 const SMALL_D: [Self; 6];
109 const LARGE_N: [Self; 11];
110 const LARGE_D: [Self; 12];
111 const ASYMPTOTIC_CONST: Self;
112 const RECURRENCE_THRESHOLD: Self;
113 const ONE_OVER_N_MINUS_1: Self;
114 const FACTORS: [Self; N]; const RECIPROCALS: [Self; N]; const RECURRENCE_THRESHOLDS: [Self; EXPINT_TABLE_ORDERS];
121 const RECIPROCAL_TABLE: [Self; EXPINT_TABLE_ORDERS];
123
124 fn recurrence_threshold_dyn(n: u32) -> Self;
127}
128
129pub const EXPINT_TABLE_ORDERS: usize = 33;
132
133macro_rules! impl_expint_consts {
134 (
135 SMALL_N [ $($sn_value:literal),* $(,)? ],
136 SMALL_D [ $($sd_value:literal),* $(,)? ],
137 LARGE_N [ $($ln_value:literal),* $(,)? ],
138 LARGE_D [ $($ld_value:literal),* $(,)? ]
139 ) => {
140 impl<const N: usize> ExpIntConsts<N> for f32 {
141 const SMALL_N: [Self; 6] = [$($sn_value),*];
142 const SMALL_D: [Self; 6] = [$($sd_value),*];
143 const LARGE_N: [Self; 11] = [$($ln_value),*];
144 const LARGE_D: [Self; 12] = [$($ld_value),*];
145 const ASYMPTOTIC_CONST: Self = 0.66373538970947265625;
146 const RECURRENCE_THRESHOLD: Self = const { recurrence_threshold(N) as f32 };
147
148 const RECURRENCE_THRESHOLDS: [Self; EXPINT_TABLE_ORDERS] = {
149 let mut t = [0.0; EXPINT_TABLE_ORDERS]; let mut i = 0;
150 while i < EXPINT_TABLE_ORDERS { t[i] = recurrence_threshold(i) as f32; i += 1; }
151 t
152 };
153 const RECIPROCAL_TABLE: [Self; EXPINT_TABLE_ORDERS] = {
154 let mut r = [0.0; EXPINT_TABLE_ORDERS]; let mut i = 0;
155 while i < EXPINT_TABLE_ORDERS { r[i] = 1.0 / (2 + i) as Self; i += 1; }
156 r
157 };
158
159 #[inline(always)]
160 fn recurrence_threshold_dyn(n: u32) -> Self {
161 match <Self as ExpIntConsts<N>>::RECURRENCE_THRESHOLDS.get(n as usize) {
162 Some(&t) => t,
163 None => recurrence_threshold(n as usize) as f32,
164 }
165 }
166 const ONE_OVER_N_MINUS_1: Self = if N > 1 { 1.0 / (N as Self - 1.0) } else { Self::INFINITY };
167 const FACTORS: [Self; N] = {
168 let mut facts = [0.0; N]; let mut i = 0;
169 while i < N { facts[i] = (2 + i) as Self; i += 1; }
170 facts
171 };
172 const RECIPROCALS: [Self; N] = {
173 let mut r = Self::FACTORS; let mut i = 0;
174 while i < N { r[i] = 1.0 / r[i]; i += 1; }
175 r
176 };
177 }
178
179 impl<const N: usize> ExpIntConsts<N> for f64 {
180 const SMALL_N: [Self; 6] = [$($sn_value),*];
181 const SMALL_D: [Self; 6] = [$($sd_value),*];
182 const LARGE_N: [Self; 11] = [$($ln_value),*];
183 const LARGE_D: [Self; 12] = [$($ld_value),*];
184 const ASYMPTOTIC_CONST: Self = 0.66373538970947265625;
185 const RECURRENCE_THRESHOLD: Self = const { recurrence_threshold(N) };
186
187 const RECURRENCE_THRESHOLDS: [Self; EXPINT_TABLE_ORDERS] = {
188 let mut t = [0.0; EXPINT_TABLE_ORDERS]; let mut i = 0;
189 while i < EXPINT_TABLE_ORDERS { t[i] = recurrence_threshold(i); i += 1; }
190 t
191 };
192 const RECIPROCAL_TABLE: [Self; EXPINT_TABLE_ORDERS] = {
193 let mut r = [0.0; EXPINT_TABLE_ORDERS]; let mut i = 0;
194 while i < EXPINT_TABLE_ORDERS { r[i] = 1.0 / (2 + i) as Self; i += 1; }
195 r
196 };
197
198 #[inline(always)]
199 fn recurrence_threshold_dyn(n: u32) -> Self {
200 match <Self as ExpIntConsts<N>>::RECURRENCE_THRESHOLDS.get(n as usize) {
201 Some(&t) => t,
202 None => recurrence_threshold(n as usize),
203 }
204 }
205 const ONE_OVER_N_MINUS_1: Self = if N > 1 { 1.0 / (N as Self - 1.0) } else { Self::INFINITY };
206 const FACTORS: [Self; N] = {
207 let mut facts = [0.0; N]; let mut i = 0;
208 while i < N { facts[i] = (2 + i) as Self; i += 1; }
209 facts
210 };
211 const RECIPROCALS: [Self; N] = {
212 let mut r = Self::FACTORS; let mut i = 0;
213 while i < N { r[i] = 1.0 / r[i]; i += 1; }
214 r
215 };
216 }
217 };
218}
219
220impl_expint_consts! {
221 SMALL_N [
222 -0.000111507792921197858394,
223 -0.00399167106081113256961,
224 -0.0368031736257943745142,
225 -0.245088216639761496153,
226 0.0320913665303559189999,
227 0.0865197248079397976498,
228 ],
229 SMALL_D [
230 -0.528611029520217142048e-6,
231 0.000131049900798434683324,
232 0.00427347600017103698101,
233 0.056770677104207528384,
234 0.37091387659397013215,
235 1.0,
236 ],
237 LARGE_N [
238 -1185.45720315201027667,
239 -14751.4895786128450662,
240 -54844.4587226402067411,
241 -86273.1567711649528784,
242 -66598.2652345418633509,
243 -27182.6254466733970467,
244 -6046.8250112711035463,
245 -724.581482791462469795,
246 -43.3058660811817946037,
247 -0.999999999999998811143,
248 -0.121013190657725568138e-18,
249 ],
250 LARGE_D [
251 -0.776491285282330997549,
252 1229.20784182403048905,
253 18455.4124737722049515,
254 86722.3403467334749201,
255 180329.498380501819718,
256 192104.047790227984431,
257 113057.05869159631492,
258 38129.5594484818471461,
259 7417.37624454689546708,
260 809.193214954550328455,
261 45.3058660811801465927,
262 1.0,
263 ]
264}
265
266#[inline(always)]
315fn expint_fraction_n<P, E, V, const N: usize>(x: V, exp_neg_x: V) -> V
316where
317 P: Policy,
318 E: FloatElementWithBits + ExpIntConsts<N>,
319 V: FloatVectorWithBits<Element = E> + crate::specialized::SpecializedSpecialMath<E>,
320{
321 let tol = if const { P::POLICY.precision.le(PrecisionPolicy::Worst) } {
327 <V as FloatConsts>::SQRT_EPSILON
329 } else if const { P::POLICY.precision.le(PrecisionPolicy::Medium) } {
330 <V as FloatConsts>::SQRT_EPSILON * <V as FloatConsts>::FOURTH_ROOT_EPSILON
334 } else {
335 <V as FloatConsts>::EPSILON
336 };
337
338 let tiny = V::MIN_POSITIVE;
339 let two = V::TWO;
340 let n_large = const { N as thermite::LargeInt };
341
342 let mut b = x + V::splat(E::from_int(n_large));
345 let mut c = V::MAX;
346 let mut d = V::ONE / b;
347 let mut h = d;
348
349 let mut active = <V::Mask as GenericMask>::TRUTHY;
350 let mut i = 1u32;
351
352 while i <= CF_MAX_ITER {
353 V::_loop_hint();
354
355 let a = V::splat(E::from_int(
357 -(i as thermite::LargeInt) * (n_large - 1 + i as thermite::LargeInt),
358 ));
359 b += two;
360
361 let den = a.mul_adde(d, b);
362 d = V::ONE / den.is_zero().select(tiny, den);
363
364 let num = b + a / c;
365 c = num.is_zero().select(tiny, num);
366
367 let delta = c * d;
368
369 h = active.select(h * delta, h);
372
373 active &= (delta - V::ONE).abs().cmp_gt(tol);
376
377 if i.is_multiple_of(4) && active.none() {
378 break;
379 }
380
381 i += 1;
382 }
383
384 h * exp_neg_x
385}
386
387#[inline(always)]
388pub fn expint_double_n<P: Policy, E, V, const N: usize>(x: V) -> V
390where
391 E: FloatElementWithBits + ExpIntConsts<N>,
392 V: FloatVectorWithBits<Element = E> + crate::specialized::SpecializedSpecialMath<E>,
393{
394 expint_double_primal_n::<P, E, V, N>(x).0
395}
396
397#[inline(always)]
406fn expint_fraction<P, E, V>(x: V, exp_neg_x: V, n: u32) -> V
407where
408 P: Policy,
409 E: FloatElementWithBits,
410 V: FloatVectorWithBits<Element = E> + crate::specialized::SpecializedSpecialMath<E>,
411{
412 let tol = if const { P::POLICY.precision.le(PrecisionPolicy::Worst) } {
413 <V as FloatConsts>::SQRT_EPSILON
414 } else if const { P::POLICY.precision.le(PrecisionPolicy::Medium) } {
415 <V as FloatConsts>::SQRT_EPSILON * <V as FloatConsts>::FOURTH_ROOT_EPSILON
416 } else {
417 <V as FloatConsts>::EPSILON
418 };
419
420 let tiny = V::MIN_POSITIVE;
421 let two = V::TWO;
422 let n_large = n as thermite::LargeInt;
423
424 let mut b = x + V::splat(E::from_int(n_large));
425 let mut c = V::MAX;
426 let mut d = V::ONE / b;
427 let mut h = d;
428
429 let mut active = <V::Mask as GenericMask>::TRUTHY;
430 let mut i = 1u32;
431
432 while i <= CF_MAX_ITER {
433 V::_loop_hint();
434
435 let a = V::splat(E::from_int(
436 -(i as thermite::LargeInt) * (n_large - 1 + i as thermite::LargeInt),
437 ));
438 b += two;
439
440 let den = a.mul_adde(d, b);
441 d = V::ONE / den.is_zero().select(tiny, den);
442
443 let num = b + a / c;
444 c = num.is_zero().select(tiny, num);
445
446 let delta = c * d;
447
448 h = active.select(h * delta, h);
449
450 active &= (delta - V::ONE).abs().cmp_gt(tol);
451
452 if i.is_multiple_of(4) && active.none() {
453 break;
454 }
455
456 i += 1;
457 }
458
459 h * exp_neg_x
460}
461
462#[inline(always)]
464pub fn expint_double_primal<P: Policy, E, V>(x: V, n: u32) -> (V, V)
465where
466 E: FloatElementWithBits + ExpIntConsts<1>,
467 V: FloatVectorWithBits<Element = E> + crate::specialized::SpecializedSpecialMath<E>,
468{
469 let exp_neg_x = (-x).exp_p::<P>();
470 let x_ex = exp_neg_x / x;
471
472 if n == 0 {
473 let mut result = x_ex;
474 let mut prev = x_ex * (V::ONE + x.approx_reciprocal_p::<P>());
475
476 if const { P::POLICY.check_overflow } {
477 let x_is_zero = x.is_zero();
478 result = x_is_zero.select(V::INFINITY, result);
479 prev = x_is_zero.select(V::INFINITY, prev);
480
481 let bad = x.cmp_lt(V::ZERO) | x.is_nan();
482 result = bad.select(V::NAN, result);
483 prev = bad.select(V::NAN, prev);
484 }
485
486 return (result, prev);
487 }
488
489 let is_large = x.cmp_gt(V::ONE);
490
491 let inv_x = x.approx_reciprocal_p::<P>();
492
493 let mut e_n = x
494 .poly_rev_n_p::<P, _>(&<E as ExpIntConsts<1>>::SMALL_N)
495 .approx_div_p::<P>(x.poly_rev_n_p::<P, _>(&<E as ExpIntConsts<1>>::SMALL_D));
496
497 let large_e1 = inv_x
498 .poly_rev_n_p::<P, _>(&<E as ExpIntConsts<1>>::LARGE_N)
499 .approx_div_p::<P>(inv_x.poly_rev_n_p::<P, _>(&<E as ExpIntConsts<1>>::LARGE_D));
500
501 e_n += x - x.ln_p::<P>() - V::splat(<E as ExpIntConsts<1>>::ASYMPTOTIC_CONST);
502
503 e_n = is_large.select((V::ONE + large_e1) * x_ex, e_n);
504
505 let mut e_prev = x_ex;
507
508 if n > 1 {
509 e_prev = e_n;
510 e_n = x.nmul_adde(e_n, exp_neg_x);
511
512 let mut k = 0usize;
513 while k < n as usize - 2 {
514 e_prev = e_n;
515 let f = E::from_int((2 + k) as thermite::LargeInt);
516 e_n = if const { P::POLICY.precision.ge(PrecisionPolicy::Best) } {
517 x.nmul_adde(e_n, exp_neg_x) / V::splat(f)
518 } else {
519 let r = match <E as ExpIntConsts<1>>::RECIPROCAL_TABLE.get(k) {
522 Some(&r) => r,
523 None => E::ONE / f,
524 };
525 x.nmul_adde(e_n, exp_neg_x).scale(r)
526 };
527 k += 1;
528 }
529 }
530
531 let is_very_large = x.cmp_ge(V::splat(<E as ExpIntConsts<1>>::recurrence_threshold_dyn(n)));
532
533 if n > 1 && thermite::unlikely(is_very_large.any()) {
534 e_n = is_very_large.select(expint_fraction::<P, E, V>(x, exp_neg_x, n), e_n);
535
536 let back = (exp_neg_x - e_n.scale(E::from_int(n as thermite::LargeInt - 1))) / x;
537 e_prev = is_very_large.select(back, e_prev);
538 }
539
540 if const { P::POLICY.check_overflow } {
541 let x_is_zero = x.is_zero();
542
543 if n == 1 {
544 e_n = x_is_zero.select(V::INFINITY, e_n);
545 } else {
546 e_n = x_is_zero.select(V::splat(E::ONE / E::from_int(n as thermite::LargeInt - 1)), e_n);
547 }
548
549 if n <= 2 {
550 e_prev = x_is_zero.select(V::INFINITY, e_prev);
551 } else {
552 e_prev = x_is_zero.select(V::splat(E::ONE / E::from_int(n as thermite::LargeInt - 2)), e_prev);
553 }
554
555 let bad = x.cmp_lt(V::ZERO) | x.is_nan();
556 e_n = bad.select(V::NAN, e_n);
557 e_prev = bad.select(V::NAN, e_prev);
558 }
559
560 (e_n, e_prev)
561}
562
563#[inline(always)]
565pub fn expint_double<P: Policy, E, V>(x: V, n: u32) -> V
566where
567 E: FloatElementWithBits + ExpIntConsts<1>,
568 V: FloatVectorWithBits<Element = E> + crate::specialized::SpecializedSpecialMath<E>,
569{
570 expint_double_primal::<P, E, V>(x, n).0
571}
572
573pub fn expint_double_primal_n<P: Policy, E, V, const N: usize>(x: V) -> (V, V)
585where
586 E: FloatElementWithBits + ExpIntConsts<N>,
587 V: FloatVectorWithBits<Element = E> + crate::specialized::SpecializedSpecialMath<E>,
588{
589 let exp_neg_x = (-x).exp_p::<P>();
590 let x_ex = exp_neg_x / x;
591
592 if const { N == 0 } {
593 let mut result = x_ex;
594 let mut prev = x_ex * (V::ONE + x.approx_reciprocal_p::<P>());
596
597 if const { P::POLICY.check_overflow } {
598 let x_is_zero = x.is_zero();
599 result = x_is_zero.select(V::INFINITY, result);
600 prev = x_is_zero.select(V::INFINITY, prev);
601
602 let bad = x.cmp_lt(V::ZERO) | x.is_nan();
603 result = bad.select(V::NAN, result);
604 prev = bad.select(V::NAN, prev);
605 }
606
607 return (result, prev);
608 }
609
610 let is_large = x.cmp_gt(V::ONE);
611
612 let inv_x = x.approx_reciprocal_p::<P>();
613
614 let mut e_n = x
616 .poly_rev_n_p::<P, _>(&E::SMALL_N)
617 .approx_div_p::<P>(x.poly_rev_n_p::<P, _>(&E::SMALL_D));
618
619 let large_e1 = inv_x
621 .poly_rev_n_p::<P, _>(&E::LARGE_N)
622 .approx_div_p::<P>(inv_x.poly_rev_n_p::<P, _>(&E::LARGE_D));
623
624 e_n += x - x.ln_p::<P>() - V::splat(E::ASYMPTOTIC_CONST);
626
627 e_n = is_large.select((V::ONE + large_e1) * x_ex, e_n);
628
629 let mut e_prev = x_ex;
637
638 if const { N > 1 } {
639 e_prev = e_n;
640 e_n = x.nmul_adde(e_n, exp_neg_x);
641
642 if const { N > 2 } {
643 if const { P::POLICY.precision.ge(PrecisionPolicy::Best) } {
644 let mut n = 0;
645 while n < (N - 2) {
646 e_prev = e_n;
647 e_n = x.nmul_adde(e_n, exp_neg_x) / V::splat(E::FACTORS[n]);
648 n += 1;
649 }
650 } else {
651 use crunchy::unroll;
652
653 macro_rules! unroll_recurrence {
654 ($($len:tt),*) => {
655 $( if const { N == ($len + 2) } {
656 unroll! { for n in 0..$len {
657 e_prev = e_n;
658 e_n = x.nmul_adde(e_n, exp_neg_x)
659 .scale(const { if n < N { E::RECIPROCALS[n] } else { E::ONE } });
660 }}
661 } else )* {
662 let mut n = 0;
663 while n < const { if N > 2 { N - 2 } else { 0 } } {
664 e_prev = e_n;
665 e_n = x.nmul_adde(e_n, exp_neg_x).scale(E::RECIPROCALS[n]);
666 n += 1;
667 }
668 }
669 };
670 }
671
672 unroll_recurrence!(1, 2, 3, 4, 5, 6); }
674 }
675 }
676
677 let is_very_large = x.cmp_ge(V::splat(E::RECURRENCE_THRESHOLD));
678
679 if const { N > 1 } && thermite::unlikely(is_very_large.any()) {
683 e_n = is_very_large.select(expint_fraction_n::<P, E, V, N>(x, exp_neg_x), e_n);
684
685 let back = (exp_neg_x - e_n.scale(E::from_int(const { N as thermite::LargeInt - 1 }))) / x;
692 e_prev = is_very_large.select(back, e_prev);
693 }
694
695 if const { P::POLICY.check_overflow } {
696 let x_is_zero = x.is_zero();
698
699 if const { N == 1 } {
700 e_n = x_is_zero.select(V::INFINITY, e_n);
701 } else if const { N > 1 } {
702 e_n = x_is_zero.select(V::splat(E::ONE_OVER_N_MINUS_1), e_n);
703 }
704
705 if const { N <= 2 } {
707 e_prev = x_is_zero.select(V::INFINITY, e_prev);
708 } else {
709 e_prev = x_is_zero.select(
710 V::splat(E::ONE / E::from_int(const { N as thermite::LargeInt - 2 })),
711 e_prev,
712 );
713 }
714
715 let bad = x.cmp_lt(V::ZERO) | x.is_nan();
717 e_n = bad.select(V::NAN, e_n);
718 e_prev = bad.select(V::NAN, e_prev);
719 }
720
721 (e_n, e_prev)
722}