Coverage Report

Created: 2026-09-01 07:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pxfm-0.1.30/src/bessel/i0ef.rs
Line
Count
Source
1
/*
2
 * // Copyright (c) Radzivon Bartoshyk 7/2025. All rights reserved.
3
 * //
4
 * // Redistribution and use in source and binary forms, with or without modification,
5
 * // are permitted provided that the following conditions are met:
6
 * //
7
 * // 1.  Redistributions of source code must retain the above copyright notice, this
8
 * // list of conditions and the following disclaimer.
9
 * //
10
 * // 2.  Redistributions in binary form must reproduce the above copyright notice,
11
 * // this list of conditions and the following disclaimer in the documentation
12
 * // and/or other materials provided with the distribution.
13
 * //
14
 * // 3.  Neither the name of the copyright holder nor the names of its
15
 * // contributors may be used to endorse or promote products derived from
16
 * // this software without specific prior written permission.
17
 * //
18
 * // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
 * // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
 * // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
 * // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
 * // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
 * // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
 * // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
 * // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
29
use crate::bessel::j0f::j1f_rsqrt;
30
use crate::common::f_fmla;
31
use crate::exponents::core_expf;
32
use crate::polyeval::{
33
    f_estrin_polyeval5, f_estrin_polyeval7, f_estrin_polyeval8, f_polyeval6, f_polyeval10,
34
};
35
36
/// Modified exponentially scaled Bessel of the first kind of order 0
37
///
38
/// Computes exp(-|x|)*I0(x)
39
///
40
/// Max ULP 0.5
41
0
pub fn f_i0ef(x: f32) -> f32 {
42
0
    let ux = x.to_bits().wrapping_shl(1);
43
0
    if ux >= 0xffu32 << 24 || ux == 0 {
44
        // |x| == 0, |x| == inf, |x| == NaN
45
0
        if ux == 0 {
46
            // |x| == 0
47
0
            return 1.;
48
0
        }
49
0
        if x.is_infinite() {
50
0
            return 0.;
51
0
        }
52
0
        return x + f32::NAN; // x == NaN
53
0
    }
54
55
0
    let xb = x.to_bits() & 0x7fff_ffff;
56
57
0
    if xb <= 0x40f00000u32 {
58
        // |x| <= 7.5
59
0
        let core_expf = core_expf(-f32::from_bits(xb));
60
0
        if xb < 0x3f800000u32 {
61
0
            if xb <= 0x34000000u32 {
62
                // |x| <= f32::EPSILON
63
                // taylor series for I0(x) * exp(-x) ~ 1 - x + O(x^2)
64
0
                return 1. - x;
65
0
            }
66
            // |x| < 1
67
0
            return i0f_small(f32::from_bits(xb), core_expf);
68
0
        } else if xb <= 0x40600000u32 {
69
            // |x| <= 3.5
70
0
            return i0ef_1_to_3p5(f32::from_bits(xb), core_expf);
71
0
        } else if xb <= 0x40c00000u32 {
72
            // |x| <= 6
73
0
            return i0f_3p5_to_6(f32::from_bits(xb), core_expf);
74
0
        }
75
0
        return i0f_6_to_7p5(f32::from_bits(xb), core_expf);
76
0
    }
77
78
0
    i0ef_asympt(f32::from_bits(xb))
79
0
}
80
81
/**
82
How polynomial is obtained described at [i0f_1_to_7p5].
83
84
Computes I0(x) as follows:
85
I0(x) = 1 + (x/2)^2 * P(x)
86
87
This method valid only [0;1]
88
89
Generated by Wolfram Mathematica:
90
```text
91
<<FunctionApproximations`
92
ClearAll["Global`*"]
93
f[x_]:=(BesselI[0,x]-1)/(x/2)^2
94
g[z_]:=f[2 Sqrt[z]]
95
{err, approx}=MiniMaxApproximation[g[z],{z,{0.0000001,1},6,0},WorkingPrecision->60]
96
poly=Numerator[approx][[1]];
97
coeffs=CoefficientList[poly,z];
98
TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
99
```
100
**/
101
#[inline]
102
0
pub(crate) fn i0f_small(x: f32, v_exp: f64) -> f32 {
103
0
    let dx = x as f64;
104
    const C: f64 = 1. / 4.;
105
0
    let eval_x = dx * dx * C;
106
107
0
    let p = f_estrin_polyeval7(
108
0
        eval_x,
109
0
        f64::from_bits(0x3ff000000000013a),
110
0
        f64::from_bits(0x3fcffffffffc20b6),
111
0
        f64::from_bits(0x3f9c71c71e6cd6a2),
112
0
        f64::from_bits(0x3f5c71c65b0af15f),
113
0
        f64::from_bits(0x3f1234796fceb081),
114
0
        f64::from_bits(0x3ec0280faf31678c),
115
0
        f64::from_bits(0x3e664fd494223545),
116
    );
117
0
    (f_fmla(p, eval_x, 1.) * v_exp) as f32
118
0
}
119
120
/**
121
Computes I0.
122
123
/// Valid only on interval [1;3.5]
124
125
as rational approximation I0 = 1 + (x/2)^2 * Pn((x/2)^2)/Qm((x/2)^2))
126
127
Generated by Wolram Mathematica:
128
```python
129
<<FunctionApproximations`
130
ClearAll["Global`*"]
131
f[x_]:=(BesselI[0,x]-1)/(x/2)^2
132
g[z_]:=f[2 Sqrt[z]]
133
{err, approx}=MiniMaxApproximation[g[z],{z,{1,3.5},5,4},WorkingPrecision->60]
134
poly=Numerator[approx][[1]];
135
coeffs=CoefficientList[poly,z];
136
TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
137
poly=Denominator[approx][[1]];
138
coeffs=CoefficientList[poly,z];
139
TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
140
```
141
**/
142
#[inline]
143
0
fn i0ef_1_to_3p5(x: f32, v_exp: f64) -> f32 {
144
0
    let dx = x as f64;
145
    const C: f64 = 1. / 4.;
146
0
    let eval_x = dx * dx * C;
147
148
0
    let p_num = f_polyeval6(
149
0
        eval_x,
150
0
        f64::from_bits(0x3feffffffffffb69),
151
0
        f64::from_bits(0x3fc9ed7bd9dc97a7),
152
0
        f64::from_bits(0x3f915c14693c842e),
153
0
        f64::from_bits(0x3f45c6dc6a719e42),
154
0
        f64::from_bits(0x3eeacb79eba725f7),
155
0
        f64::from_bits(0x3e7b51e2acfc4355),
156
    );
157
0
    let p_den = f_estrin_polyeval5(
158
0
        eval_x,
159
0
        f64::from_bits(0x3ff0000000000000),
160
0
        f64::from_bits(0xbfa84a10988f28eb),
161
0
        f64::from_bits(0x3f50f5599197a4be),
162
0
        f64::from_bits(0xbeea420cf9b13b1b),
163
0
        f64::from_bits(0x3e735d0c1eb6ed7d),
164
    );
165
166
0
    (f_fmla(p_num / p_den, eval_x, 1.) * v_exp) as f32
167
0
}
168
169
// Valid only on interval [6;7]
170
// Generated by Wolfram Mathematica:
171
// <<FunctionApproximations`
172
// ClearAll["Global`*"]
173
// f[x_]:=(BesselI[0,x]-1)/(x/2)^2
174
// g[z_]:=f[2 Sqrt[z]]
175
// {err, approx}=MiniMaxApproximation[g[z],{z,{6,7},7,6},WorkingPrecision->60]
176
// poly=Numerator[approx][[1]];
177
// coeffs=CoefficientList[poly,z];
178
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
179
// poly=Denominator[approx][[1]];
180
// coeffs=CoefficientList[poly,z];
181
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
182
#[inline]
183
0
fn i0f_6_to_7p5(x: f32, v_exp: f64) -> f32 {
184
0
    let dx = x as f64;
185
    const C: f64 = 1. / 4.;
186
0
    let eval_x = dx * dx * C;
187
188
0
    let p_num = f_estrin_polyeval8(
189
0
        eval_x,
190
0
        f64::from_bits(0x3fefffffffffff7d),
191
0
        f64::from_bits(0x3fcb373b00569ccf),
192
0
        f64::from_bits(0x3f939069c3363b81),
193
0
        f64::from_bits(0x3f4c2095c90c66b3),
194
0
        f64::from_bits(0x3ef6713f648413db),
195
0
        f64::from_bits(0x3e947efa2f9936b4),
196
0
        f64::from_bits(0x3e2486a182f49420),
197
0
        f64::from_bits(0x3da213034a33de33),
198
    );
199
0
    let p_den = f_estrin_polyeval7(
200
0
        eval_x,
201
0
        f64::from_bits(0x3ff0000000000000),
202
0
        f64::from_bits(0xbfa32313fea59d9e),
203
0
        f64::from_bits(0x3f460594c2ec6706),
204
0
        f64::from_bits(0xbedf725fb714690f),
205
0
        f64::from_bits(0x3e6d9cb39b19555c),
206
0
        f64::from_bits(0xbdf1900e3abcb7a6),
207
0
        f64::from_bits(0x3d64a21a2ea78ef6),
208
    );
209
210
0
    (f_fmla(p_num / p_den, eval_x, 1.) * v_exp) as f32
211
0
}
212
213
// Valid only on interval [3.5;6]
214
// Generated in Wolfram Mathematica:
215
// <<FunctionApproximations`
216
// ClearAll["Global`*"]
217
// f[x_]:=(BesselI[0,x]-1)/(x/2)^2
218
// g[z_]:=f[2 Sqrt[z]]
219
// {err, approx}=MiniMaxApproximation[g[z],{z,{3.5,6},5,5},WorkingPrecision->60]
220
// poly=Numerator[approx][[1]];
221
// coeffs=CoefficientList[poly,z];
222
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
223
// poly=Denominator[approx][[1]];
224
// coeffs=CoefficientList[poly,z];
225
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
226
#[inline]
227
0
fn i0f_3p5_to_6(x: f32, v_exp: f64) -> f32 {
228
0
    let dx = x as f64;
229
    const C: f64 = 1. / 4.;
230
0
    let eval_x = dx * dx * C;
231
232
0
    let p_num = f_polyeval6(
233
0
        eval_x,
234
0
        f64::from_bits(0x3feffffffffd9550),
235
0
        f64::from_bits(0x3fc97e18ee033fb4),
236
0
        f64::from_bits(0x3f90b3199079bce1),
237
0
        f64::from_bits(0x3f442c300a425372),
238
0
        f64::from_bits(0x3ee7831030ae18ca),
239
0
        f64::from_bits(0x3e76387d67354932),
240
    );
241
0
    let p_den = f_polyeval6(
242
0
        eval_x,
243
0
        f64::from_bits(0x3ff0000000000000),
244
0
        f64::from_bits(0xbfaa079c484e406a),
245
0
        f64::from_bits(0x3f5452098f1556fb),
246
0
        f64::from_bits(0xbef33efb4a8128ac),
247
0
        f64::from_bits(0x3e865996e19448ca),
248
0
        f64::from_bits(0xbe09acbb64533c3e),
249
    );
250
251
0
    (f_fmla(p_num / p_den, eval_x, 1.) * v_exp) as f32
252
0
}
253
254
/**
255
Asymptotic expansion for I0.
256
257
Computes:
258
sqrt(x) * exp(-x) * I0(x) = Pn(1/x)/Qn(1/x)
259
hence:
260
I0(x)exp(-x) = Pn(1/x)/Qm(1/x)/sqrt(x)
261
262
Generated by Mathematica:
263
```text
264
<<FunctionApproximations`
265
ClearAll["Global`*"]
266
f[x_]:=Sqrt[x] Exp[-x] BesselI[0,x]
267
g[z_]:=f[1/z]
268
{err,approx}=MiniMaxApproximation[g[z],{z,{2^-33,1/7.5},9,9},WorkingPrecision->70]
269
num=Numerator[approx][[1]];
270
den=Denominator[approx][[1]];
271
poly=num;
272
coeffs=CoefficientList[poly,z];
273
TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
274
poly=den;
275
coeffs=CoefficientList[poly,z];
276
TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
277
```
278
**/
279
#[inline]
280
0
fn i0ef_asympt(x: f32) -> f32 {
281
0
    let dx = x as f64;
282
0
    let recip = 1. / dx;
283
0
    let p_num = f_polyeval10(
284
0
        recip,
285
0
        f64::from_bits(0x3fd9884533d4364f),
286
0
        f64::from_bits(0xc02ed6c9269921a7),
287
0
        f64::from_bits(0x4070ee77ffed64a5),
288
0
        f64::from_bits(0xc0a4ffd558b06889),
289
0
        f64::from_bits(0x40cf2633e2840f6f),
290
0
        f64::from_bits(0xc0ea813a9ba42b84),
291
0
        f64::from_bits(0x40f569bf5d63eb8c),
292
0
        f64::from_bits(0xc0b3138874cdd180),
293
0
        f64::from_bits(0xc0fa3152ed485937),
294
0
        f64::from_bits(0x40ddaccbed454f47),
295
    );
296
0
    let p_den = f_polyeval10(
297
0
        recip,
298
0
        f64::from_bits(0x3ff0000000000000),
299
0
        f64::from_bits(0xc0436352c350b88c),
300
0
        f64::from_bits(0x40855eaa17b05edd),
301
0
        f64::from_bits(0xc0baa46f155bd266),
302
0
        f64::from_bits(0x40e3e9fd90a2e695),
303
0
        f64::from_bits(0xc1012dc621dfc1e8),
304
0
        f64::from_bits(0x410cafeea713e8ce),
305
0
        f64::from_bits(0xc0e0a3ee0077d7f7),
306
0
        f64::from_bits(0xc110bcced6a39e9e),
307
0
        f64::from_bits(0x40f9a1e4a91be4d6),
308
    );
309
0
    let z = p_num / p_den;
310
0
    let r_sqrt = j1f_rsqrt(dx);
311
0
    (z * r_sqrt) as f32
312
0
}
313
314
#[cfg(test)]
315
mod tests {
316
    use super::*;
317
318
    #[test]
319
    fn test_i0f() {
320
        assert!(f_i0ef(f32::NAN).is_nan());
321
        assert_eq!(f_i0ef(f32::NEG_INFINITY), 0.);
322
        assert_eq!(f_i0ef(f32::INFINITY), 0.);
323
        assert_eq!(f_i0ef(1.), 0.4657596);
324
        assert_eq!(f_i0ef(5.), 0.1835408);
325
        assert_eq!(f_i0ef(16.), 0.100544125);
326
        assert_eq!(f_i0ef(32.), 0.070804186);
327
        assert_eq!(f_i0ef(92.0), 0.04164947);
328
        assert_eq!(f_i0ef(0.), 1.0);
329
        assert_eq!(f_i0ef(28.), 0.075736605);
330
        assert_eq!(f_i0ef(-28.), 0.075736605);
331
        assert_eq!(f_i0ef(-32.), 0.070804186);
332
        assert_eq!(f_i0ef(-92.0), 0.04164947);
333
        assert_eq!(f_i0ef(-0.), 1.0);
334
    }
335
}