/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pxfm-0.1.25/src/bessel/k1e.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::k1::k1_small; |
32 | | use crate::double_double::DoubleDouble; |
33 | | use crate::dyadic_float::{DyadicFloat128, DyadicSign}; |
34 | | |
35 | | /// Modified exponentially scaled Bessel of the second kind of order 1 |
36 | | /// |
37 | | /// Computes K1(x)exp(x) |
38 | 0 | pub fn f_k1e(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_exp = i0_exp(x); |
58 | 0 | let v_k = k1_small(x); |
59 | 0 | return DoubleDouble::quick_mult(v_exp, v_k).to_f64(); |
60 | 0 | } |
61 | | |
62 | 0 | k1e_asympt(x) |
63 | 0 | } |
64 | | |
65 | | /** |
66 | | Generated by Wolfram Mathematica: |
67 | | ```text |
68 | | <<FunctionApproximations` |
69 | | ClearAll["Global`*"] |
70 | | f[x_]:=Sqrt[x] Exp[x] BesselK[1,x] |
71 | | g[z_]:=f[1/z] |
72 | | {err,approx}=MiniMaxApproximation[g[z],{z,{0.0000000000001,1},11,11},WorkingPrecision->60] |
73 | | poly=Numerator[approx][[1]]; |
74 | | coeffs=CoefficientList[poly,z]; |
75 | | TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
76 | | poly=Denominator[approx][[1]]; |
77 | | coeffs=CoefficientList[poly,z]; |
78 | | TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
79 | | ``` |
80 | | **/ |
81 | | #[inline] |
82 | 0 | fn k1e_asympt(x: f64) -> f64 { |
83 | 0 | let recip = DoubleDouble::from_quick_recip(x); |
84 | 0 | let r_sqrt = DoubleDouble::from_sqrt(x); |
85 | | |
86 | | const P: [(u64, u64); 12] = [ |
87 | | (0xbc9a6a0690becb3b, 0x3ff40d931ff62706), |
88 | | (0xbce573e1bbf2f0b7, 0x40402cebfab5721d), |
89 | | (0x3d11a739b7c11e7b, 0x4074f58abc0cfbf1), |
90 | | (0xbd2682a09ded0116, 0x409c8315f8facef2), |
91 | | (0xbd3a19e91a120168, 0x40b65f7a4caed8b9), |
92 | | (0x3d449c3d2b834543, 0x40c4fe41fdb4e7b8), |
93 | | (0xbd6bdd415ac7f7e1, 0x40c7aa402d035d03), |
94 | | (0x3d528412ff0d6b24, 0x40bf68faddd7d850), |
95 | | (0xbd48f4bb3f61dac6, 0x40a75f5650249952), |
96 | | (0xbd1dc534b275e309, 0x4081bddd259c0582), |
97 | | (0xbcce5103350bd226, 0x4046c7a049014484), |
98 | | (0x3c8935f8acd6c1d0, 0x3fef7524082b1859), |
99 | | ]; |
100 | | |
101 | 0 | let x2 = DoubleDouble::quick_mult(recip, recip); |
102 | 0 | let x4 = DoubleDouble::quick_mult(x2, x2); |
103 | 0 | let x8 = DoubleDouble::quick_mult(x4, x4); |
104 | | |
105 | 0 | let e0 = DoubleDouble::mul_add( |
106 | 0 | recip, |
107 | 0 | DoubleDouble::from_bit_pair(P[1]), |
108 | 0 | DoubleDouble::from_bit_pair(P[0]), |
109 | | ); |
110 | 0 | let e1 = DoubleDouble::mul_add( |
111 | 0 | recip, |
112 | 0 | DoubleDouble::from_bit_pair(P[3]), |
113 | 0 | DoubleDouble::from_bit_pair(P[2]), |
114 | | ); |
115 | 0 | let e2 = DoubleDouble::mul_add( |
116 | 0 | recip, |
117 | 0 | DoubleDouble::from_bit_pair(P[5]), |
118 | 0 | DoubleDouble::from_bit_pair(P[4]), |
119 | | ); |
120 | 0 | let e3 = DoubleDouble::mul_add( |
121 | 0 | recip, |
122 | 0 | DoubleDouble::from_bit_pair(P[7]), |
123 | 0 | DoubleDouble::from_bit_pair(P[6]), |
124 | | ); |
125 | 0 | let e4 = DoubleDouble::mul_add( |
126 | 0 | recip, |
127 | 0 | DoubleDouble::from_bit_pair(P[9]), |
128 | 0 | DoubleDouble::from_bit_pair(P[8]), |
129 | | ); |
130 | 0 | let e5 = DoubleDouble::mul_add( |
131 | 0 | recip, |
132 | 0 | DoubleDouble::from_bit_pair(P[11]), |
133 | 0 | DoubleDouble::from_bit_pair(P[10]), |
134 | | ); |
135 | | |
136 | 0 | let f0 = DoubleDouble::mul_add(x2, e1, e0); |
137 | 0 | let f1 = DoubleDouble::mul_add(x2, e3, e2); |
138 | 0 | let f2 = DoubleDouble::mul_add(x2, e5, e4); |
139 | | |
140 | 0 | let g0 = DoubleDouble::mul_add(x4, f1, f0); |
141 | | |
142 | 0 | let p_num = DoubleDouble::mul_add(x8, f2, g0); |
143 | | |
144 | | const Q: [(u64, u64); 12] = [ |
145 | | (0x0000000000000000, 0x3ff0000000000000), |
146 | | (0x3cc0d2508437b3f4, 0x40396ff483adec14), |
147 | | (0xbd130a9c9f8a5338, 0x4070225588d8c15d), |
148 | | (0xbceceba8fa0e65a2, 0x4095481f6684e3bb), |
149 | | (0x3d4099f3c178fd2a, 0x40afedc8a778bf42), |
150 | | (0xbd3a7e6a6276a3e7, 0x40bc0c060112692e), |
151 | | (0x3d11538c155b16d8, 0x40bcb12bd1101782), |
152 | | (0xbd5f7b04cdea2c61, 0x40b07fa363202e10), |
153 | | (0xbce444ed035b66c6, 0x4093d6fe8f44f838), |
154 | | (0xbcf6f88fb942b610, 0x4065c99fa44030c3), |
155 | | (0xbcbd1d2aedee5bc9, 0x40207ffabeb00eea), |
156 | | (0xbc39a0c8091102c9, 0x3facff3d892cd57a), |
157 | | ]; |
158 | | |
159 | 0 | let e0 = DoubleDouble::mul_add_f64( |
160 | 0 | recip, |
161 | 0 | DoubleDouble::from_bit_pair(Q[1]), |
162 | 0 | f64::from_bits(0x3ff0000000000000), |
163 | | ); |
164 | 0 | let e1 = DoubleDouble::mul_add( |
165 | 0 | recip, |
166 | 0 | DoubleDouble::from_bit_pair(Q[3]), |
167 | 0 | DoubleDouble::from_bit_pair(Q[2]), |
168 | | ); |
169 | 0 | let e2 = DoubleDouble::mul_add( |
170 | 0 | recip, |
171 | 0 | DoubleDouble::from_bit_pair(Q[5]), |
172 | 0 | DoubleDouble::from_bit_pair(Q[4]), |
173 | | ); |
174 | 0 | let e3 = DoubleDouble::mul_add( |
175 | 0 | recip, |
176 | 0 | DoubleDouble::from_bit_pair(Q[7]), |
177 | 0 | DoubleDouble::from_bit_pair(Q[6]), |
178 | | ); |
179 | 0 | let e4 = DoubleDouble::mul_add( |
180 | 0 | recip, |
181 | 0 | DoubleDouble::from_bit_pair(Q[9]), |
182 | 0 | DoubleDouble::from_bit_pair(Q[8]), |
183 | | ); |
184 | 0 | let e5 = DoubleDouble::mul_add( |
185 | 0 | recip, |
186 | 0 | DoubleDouble::from_bit_pair(Q[11]), |
187 | 0 | DoubleDouble::from_bit_pair(Q[10]), |
188 | | ); |
189 | | |
190 | 0 | let f0 = DoubleDouble::mul_add(x2, e1, e0); |
191 | 0 | let f1 = DoubleDouble::mul_add(x2, e3, e2); |
192 | 0 | let f2 = DoubleDouble::mul_add(x2, e5, e4); |
193 | | |
194 | 0 | let g0 = DoubleDouble::mul_add(x4, f1, f0); |
195 | | |
196 | 0 | let p_den = DoubleDouble::mul_add(x8, f2, g0); |
197 | | |
198 | 0 | let z = DoubleDouble::div(p_num, p_den); |
199 | | |
200 | 0 | let r = DoubleDouble::div(z, r_sqrt); |
201 | | |
202 | 0 | let err = r.hi * f64::from_bits(0x3c10000000000000); // 2^-61 |
203 | 0 | let ub = r.hi + (r.lo + err); |
204 | 0 | let lb = r.hi + (r.lo - err); |
205 | 0 | if ub != lb { |
206 | 0 | return k1e_asympt_hard(x); |
207 | 0 | } |
208 | 0 | r.to_f64() |
209 | 0 | } |
210 | | |
211 | | /** |
212 | | Generated by Wolfram Mathematica: |
213 | | ```text |
214 | | <<FunctionApproximations` |
215 | | ClearAll["Global`*"] |
216 | | f[x_]:=Sqrt[x] Exp[x] BesselK[1,x] |
217 | | g[z_]:=f[1/z] |
218 | | {err,approx}=MiniMaxApproximation[g[z],{z,{0.0000000000001,1},14,14},WorkingPrecision->70] |
219 | | poly=Numerator[approx][[1]]; |
220 | | coeffs=CoefficientList[poly,z]; |
221 | | TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
222 | | poly=Denominator[approx][[1]]; |
223 | | coeffs=CoefficientList[poly,z]; |
224 | | TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
225 | | ``` |
226 | | **/ |
227 | | #[cold] |
228 | | #[inline(never)] |
229 | 0 | fn k1e_asympt_hard(x: f64) -> f64 { |
230 | | static P: [DyadicFloat128; 15] = [ |
231 | | DyadicFloat128 { |
232 | | sign: DyadicSign::Pos, |
233 | | exponent: -127, |
234 | | mantissa: 0xa06c98ff_b1382cb2_be5210ac_f26f25d1_u128, |
235 | | }, |
236 | | DyadicFloat128 { |
237 | | sign: DyadicSign::Pos, |
238 | | exponent: -122, |
239 | | mantissa: 0xc5f546cb_659a39d0_fafbd188_36ca05b9_u128, |
240 | | }, |
241 | | DyadicFloat128 { |
242 | | sign: DyadicSign::Pos, |
243 | | exponent: -118, |
244 | | mantissa: 0xcd0b7cfa_de158d26_7084bbe9_f1bdb66d_u128, |
245 | | }, |
246 | | DyadicFloat128 { |
247 | | sign: DyadicSign::Pos, |
248 | | exponent: -115, |
249 | | mantissa: 0xeac7be2f_957d1260_8849508a_2a5a8972_u128, |
250 | | }, |
251 | | DyadicFloat128 { |
252 | | sign: DyadicSign::Pos, |
253 | | exponent: -112, |
254 | | mantissa: 0xa4d14fec_fecc6444_4c7b0287_dad71a86_u128, |
255 | | }, |
256 | | DyadicFloat128 { |
257 | | sign: DyadicSign::Pos, |
258 | | exponent: -110, |
259 | | mantissa: 0x94e3180c_01df9932_ad2acd8b_bab59c05_u128, |
260 | | }, |
261 | | DyadicFloat128 { |
262 | | sign: DyadicSign::Pos, |
263 | | exponent: -109, |
264 | | mantissa: 0xb0de10f8_74918442_94a96368_8eaa4d0d_u128, |
265 | | }, |
266 | | DyadicFloat128 { |
267 | | sign: DyadicSign::Pos, |
268 | | exponent: -108, |
269 | | mantissa: 0x8adfea76_d6dbe5d9_46bfaf83_9341f4b5_u128, |
270 | | }, |
271 | | DyadicFloat128 { |
272 | | sign: DyadicSign::Pos, |
273 | | exponent: -108, |
274 | | mantissa: 0x8f0a4337_b69b602c_cf187222_f3a3379f_u128, |
275 | | }, |
276 | | DyadicFloat128 { |
277 | | sign: DyadicSign::Pos, |
278 | | exponent: -109, |
279 | | mantissa: 0xbd4c3ebf_c2db0fad_1b425641_cc470043_u128, |
280 | | }, |
281 | | DyadicFloat128 { |
282 | | sign: DyadicSign::Pos, |
283 | | exponent: -110, |
284 | | mantissa: 0x9b14d29f_9b97e3c8_c1a7b9d0_787f0ddb_u128, |
285 | | }, |
286 | | DyadicFloat128 { |
287 | | sign: DyadicSign::Pos, |
288 | | exponent: -112, |
289 | | mantissa: 0x93e670d2_07a553ef_a90d4895_cf1b5011_u128, |
290 | | }, |
291 | | DyadicFloat128 { |
292 | | sign: DyadicSign::Pos, |
293 | | exponent: -115, |
294 | | mantissa: 0x93e0ee0a_cb4d8910_6b4d3e37_f4f9df49_u128, |
295 | | }, |
296 | | DyadicFloat128 { |
297 | | sign: DyadicSign::Pos, |
298 | | exponent: -120, |
299 | | mantissa: 0xff0ce10d_5585abd1_e8a53a12_65131ad4_u128, |
300 | | }, |
301 | | DyadicFloat128 { |
302 | | sign: DyadicSign::Pos, |
303 | | exponent: -126, |
304 | | mantissa: 0xf020536d_822cbe51_c8de095a_03367c83_u128, |
305 | | }, |
306 | | ]; |
307 | | |
308 | | static Q: [DyadicFloat128; 15] = [ |
309 | | DyadicFloat128 { |
310 | | sign: DyadicSign::Pos, |
311 | | exponent: -127, |
312 | | mantissa: 0x80000000_00000000_00000000_00000000_u128, |
313 | | }, |
314 | | DyadicFloat128 { |
315 | | sign: DyadicSign::Pos, |
316 | | exponent: -122, |
317 | | mantissa: 0x9c729dd5_4828a918_42807f58_d485a511_u128, |
318 | | }, |
319 | | DyadicFloat128 { |
320 | | sign: DyadicSign::Pos, |
321 | | exponent: -118, |
322 | | mantissa: 0x9ff6f631_0794001d_433ab0c5_d4c682a9_u128, |
323 | | }, |
324 | | DyadicFloat128 { |
325 | | sign: DyadicSign::Pos, |
326 | | exponent: -115, |
327 | | mantissa: 0xb3f81e8b_1e0e85a6_3928342e_c83088a1_u128, |
328 | | }, |
329 | | DyadicFloat128 { |
330 | | sign: DyadicSign::Pos, |
331 | | exponent: -113, |
332 | | mantissa: 0xf6b1c203_a60d4294_239ad045_2c67c224_u128, |
333 | | }, |
334 | | DyadicFloat128 { |
335 | | sign: DyadicSign::Pos, |
336 | | exponent: -111, |
337 | | mantissa: 0xd7a98b14_7a499762_abde5c38_3a5b40e4_u128, |
338 | | }, |
339 | | DyadicFloat128 { |
340 | | sign: DyadicSign::Pos, |
341 | | exponent: -110, |
342 | | mantissa: 0xf4eb8b77_a2cdc686_afd1273f_d464c8b7_u128, |
343 | | }, |
344 | | DyadicFloat128 { |
345 | | sign: DyadicSign::Pos, |
346 | | exponent: -109, |
347 | | mantissa: 0xb4c1e12a_93ee86fc_930c6f94_cfa6ac3a_u128, |
348 | | }, |
349 | | DyadicFloat128 { |
350 | | sign: DyadicSign::Pos, |
351 | | exponent: -109, |
352 | | mantissa: 0xaaeaab88_32b776b7_fdd76b0f_24349f41_u128, |
353 | | }, |
354 | | DyadicFloat128 { |
355 | | sign: DyadicSign::Pos, |
356 | | exponent: -110, |
357 | | mantissa: 0xc8ec9d61_5bf2ee9b_878b4962_4a5cee85_u128, |
358 | | }, |
359 | | DyadicFloat128 { |
360 | | sign: DyadicSign::Pos, |
361 | | exponent: -111, |
362 | | mantissa: 0x8b97bab0_3351673f_22f10d40_fd1c9ff3_u128, |
363 | | }, |
364 | | DyadicFloat128 { |
365 | | sign: DyadicSign::Pos, |
366 | | exponent: -114, |
367 | | mantissa: 0xd31cb80a_bf8cbedc_b0dcf7e7_c599f79e_u128, |
368 | | }, |
369 | | DyadicFloat128 { |
370 | | sign: DyadicSign::Pos, |
371 | | exponent: -117, |
372 | | mantissa: 0x96b354c8_69197193_ea4f608f_81943988_u128, |
373 | | }, |
374 | | DyadicFloat128 { |
375 | | sign: DyadicSign::Pos, |
376 | | exponent: -122, |
377 | | mantissa: 0x989af1bb_e48b5c44_7cd09746_f15e935a_u128, |
378 | | }, |
379 | | DyadicFloat128 { |
380 | | sign: DyadicSign::Pos, |
381 | | exponent: -130, |
382 | | mantissa: 0xb7b51326_23c29ed5_8d3dcf5a_79bd9a4f_u128, |
383 | | }, |
384 | | ]; |
385 | | |
386 | 0 | let recip = DyadicFloat128::accurate_reciprocal(x); |
387 | 0 | let r_sqrt = bessel_rsqrt_hard(x, recip); |
388 | | |
389 | 0 | let mut p0 = P[14]; |
390 | 0 | for i in (0..14).rev() { |
391 | 0 | p0 = recip * p0 + P[i]; |
392 | 0 | } |
393 | | |
394 | 0 | let mut q0 = Q[14]; |
395 | 0 | for i in (0..14).rev() { |
396 | 0 | q0 = recip * q0 + Q[i]; |
397 | 0 | } |
398 | | |
399 | 0 | let v = p0 * q0.reciprocal(); |
400 | 0 | let r = v * r_sqrt; |
401 | 0 | r.fast_as_f64() |
402 | 0 | } |
403 | | |
404 | | #[cfg(test)] |
405 | | mod tests { |
406 | | use super::*; |
407 | | #[test] |
408 | | fn test_k1() { |
409 | | assert_eq!(f_k1e(0.643), 2.253195748291852); |
410 | | assert_eq!(f_k1e(0.964), 1.6787831013451477); |
411 | | assert_eq!(f_k1e(2.964), 0.8123854795542738); |
412 | | assert_eq!(f_k1e(8.43), 0.4502184086111872); |
413 | | assert_eq!(f_k1e(16.43), 0.3161307996938612); |
414 | | assert_eq!(f_k1e(423.43), 0.06096117017402597); |
415 | | assert_eq!(f_k1e(9044.431), 0.01317914752085687); |
416 | | assert_eq!(k1e_asympt_hard(16.43), 0.3161307996938612); |
417 | | assert_eq!(k1e_asympt_hard(423.43), 0.06096117017402597); |
418 | | assert_eq!(k1e_asympt_hard(9044.431), 0.01317914752085687); |
419 | | assert_eq!(f_k1e(0.), f64::INFINITY); |
420 | | assert_eq!(f_k1e(-0.), f64::INFINITY); |
421 | | assert!(f_k1e(-0.5).is_nan()); |
422 | | assert!(f_k1e(f64::NEG_INFINITY).is_nan()); |
423 | | assert_eq!(f_k1e(f64::INFINITY), 0.); |
424 | | } |
425 | | } |