pxfm/bessel/
alpha1.rs

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 */
29use crate::double_double::DoubleDouble;
30use crate::dyadic_float::{DyadicFloat128, DyadicSign};
31use crate::polyeval::f_polyeval9;
32
33/**
34Note expansion generation below: this is negative series expressed in Sage as positive,
35so before any real evaluation `x=1/x` should be applied.
36
37Generated by SageMath:
38```python
39def binomial_like(n, m):
40    prod = QQ(1)
41    z = QQ(4)*(n**2)
42    for k in range(1,m + 1):
43        prod *= (z - (2*k - 1)**2)
44    return prod / (QQ(2)**(2*m) * (ZZ(m).factorial()))
45
46R = LaurentSeriesRing(RealField(300), 'x',default_prec=300)
47x = R.gen()
48
49def Pn_asymptotic(n, y, terms=10):
50    # now y = 1/x
51    return sum( (-1)**m * binomial_like(n, 2*m) / (QQ(2)**(2*m)) * y**(QQ(2)*m) for m in range(terms) )
52
53def Qn_asymptotic(n, y, terms=10):
54    return sum( (-1)**m * binomial_like(n, 2*m + 1) / (QQ(2)**(2*m + 1)) * y**(QQ(2)*m + 1) for m in range(terms) )
55
56P = Pn_asymptotic(1, x, 50)
57Q = Qn_asymptotic(1, x, 50)
58
59R_series = (-Q/P)
60
61# alpha is atan(R_series) so we're doing Taylor series atan expansion on R_series
62
63arctan_series_Z = sum([QQ(-1)**k * x**(QQ(2)*k+1) / RealField(700)(RealField(700)(2)*k+1) for k in range(25)])
64alpha_series = arctan_series_Z(R_series)
65
66# see the series
67print(alpha_series)
68```
69
70See notes/bessel_asympt.ipynb for generation
71**/
72#[inline]
73pub(crate) fn bessel_1_asympt_alpha_fast(recip: DoubleDouble) -> DoubleDouble {
74    const C: [u64; 12] = [
75        0xbfd8000000000000,
76        0x3fc5000000000000,
77        0xbfd7bccccccccccd,
78        0x4002f486db6db6db,
79        0xc03e9fbf40000000,
80        0x4084997b55945d17,
81        0xc0d4a914195269d9,
82        0x412cd1b53816aec1,
83        0xc18aa4095d419351,
84        0x41ef809305f11b9d,
85        0xc2572e6809ed618b,
86        0x42c4c5b6057839f9,
87    ];
88
89    // Doing (1/x)*(1/x) instead (1/(x*x)) to avoid spurious overflow/underflow
90    let x2 = DoubleDouble::quick_mult(recip, recip);
91
92    let p = f_polyeval9(
93        x2.hi,
94        f64::from_bits(C[3]),
95        f64::from_bits(C[4]),
96        f64::from_bits(C[5]),
97        f64::from_bits(C[6]),
98        f64::from_bits(C[7]),
99        f64::from_bits(C[8]),
100        f64::from_bits(C[9]),
101        f64::from_bits(C[10]),
102        f64::from_bits(C[11]),
103    );
104
105    let mut z = DoubleDouble::mul_f64_add_f64(x2, p, f64::from_bits(C[2]));
106    z = DoubleDouble::mul_add_f64(x2, z, f64::from_bits(C[1]));
107    z = DoubleDouble::mul_add_f64(x2, z, f64::from_bits(C[0]));
108    DoubleDouble::quick_mult(z, recip)
109}
110
111/**
112Note expansion generation below: this is negative series expressed in Sage as positive,
113so before any real evaluation `x=1/x` should be applied.
114
115Generated by SageMath:
116```python
117def binomial_like(n, m):
118    prod = QQ(1)
119    z = QQ(4)*(n**2)
120    for k in range(1,m + 1):
121        prod *= (z - (2*k - 1)**2)
122    return prod / (QQ(2)**(2*m) * (ZZ(m).factorial()))
123
124R = LaurentSeriesRing(RealField(300), 'x',default_prec=300)
125x = R.gen()
126
127def Pn_asymptotic(n, y, terms=10):
128    # now y = 1/x
129    return sum( (-1)**m * binomial_like(n, 2*m) / (QQ(2)**(2*m)) * y**(QQ(2)*m) for m in range(terms) )
130
131def Qn_asymptotic(n, y, terms=10):
132    return sum( (-1)**m * binomial_like(n, 2*m + 1) / (QQ(2)**(2*m + 1)) * y**(QQ(2)*m + 1) for m in range(terms) )
133
134P = Pn_asymptotic(1, x, 50)
135Q = Qn_asymptotic(1, x, 50)
136
137R_series = (-Q/P)
138
139# alpha is atan(R_series) so we're doing Taylor series atan expansion on R_series
140
141arctan_series_Z = sum([QQ(-1)**k * x**(QQ(2)*k+1) / RealField(700)(RealField(700)(2)*k+1) for k in range(25)])
142alpha_series = arctan_series_Z(R_series)
143
144# see the series
145print(alpha_series)
146```
147
148See notes/bessel_asympt.ipynb for generation
149**/
150#[inline]
151pub(crate) fn bessel_1_asympt_alpha(recip: DoubleDouble) -> DoubleDouble {
152    const C: [(u64, u64); 12] = [
153        (0x0000000000000000, 0xbfd8000000000000),
154        (0x0000000000000000, 0x3fc5000000000000),
155        (0x3c6999999999999a, 0xbfd7bccccccccccd),
156        (0x3cab6db6db6db6db, 0x4002f486db6db6db),
157        (0x0000000000000000, 0xc03e9fbf40000000),
158        (0x3d21745d1745d174, 0x4084997b55945d17),
159        (0x3d789d89d89d89d9, 0xc0d4a914195269d9),
160        (0xbdb999999999999a, 0x412cd1b53816aec1),
161        (0xbdfe5a5a5a5a5a5a, 0xc18aa4095d419351),
162        (0x3e7e0ca50d79435e, 0x41ef809305f11b9d),
163        (0xbedff8b720000000, 0xc2572e6809ed618b),
164        (0xbf64e5d8ae68b7a7, 0x42c4c5b6057839f9),
165    ];
166
167    // Doing (1/x)*(1/x) instead (1/(x*x)) to avoid spurious overflow/underflow
168    let x2 = DoubleDouble::quick_mult(recip, recip);
169
170    let mut p = DoubleDouble::mul_add(
171        x2,
172        DoubleDouble::from_bit_pair(C[11]),
173        DoubleDouble::from_bit_pair(C[10]),
174    );
175
176    p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[9]));
177    p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[8]));
178    p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[7]));
179    p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[6]));
180    p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[5]));
181    p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[4]));
182    p = DoubleDouble::mul_add_f64(x2, p, f64::from_bits(C[3].1));
183    p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[2]));
184    p = DoubleDouble::mul_add_f64(x2, p, f64::from_bits(C[1].1));
185    p = DoubleDouble::mul_add_f64(x2, p, f64::from_bits(C[0].1));
186
187    let z = DoubleDouble::quick_mult(p, recip);
188
189    DoubleDouble::from_exact_add(z.hi, z.lo)
190}
191
192//
193/// See [bessel_1_asympt_alpha] for the info
194pub(crate) fn bessel_1_asympt_alpha_hard(reciprocal: DyadicFloat128) -> DyadicFloat128 {
195    static C: [DyadicFloat128; 18] = [
196        DyadicFloat128 {
197            sign: DyadicSign::Neg,
198            exponent: -129,
199            mantissa: 0xc0000000_00000000_00000000_00000000_u128,
200        },
201        DyadicFloat128 {
202            sign: DyadicSign::Pos,
203            exponent: -130,
204            mantissa: 0xa8000000_00000000_00000000_00000000_u128,
205        },
206        DyadicFloat128 {
207            sign: DyadicSign::Neg,
208            exponent: -129,
209            mantissa: 0xbde66666_66666666_66666666_66666666_u128,
210        },
211        DyadicFloat128 {
212            sign: DyadicSign::Pos,
213            exponent: -126,
214            mantissa: 0x97a436db_6db6db6d_b6db6db6_db6db6db_u128,
215        },
216        DyadicFloat128 {
217            sign: DyadicSign::Neg,
218            exponent: -123,
219            mantissa: 0xf4fdfa00_00000000_00000000_00000000_u128,
220        },
221        DyadicFloat128 {
222            sign: DyadicSign::Pos,
223            exponent: -118,
224            mantissa: 0xa4cbdaac_a2e8ba2e_8ba2e8ba_2e8ba2e9_u128,
225        },
226        DyadicFloat128 {
227            sign: DyadicSign::Neg,
228            exponent: -113,
229            mantissa: 0xa548a0ca_934ec4ec_4ec4ec4e_c4ec4ec5_u128,
230        },
231        DyadicFloat128 {
232            sign: DyadicSign::Pos,
233            exponent: -108,
234            mantissa: 0xe68da9c0_b5760666_66666666_66666666_u128,
235        },
236        DyadicFloat128 {
237            sign: DyadicSign::Neg,
238            exponent: -102,
239            mantissa: 0xd5204aea_0c9a8879_69696969_69696969_u128,
240        },
241        DyadicFloat128 {
242            sign: DyadicSign::Pos,
243            exponent: -96,
244            mantissa: 0xfc04982f_88dce9e0_ca50d794_35e50d79_u128,
245        },
246        DyadicFloat128 {
247            sign: DyadicSign::Neg,
248            exponent: -89,
249            mantissa: 0xb973404f_6b0c58ff_c5b90000_00000000_u128,
250        },
251        DyadicFloat128 {
252            sign: DyadicSign::Pos,
253            exponent: -82,
254            mantissa: 0xa62db02b_c1cfc563_44ea32e9_0b21642d_u128,
255        },
256        DyadicFloat128 {
257            sign: DyadicSign::Neg,
258            exponent: -75,
259            mantissa: 0xb220e7ff_443c1584_7e85f4e0_55eb851f_u128,
260        },
261        DyadicFloat128 {
262            sign: DyadicSign::Pos,
263            exponent: -68,
264            mantissa: 0xe10a255c_ca5e68cc_00c2d6c0_acdc8000_u128,
265        },
266        DyadicFloat128 {
267            sign: DyadicSign::Neg,
268            exponent: -60,
269            mantissa: 0xa573790c_5186f23b_5db502ea_d9fa5432_u128,
270        },
271        DyadicFloat128 {
272            sign: DyadicSign::Pos,
273            exponent: -52,
274            mantissa: 0x8c0ffedc_407a7015_453df84e_9c3f1d39_u128,
275        },
276        DyadicFloat128 {
277            sign: DyadicSign::Neg,
278            exponent: -44,
279            mantissa: 0x874226ed_c298a17a_d8c49a4e_dc9281a5_u128,
280        },
281        DyadicFloat128 {
282            sign: DyadicSign::Pos,
283            exponent: -36,
284            mantissa: 0x93cab36c_9ab9495c_310fa9cd_4b065359_u128,
285        },
286    ];
287
288    let x2 = reciprocal * reciprocal;
289
290    let mut p = C[17];
291    for i in (0..17).rev() {
292        p = x2 * p + C[i];
293    }
294
295    p * reciprocal
296}