Coverage Report

Created: 2025-10-28 08:03

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/k0e.rs
Line
Count
Source
1
/*
2
 * // Copyright (c) Radzivon Bartoshyk 8/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::i0::bessel_rsqrt_hard;
30
use crate::bessel::i0_exp;
31
use crate::bessel::k0::k0_small_dd;
32
use crate::double_double::DoubleDouble;
33
use crate::dyadic_float::{DyadicFloat128, DyadicSign};
34
35
/// Modified exponentially scaled Bessel of the first kind of order 0
36
///
37
/// Computes K0(x)exp(x)
38
0
pub fn f_k0e(x: f64) -> f64 {
39
0
    let ix = x.to_bits();
40
41
0
    if ix >= 0x7ffu64 << 52 || ix == 0 {
42
        // |x| == NaN, x == inf, |x| == 0, x < 0
43
0
        if ix.wrapping_shl(1) == 0 {
44
            // |x| == 0
45
0
            return f64::INFINITY;
46
0
        }
47
0
        if x.is_infinite() {
48
0
            return if x.is_sign_positive() { 0. } else { f64::NAN };
49
0
        }
50
0
        return x + f64::NAN; // x == NaN
51
0
    }
52
53
0
    let xb = x.to_bits();
54
55
0
    if xb <= 0x3ff0000000000000 {
56
        // x <= 1
57
0
        let v_k0 = k0_small_dd(x);
58
0
        let v_exp = i0_exp(x);
59
0
        return DoubleDouble::quick_mult(v_exp, v_k0).to_f64();
60
0
    }
61
62
0
    k0e_asympt(x)
63
0
}
64
65
/**
66
Generated in Wolfram
67
68
Computes sqrt(x)*exp(x)*K0(x)=Pn(1/x)/Qm(1/x)
69
hence
70
K0(x)exp(x) = Pn(1/x)/Qm(1/x) / sqrt(x)
71
72
```text
73
<<FunctionApproximations`
74
ClearAll["Global`*"]
75
f[x_]:=Sqrt[x] Exp[x] BesselK[0,x]
76
g[z_]:=f[1/z]
77
{err, approx}=MiniMaxApproximation[g[z],{z,{0.0000000000001,1},11,11},WorkingPrecision->60]
78
poly=Numerator[approx][[1]];
79
coeffs=CoefficientList[poly,z];
80
TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
81
poly=Denominator[approx][[1]];
82
coeffs=CoefficientList[poly,z];
83
TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
84
```
85
**/
86
#[inline]
87
0
fn k0e_asympt(x: f64) -> f64 {
88
0
    let recip = DoubleDouble::from_quick_recip(x);
89
0
    let r_sqrt = DoubleDouble::from_sqrt(x);
90
91
    const P: [(u64, u64); 12] = [
92
        (0xbc9a6a11d237114e, 0x3ff40d931ff62706),
93
        (0x3cdd614ddf4929e5, 0x4040645168c3e483),
94
        (0xbd1ecf9ea0af6ab2, 0x40757419a703a2ab),
95
        (0xbd3da3551fb27770, 0x409d4e65365522a2),
96
        (0xbd564d58855d1a46, 0x40b6dd32f5a199d9),
97
        (0xbd6cf055ca963a8e, 0x40c4fd2368f19618),
98
        (0x3d4b6cdfbdc058df, 0x40c68faa11ebcd59),
99
        (0x3d5b4ce4665bfa46, 0x40bb6fbe08e0a8ea),
100
        (0xbd4316909063be15, 0x40a1953103a5be31),
101
        (0x3d12f3f8edf41af0, 0x4074d2cb001e175c),
102
        (0xbcd7bba36540264f, 0x40316cffcad5f8f9),
103
        (0xbc6bf28dfdd5d37d, 0x3fc2f487fe78b8d7),
104
    ];
105
106
0
    let x2 = DoubleDouble::quick_mult(recip, recip);
107
0
    let x4 = DoubleDouble::quick_mult(x2, x2);
108
0
    let x8 = DoubleDouble::quick_mult(x4, x4);
109
110
0
    let e0 = DoubleDouble::mul_add(
111
0
        recip,
112
0
        DoubleDouble::from_bit_pair(P[1]),
113
0
        DoubleDouble::from_bit_pair(P[0]),
114
    );
115
0
    let e1 = DoubleDouble::mul_add(
116
0
        recip,
117
0
        DoubleDouble::from_bit_pair(P[3]),
118
0
        DoubleDouble::from_bit_pair(P[2]),
119
    );
120
0
    let e2 = DoubleDouble::mul_add(
121
0
        recip,
122
0
        DoubleDouble::from_bit_pair(P[5]),
123
0
        DoubleDouble::from_bit_pair(P[4]),
124
    );
125
0
    let e3 = DoubleDouble::mul_add(
126
0
        recip,
127
0
        DoubleDouble::from_bit_pair(P[7]),
128
0
        DoubleDouble::from_bit_pair(P[6]),
129
    );
130
0
    let e4 = DoubleDouble::mul_add(
131
0
        recip,
132
0
        DoubleDouble::from_bit_pair(P[9]),
133
0
        DoubleDouble::from_bit_pair(P[8]),
134
    );
135
0
    let e5 = DoubleDouble::mul_add(
136
0
        recip,
137
0
        DoubleDouble::from_bit_pair(P[11]),
138
0
        DoubleDouble::from_bit_pair(P[10]),
139
    );
140
141
0
    let f0 = DoubleDouble::mul_add(x2, e1, e0);
142
0
    let f1 = DoubleDouble::mul_add(x2, e3, e2);
143
0
    let f2 = DoubleDouble::mul_add(x2, e5, e4);
144
145
0
    let g0 = DoubleDouble::mul_add(x4, f1, f0);
146
147
0
    let p_num = DoubleDouble::mul_add(x8, f2, g0);
148
149
    const Q: [(u64, u64); 12] = [
150
        (0x0000000000000000, 0x3ff0000000000000),
151
        (0xbcb9e8a5b17e696a, 0x403a485acd64d64a),
152
        (0x3cd2e2e9c87f71f7, 0x4071518092320ecb),
153
        (0xbd0d05bdb9431a2f, 0x4097e57e4c22c08e),
154
        (0x3d5207068ab19ba9, 0x40b2ebadb2db62f9),
155
        (0xbd64e37674083471, 0x40c1c0e4e9d6493d),
156
        (0x3d3efb7a9a62b020, 0x40c3b94e8d62cdc7),
157
        (0x3d47d6ce80a2114b, 0x40b93c2fd39e868e),
158
        (0xbd1dfda61f525861, 0x40a1877a53a7f8d8),
159
        (0x3d1236ff523dfcfa, 0x4077c3a10c2827de),
160
        (0xbcc889997c9b0fe7, 0x4039a1d80b11c4a1),
161
        (0x3c7ded0e8d73dddc, 0x3fdafe4913722123),
162
    ];
163
164
0
    let e0 = DoubleDouble::mul_add_f64(
165
0
        recip,
166
0
        DoubleDouble::from_bit_pair(Q[1]),
167
0
        f64::from_bits(0x3ff0000000000000),
168
    );
169
0
    let e1 = DoubleDouble::mul_add(
170
0
        recip,
171
0
        DoubleDouble::from_bit_pair(Q[3]),
172
0
        DoubleDouble::from_bit_pair(Q[2]),
173
    );
174
0
    let e2 = DoubleDouble::mul_add(
175
0
        recip,
176
0
        DoubleDouble::from_bit_pair(Q[5]),
177
0
        DoubleDouble::from_bit_pair(Q[4]),
178
    );
179
0
    let e3 = DoubleDouble::mul_add(
180
0
        recip,
181
0
        DoubleDouble::from_bit_pair(Q[7]),
182
0
        DoubleDouble::from_bit_pair(Q[6]),
183
    );
184
0
    let e4 = DoubleDouble::mul_add(
185
0
        recip,
186
0
        DoubleDouble::from_bit_pair(Q[9]),
187
0
        DoubleDouble::from_bit_pair(Q[8]),
188
    );
189
0
    let e5 = DoubleDouble::mul_add(
190
0
        recip,
191
0
        DoubleDouble::from_bit_pair(Q[11]),
192
0
        DoubleDouble::from_bit_pair(Q[10]),
193
    );
194
195
0
    let f0 = DoubleDouble::mul_add(x2, e1, e0);
196
0
    let f1 = DoubleDouble::mul_add(x2, e3, e2);
197
0
    let f2 = DoubleDouble::mul_add(x2, e5, e4);
198
199
0
    let g0 = DoubleDouble::mul_add(x4, f1, f0);
200
201
0
    let p_den = DoubleDouble::mul_add(x8, f2, g0);
202
203
0
    let z = DoubleDouble::div(p_num, p_den);
204
205
0
    let r = DoubleDouble::div(z, r_sqrt);
206
207
0
    let err = r.hi * f64::from_bits(0x3c10000000000000); // 2^-62
208
0
    let ub = r.hi + (r.lo + err);
209
0
    let lb = r.hi + (r.lo - err);
210
0
    if ub != lb {
211
0
        return k0e_asympt_hard(x);
212
0
    }
213
0
    r.to_f64()
214
0
}
215
216
/**
217
Generated in Wolfram
218
219
Computes sqrt(x)*exp(x)*K0(x)=Pn(1/x)/Qm(1/x)
220
hence
221
K0(x)exp(x) = Pn(1/x)/Qm(1/x) / sqrt(x)
222
223
```text
224
<<FunctionApproximations`
225
ClearAll["Global`*"]
226
f[x_]:=Sqrt[x] Exp[x] BesselK[0,x]
227
g[z_]:=f[1/z]
228
{err, approx}=MiniMaxApproximation[g[z],{z,{0.0000000000001,1},14,14},WorkingPrecision->90]
229
poly=Numerator[approx][[1]];
230
coeffs=CoefficientList[poly,z];
231
TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
232
poly=Denominator[approx][[1]];
233
coeffs=CoefficientList[poly,z];
234
TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
235
```
236
**/
237
#[inline(never)]
238
#[cold]
239
0
fn k0e_asympt_hard(x: f64) -> f64 {
240
    static P: [DyadicFloat128; 15] = [
241
        DyadicFloat128 {
242
            sign: DyadicSign::Pos,
243
            exponent: -127,
244
            mantissa: 0xa06c98ff_b1382cb2_be520f51_a7b8f970_u128,
245
        },
246
        DyadicFloat128 {
247
            sign: DyadicSign::Pos,
248
            exponent: -122,
249
            mantissa: 0xc84d8d0c_7faeef84_e56abccc_3d70f8a2_u128,
250
        },
251
        DyadicFloat128 {
252
            sign: DyadicSign::Pos,
253
            exponent: -118,
254
            mantissa: 0xd1a71096_3da22280_35768c9e_0d3ddf42_u128,
255
        },
256
        DyadicFloat128 {
257
            sign: DyadicSign::Pos,
258
            exponent: -115,
259
            mantissa: 0xf202e474_3698aabb_05688da0_ea1a088d_u128,
260
        },
261
        DyadicFloat128 {
262
            sign: DyadicSign::Pos,
263
            exponent: -112,
264
            mantissa: 0xaaa01830_8138af4d_1137b2dd_11a216f5_u128,
265
        },
266
        DyadicFloat128 {
267
            sign: DyadicSign::Pos,
268
            exponent: -110,
269
            mantissa: 0x99e0649f_320bca1a_c7adadb3_f5d8498e_u128,
270
        },
271
        DyadicFloat128 {
272
            sign: DyadicSign::Pos,
273
            exponent: -109,
274
            mantissa: 0xb4d81657_de1baf00_918cbc76_c6974e96_u128,
275
        },
276
        DyadicFloat128 {
277
            sign: DyadicSign::Pos,
278
            exponent: -108,
279
            mantissa: 0x8a9a28c8_a61c2c7a_12416d56_51c0b3d3_u128,
280
        },
281
        DyadicFloat128 {
282
            sign: DyadicSign::Pos,
283
            exponent: -108,
284
            mantissa: 0x88a079f1_d9bd4582_6353316c_3aeb9dc9_u128,
285
        },
286
        DyadicFloat128 {
287
            sign: DyadicSign::Pos,
288
            exponent: -109,
289
            mantissa: 0xa82e10eb_9dc6225a_ef6223e7_54aa254d_u128,
290
        },
291
        DyadicFloat128 {
292
            sign: DyadicSign::Pos,
293
            exponent: -111,
294
            mantissa: 0xf5fc07fe_6b652e8a_0b9e74ba_d0c56118_u128,
295
        },
296
        DyadicFloat128 {
297
            sign: DyadicSign::Pos,
298
            exponent: -113,
299
            mantissa: 0xc5288444_c7354b24_4a4e1663_86488928_u128,
300
        },
301
        DyadicFloat128 {
302
            sign: DyadicSign::Pos,
303
            exponent: -116,
304
            mantissa: 0x96d3d226_a220ae6e_d6cca1ae_40f01e27_u128,
305
        },
306
        DyadicFloat128 {
307
            sign: DyadicSign::Pos,
308
            exponent: -121,
309
            mantissa: 0xa7ab931b_499c4964_499c1091_4ab9673d_u128,
310
        },
311
        DyadicFloat128 {
312
            sign: DyadicSign::Pos,
313
            exponent: -129,
314
            mantissa: 0xf8373d1a_9ff3f9c6_e5cfbe0a_85ccc131_u128,
315
        },
316
    ];
317
318
    static Q: [DyadicFloat128; 15] = [
319
        DyadicFloat128 {
320
            sign: DyadicSign::Pos,
321
            exponent: -127,
322
            mantissa: 0x80000000_00000000_00000000_00000000_u128,
323
        },
324
        DyadicFloat128 {
325
            sign: DyadicSign::Pos,
326
            exponent: -122,
327
            mantissa: 0xa05190f4_dcf0d35c_277e0f21_0635c538_u128,
328
        },
329
        DyadicFloat128 {
330
            sign: DyadicSign::Pos,
331
            exponent: -118,
332
            mantissa: 0xa8837381_94c38992_86c0995d_5e5fa474_u128,
333
        },
334
        DyadicFloat128 {
335
            sign: DyadicSign::Pos,
336
            exponent: -115,
337
            mantissa: 0xc3a4f279_9297e905_f59cc065_75959de8_u128,
338
        },
339
        DyadicFloat128 {
340
            sign: DyadicSign::Pos,
341
            exponent: -112,
342
            mantissa: 0x8b05ade4_03432e06_881ce37d_a907216d_u128,
343
        },
344
        DyadicFloat128 {
345
            sign: DyadicSign::Pos,
346
            exponent: -111,
347
            mantissa: 0xfd77f85e_35626f21_355ae728_01b78cbe_u128,
348
        },
349
        DyadicFloat128 {
350
            sign: DyadicSign::Pos,
351
            exponent: -109,
352
            mantissa: 0x972ed117_254970eb_661121dc_a4462d2f_u128,
353
        },
354
        DyadicFloat128 {
355
            sign: DyadicSign::Pos,
356
            exponent: -109,
357
            mantissa: 0xec9d204a_9294ab57_2ef500d5_59d701b7_u128,
358
        },
359
        DyadicFloat128 {
360
            sign: DyadicSign::Pos,
361
            exponent: -109,
362
            mantissa: 0xf033522d_cae45860_53a01453_c56da895_u128,
363
        },
364
        DyadicFloat128 {
365
            sign: DyadicSign::Pos,
366
            exponent: -109,
367
            mantissa: 0x9a33640c_9896ead5_1ce040d7_b36544f3_u128,
368
        },
369
        DyadicFloat128 {
370
            sign: DyadicSign::Pos,
371
            exponent: -111,
372
            mantissa: 0xefe714fa_49da0166_fdf8bc68_57b77fa0_u128,
373
        },
374
        DyadicFloat128 {
375
            sign: DyadicSign::Pos,
376
            exponent: -113,
377
            mantissa: 0xd323b84c_214196b0_e25b8931_930fea0d_u128,
378
        },
379
        DyadicFloat128 {
380
            sign: DyadicSign::Pos,
381
            exponent: -116,
382
            mantissa: 0xbbb5240b_346642d8_010383cb_1e8a607e_u128,
383
        },
384
        DyadicFloat128 {
385
            sign: DyadicSign::Pos,
386
            exponent: -120,
387
            mantissa: 0x88dcfa2a_f9f7d2ab_dd017994_8fae7e87_u128,
388
        },
389
        DyadicFloat128 {
390
            sign: DyadicSign::Pos,
391
            exponent: -127,
392
            mantissa: 0xc891477c_526e0f5e_74c4ae9f_9d8732b5_u128,
393
        },
394
    ];
395
396
0
    let recip = DyadicFloat128::accurate_reciprocal(x);
397
0
    let r_sqrt = bessel_rsqrt_hard(x, recip);
398
399
0
    let mut p0 = P[14];
400
0
    for i in (0..14).rev() {
401
0
        p0 = recip * p0 + P[i];
402
0
    }
403
404
0
    let mut q = Q[14];
405
0
    for i in (0..14).rev() {
406
0
        q = recip * q + Q[i];
407
0
    }
408
409
0
    let v = p0 * q.reciprocal();
410
0
    let r = v * r_sqrt;
411
0
    r.fast_as_f64()
412
0
}
413
414
#[cfg(test)]
415
mod tests {
416
    use super::*;
417
418
    #[test]
419
    fn test_k0() {
420
        assert_eq!(f_k0e(0.00060324324), 7.533665613459802);
421
        assert_eq!(f_k0e(0.11), 2.6045757643537244);
422
        assert_eq!(f_k0e(0.643), 1.3773725807788395);
423
        assert_eq!(f_k0e(0.964), 1.1625987432322884);
424
        assert_eq!(f_k0e(2.964), 0.7017119941259377);
425
        assert_eq!(f_k0e(423.43), 0.06088931243251448);
426
        assert_eq!(f_k0e(4324235240321.43), 6.027056776336986e-7);
427
        assert_eq!(k0e_asympt_hard(423.43), 0.06088931243251448);
428
        assert_eq!(f_k0e(0.), f64::INFINITY);
429
        assert_eq!(f_k0e(-0.), f64::INFINITY);
430
        assert!(f_k0e(-0.5).is_nan());
431
        assert!(f_k0e(f64::NEG_INFINITY).is_nan());
432
        assert_eq!(f_k0e(f64::INFINITY), 0.);
433
    }
434
}