exponential_integral/
chebyshev.rs1use {
4 crate::Approx,
5 sigma_types::{Finite, Zero as _, usize::LessThan},
6};
7
8#[cfg(feature = "error")]
9use {crate::constants, sigma_types::NonNegative};
10
11#[inline]
59#[must_use]
60pub fn eval<const N_COEFFICIENTS: usize>(
61 coefficients: &[Finite<f64>; N_COEFFICIENTS],
62 x: Finite<f64>,
63 #[cfg(feature = "precision")] order: LessThan<{ N_COEFFICIENTS }>,
64) -> Approx {
65 #![expect(
66 clippy::arithmetic_side_effects,
67 reason = "property-based testing ensures this never happens"
68 )]
69
70 debug_assert!(
71 N_COEFFICIENTS > 0,
72 "Chebyshev series without any coefficients",
73 );
74
75 let two_x: Finite<f64> = Finite::new(2_f64) * x;
76
77 #[cfg(feature = "error")]
78 let mut e = NonNegative::<Finite<f64>>::ZERO;
79
80 let mut d = Finite::<f64>::ZERO;
81 let mut dd = Finite::<f64>::ZERO;
82
83 {
84 let mut j: LessThan<{ N_COEFFICIENTS }> = {
85 #[cfg(feature = "precision")]
86 {
87 order
88 }
89 #[cfg(not(feature = "precision"))]
90 {
91 LessThan::new(const { N_COEFFICIENTS - 1 })
92 }
93 };
94 while *j >= 1 {
95 let coefficient = *unsafe { coefficients.get_unchecked(*j) };
98 let tmp = d;
99 d = ((two_x * d) - dd) + coefficient;
100 #[cfg(feature = "error")]
101 {
102 e += NonNegative::<Finite<f64>>::new((two_x * tmp).map(f64::abs))
103 + NonNegative::<Finite<f64>>::new(dd.map(f64::abs))
104 + NonNegative::<Finite<f64>>::new(coefficient.map(f64::abs));
105 }
106 dd = tmp;
107
108 j.map_mut(|u| *u -= 1);
109 }
110 }
111
112 {
113 #[cfg(feature = "error")]
114 let tmp = d;
115 let coefficient = *unsafe { coefficients.get_unchecked(0) };
118 let half_coefficient = coefficient.map(|c| 0.5_f64 * c);
119 d = x * d - dd + half_coefficient;
120 #[cfg(feature = "error")]
121 {
122 e += NonNegative::<Finite<f64>>::new((x * tmp).map(f64::abs))
123 + NonNegative::<Finite<f64>>::new(dd.map(f64::abs))
124 + NonNegative::<Finite<f64>>::new(half_coefficient.map(f64::abs));
125 }
126 }
127
128 #[cfg(feature = "error")]
129 let last_coefficient = *unsafe { coefficients.get_unchecked(const { N_COEFFICIENTS - 1 }) };
132
133 Approx {
134 value: d,
135 #[cfg(feature = "error")]
136 error: NonNegative::new(Finite::new(constants::GSL_DBL_EPSILON)) * e
137 + NonNegative::new(last_coefficient.map(f64::abs)),
138 }
139}
140
141#[inline]
143#[cfg_attr(not(test), expect(dead_code, reason = "TODO: REMOVE"))]
144#[cfg_attr(test, expect(clippy::single_call_fn, reason = "TODO: REMOVE"))]
145pub(crate) const fn min(a: usize, b: usize) -> usize {
146 if a.checked_sub(b).is_some() { b } else { a }
147}