Coverage Report

Created: 2025-11-24 07:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pxfm-0.1.25/src/bessel/i0f.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_estrin_polyeval9, f_polyeval6,
34
};
35
36
/// Modified Bessel of the first kind of order 0
37
///
38
/// Max ULP 0.5
39
0
pub fn f_i0f(x: f32) -> f32 {
40
0
    let ux = x.to_bits().wrapping_shl(1);
41
0
    if ux >= 0xffu32 << 24 || ux == 0 {
42
        // |x| == 0, |x| == inf, |x| == NaN
43
0
        if ux == 0 {
44
            // |x| == 0
45
0
            return 1.;
46
0
        }
47
0
        if x.is_infinite() {
48
0
            return f32::INFINITY;
49
0
        }
50
0
        return x + f32::NAN; // x == NaN
51
0
    }
52
53
0
    let xb = x.to_bits() & 0x7fff_ffff;
54
55
0
    if xb >= 0x42b7cd32u32 {
56
        // |x| >= 91.90077
57
0
        return f32::INFINITY;
58
0
    }
59
60
0
    if xb < 0x40f00000u32 {
61
        // |x| < 7.5
62
0
        if xb < 0x3f800000u32 {
63
            // |x| < 1
64
0
            if xb <= 0x34000000u32 {
65
                // |x| < f32::EPSILON
66
                // taylor series for I0(x) ~ 1 + x^2/4 + O(x^4)
67
                #[cfg(any(
68
                    all(
69
                        any(target_arch = "x86", target_arch = "x86_64"),
70
                        target_feature = "fma"
71
                    ),
72
                    target_arch = "aarch64"
73
                ))]
74
                {
75
                    use crate::common::f_fmlaf;
76
                    return f_fmlaf(x, x * 0.25, 1.);
77
                }
78
                #[cfg(not(any(
79
                    all(
80
                        any(target_arch = "x86", target_arch = "x86_64"),
81
                        target_feature = "fma"
82
                    ),
83
                    target_arch = "aarch64"
84
                )))]
85
                {
86
0
                    let dx = x as f64;
87
0
                    return f_fmla(dx, dx * 0.25, 1.) as f32;
88
                }
89
0
            }
90
0
            return i0f_small(f32::from_bits(xb)) as f32;
91
0
        } else if xb <= 0x40600000u32 {
92
            // |x| < 3.5
93
0
            return i0f_1_to_3p5(f32::from_bits(xb));
94
0
        } else if xb <= 0x40c00000u32 {
95
            // |x| < 6
96
0
            return i0f_3p5_to_6(f32::from_bits(xb));
97
0
        }
98
0
        return i0f_6_to_7p5(f32::from_bits(xb));
99
0
    }
100
101
0
    i0f_asympt(f32::from_bits(xb))
102
0
}
103
104
/**
105
How polynomial is obtained described at [i0f_1_to_7p5].
106
107
Computes I0(x) as follows:
108
I0(x) = 1 + (x/2)^2 * P(x)
109
110
This method valid only [0;1]
111
112
Generated by Wolfram Mathematica:
113
```text
114
<<FunctionApproximations`
115
ClearAll["Global`*"]
116
f[x_]:=(BesselI[0,x]-1)/(x/2)^2
117
g[z_]:=f[2 Sqrt[z]]
118
{err, approx}=MiniMaxApproximation[g[z],{z,{0.0000001,1},6,0},WorkingPrecision->60]
119
poly=Numerator[approx][[1]];
120
coeffs=CoefficientList[poly,z];
121
TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
122
```
123
**/
124
#[inline]
125
0
pub(crate) fn i0f_small(x: f32) -> f64 {
126
0
    let dx = x as f64;
127
    const C: f64 = 1. / 4.;
128
0
    let eval_x = dx * dx * C;
129
130
0
    let p = f_estrin_polyeval7(
131
0
        eval_x,
132
0
        f64::from_bits(0x3ff000000000013a),
133
0
        f64::from_bits(0x3fcffffffffc20b6),
134
0
        f64::from_bits(0x3f9c71c71e6cd6a2),
135
0
        f64::from_bits(0x3f5c71c65b0af15f),
136
0
        f64::from_bits(0x3f1234796fceb081),
137
0
        f64::from_bits(0x3ec0280faf31678c),
138
0
        f64::from_bits(0x3e664fd494223545),
139
    );
140
0
    f_fmla(p, eval_x, 1.)
141
0
}
142
143
/**
144
Computes I0.
145
146
/// Valid only on interval [1;3.5]
147
148
as rational approximation I0 = 1 + (x/2)^2 * Pn((x/2)^2)/Qm((x/2)^2))
149
150
Generated by Wolram Mathematica:
151
```python
152
<<FunctionApproximations`
153
ClearAll["Global`*"]
154
f[x_]:=(BesselI[0,x]-1)/(x/2)^2
155
g[z_]:=f[2 Sqrt[z]]
156
{err, approx}=MiniMaxApproximation[g[z],{z,{1,3.5},5,4},WorkingPrecision->60]
157
poly=Numerator[approx][[1]];
158
coeffs=CoefficientList[poly,z];
159
TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
160
poly=Denominator[approx][[1]];
161
coeffs=CoefficientList[poly,z];
162
TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
163
```
164
**/
165
#[inline]
166
0
fn i0f_1_to_3p5(x: f32) -> f32 {
167
0
    let dx = x as f64;
168
    const C: f64 = 1. / 4.;
169
0
    let eval_x = dx * dx * C;
170
171
0
    let p_num = f_polyeval6(
172
0
        eval_x,
173
0
        f64::from_bits(0x3feffffffffffb69),
174
0
        f64::from_bits(0x3fc9ed7bd9dc97a7),
175
0
        f64::from_bits(0x3f915c14693c842e),
176
0
        f64::from_bits(0x3f45c6dc6a719e42),
177
0
        f64::from_bits(0x3eeacb79eba725f7),
178
0
        f64::from_bits(0x3e7b51e2acfc4355),
179
    );
180
0
    let p_den = f_estrin_polyeval5(
181
0
        eval_x,
182
0
        f64::from_bits(0x3ff0000000000000),
183
0
        f64::from_bits(0xbfa84a10988f28eb),
184
0
        f64::from_bits(0x3f50f5599197a4be),
185
0
        f64::from_bits(0xbeea420cf9b13b1b),
186
0
        f64::from_bits(0x3e735d0c1eb6ed7d),
187
    );
188
189
0
    f_fmla(p_num / p_den, eval_x, 1.) as f32
190
0
}
191
192
// Valid only on interval [6;7]
193
// Generated by Wolfram Mathematica:
194
// <<FunctionApproximations`
195
// ClearAll["Global`*"]
196
// f[x_]:=(BesselI[0,x]-1)/(x/2)^2
197
// g[z_]:=f[2 Sqrt[z]]
198
// {err, approx}=MiniMaxApproximation[g[z],{z,{6,7},7,6},WorkingPrecision->60]
199
// poly=Numerator[approx][[1]];
200
// coeffs=CoefficientList[poly,z];
201
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
202
// poly=Denominator[approx][[1]];
203
// coeffs=CoefficientList[poly,z];
204
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
205
#[inline]
206
0
fn i0f_6_to_7p5(x: f32) -> f32 {
207
0
    let dx = x as f64;
208
    const C: f64 = 1. / 4.;
209
0
    let eval_x = dx * dx * C;
210
211
0
    let p_num = f_estrin_polyeval8(
212
0
        eval_x,
213
0
        f64::from_bits(0x3fefffffffffff7d),
214
0
        f64::from_bits(0x3fcb373b00569ccf),
215
0
        f64::from_bits(0x3f939069c3363b81),
216
0
        f64::from_bits(0x3f4c2095c90c66b3),
217
0
        f64::from_bits(0x3ef6713f648413db),
218
0
        f64::from_bits(0x3e947efa2f9936b4),
219
0
        f64::from_bits(0x3e2486a182f49420),
220
0
        f64::from_bits(0x3da213034a33de33),
221
    );
222
0
    let p_den = f_estrin_polyeval7(
223
0
        eval_x,
224
0
        f64::from_bits(0x3ff0000000000000),
225
0
        f64::from_bits(0xbfa32313fea59d9e),
226
0
        f64::from_bits(0x3f460594c2ec6706),
227
0
        f64::from_bits(0xbedf725fb714690f),
228
0
        f64::from_bits(0x3e6d9cb39b19555c),
229
0
        f64::from_bits(0xbdf1900e3abcb7a6),
230
0
        f64::from_bits(0x3d64a21a2ea78ef6),
231
    );
232
233
0
    f_fmla(p_num / p_den, eval_x, 1.) as f32
234
0
}
235
236
// Valid only on interval [3.5;6]
237
// Generated in Wolfram Mathematica:
238
// <<FunctionApproximations`
239
// ClearAll["Global`*"]
240
// f[x_]:=(BesselI[0,x]-1)/(x/2)^2
241
// g[z_]:=f[2 Sqrt[z]]
242
// {err, approx}=MiniMaxApproximation[g[z],{z,{3.5,6},5,5},WorkingPrecision->60]
243
// poly=Numerator[approx][[1]];
244
// coeffs=CoefficientList[poly,z];
245
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
246
// poly=Denominator[approx][[1]];
247
// coeffs=CoefficientList[poly,z];
248
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
249
#[inline]
250
0
fn i0f_3p5_to_6(x: f32) -> f32 {
251
0
    let dx = x as f64;
252
    const C: f64 = 1. / 4.;
253
0
    let eval_x = dx * dx * C;
254
255
0
    let p_num = f_polyeval6(
256
0
        eval_x,
257
0
        f64::from_bits(0x3feffffffffd9550),
258
0
        f64::from_bits(0x3fc97e18ee033fb4),
259
0
        f64::from_bits(0x3f90b3199079bce1),
260
0
        f64::from_bits(0x3f442c300a425372),
261
0
        f64::from_bits(0x3ee7831030ae18ca),
262
0
        f64::from_bits(0x3e76387d67354932),
263
    );
264
0
    let p_den = f_polyeval6(
265
0
        eval_x,
266
0
        f64::from_bits(0x3ff0000000000000),
267
0
        f64::from_bits(0xbfaa079c484e406a),
268
0
        f64::from_bits(0x3f5452098f1556fb),
269
0
        f64::from_bits(0xbef33efb4a8128ac),
270
0
        f64::from_bits(0x3e865996e19448ca),
271
0
        f64::from_bits(0xbe09acbb64533c3e),
272
    );
273
274
0
    f_fmla(p_num / p_den, eval_x, 1.) as f32
275
0
}
276
277
/**
278
Asymptotic expansion for I0.
279
280
Computes:
281
sqrt(x) * exp(-x) * I0(x) = Pn(1/x)/Qn(1/x)
282
hence:
283
I0(x) = Pn(1/x)/Qm(1/x)*exp(x)/sqrt(x)
284
285
Generated by Mathematica:
286
```text
287
<<FunctionApproximations`
288
ClearAll["Global`*"]
289
f[x_]:=Sqrt[x] Exp[-x] BesselI[0,x]
290
g[z_]:=f[1/z]
291
{err, approx}=MiniMaxApproximation[g[z],{z,{1/92.3,1/7.5},8,8},WorkingPrecision->70]
292
num=Numerator[approx][[1]];
293
den=Denominator[approx][[1]];
294
poly=num;
295
coeffs=CoefficientList[poly,z];
296
TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
297
```
298
**/
299
#[inline]
300
0
fn i0f_asympt(x: f32) -> f32 {
301
0
    let dx = x as f64;
302
0
    let recip = 1. / dx;
303
0
    let p_num = f_estrin_polyeval9(
304
0
        recip,
305
0
        f64::from_bits(0x3fd9884533d44829),
306
0
        f64::from_bits(0xc02c940f40595581),
307
0
        f64::from_bits(0x406d41c495c2f762),
308
0
        f64::from_bits(0xc0a10ab76dda4520),
309
0
        f64::from_bits(0x40c825b1c2a48d07),
310
0
        f64::from_bits(0xc0e481d606d0b748),
311
0
        f64::from_bits(0x40f34759deefbd40),
312
0
        f64::from_bits(0xc0ef4b7fb49fa116),
313
0
        f64::from_bits(0x40c409a6f882ba00),
314
    );
315
0
    let p_den = f_estrin_polyeval9(
316
0
        recip,
317
0
        f64::from_bits(0x3ff0000000000000),
318
0
        f64::from_bits(0xc041f8a9131ad229),
319
0
        f64::from_bits(0x408278e56af035bb),
320
0
        f64::from_bits(0xc0b5a34a108f3e35),
321
0
        f64::from_bits(0x40dee6f278ee24f5),
322
0
        f64::from_bits(0xc0fa95093b0c4f9f),
323
0
        f64::from_bits(0x4109982b87f75651),
324
0
        f64::from_bits(0xc10618cc3c91e2db),
325
0
        f64::from_bits(0x40e30895aec6fc4f),
326
    );
327
0
    let z = p_num / p_den;
328
329
0
    let e = core_expf(x);
330
0
    let r_sqrt = j1f_rsqrt(dx);
331
0
    (z * r_sqrt * e) as f32
332
0
}
333
334
#[cfg(test)]
335
mod tests {
336
    use super::*;
337
338
    #[test]
339
    fn test_i0f() {
340
        assert!(f_i0f(f32::NAN).is_nan());
341
        assert_eq!(f_i0f(f32::NEG_INFINITY), f32::INFINITY);
342
        assert_eq!(f_i0f(f32::INFINITY), f32::INFINITY);
343
        assert_eq!(f_i0f(1.), 1.2660658);
344
        assert_eq!(f_i0f(5.), 27.239872);
345
        assert_eq!(f_i0f(16.), 893446.25);
346
        assert_eq!(f_i0f(32.), 5590908000000.0);
347
        assert_eq!(f_i0f(92.0), f32::INFINITY);
348
        assert_eq!(f_i0f(0.), 1.0);
349
        assert_eq!(f_i0f(28.), 109534600000.0);
350
        assert_eq!(f_i0f(-28.), 109534600000.0);
351
        assert_eq!(f_i0f(-16.), 893446.25);
352
        assert_eq!(f_i0f(-32.), 5590908000000.0);
353
    }
354
}