pxfm/bessel/alpha0.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//
34/// See [bessel_0_asympt_alpha] for the info
35pub(crate) fn bessel_0_asympt_alpha_hard(reciprocal: DyadicFloat128) -> DyadicFloat128 {
36 static C: [DyadicFloat128; 18] = [
37 DyadicFloat128 {
38 sign: DyadicSign::Pos,
39 exponent: -130,
40 mantissa: 0x80000000_00000000_00000000_00000000_u128,
41 },
42 DyadicFloat128 {
43 sign: DyadicSign::Neg,
44 exponent: -131,
45 mantissa: 0x85555555_55555555_55555555_55555555_u128,
46 },
47 DyadicFloat128 {
48 sign: DyadicSign::Pos,
49 exponent: -130,
50 mantissa: 0xd6999999_99999999_99999999_9999999a_u128,
51 },
52 DyadicFloat128 {
53 sign: DyadicSign::Neg,
54 exponent: -127,
55 mantissa: 0xd1ac2492_49249249_24924924_92492492_u128,
56 },
57 DyadicFloat128 {
58 sign: DyadicSign::Pos,
59 exponent: -123,
60 mantissa: 0xbbcd0fc7_1c71c71c_71c71c71_c71c71c7_u128,
61 },
62 DyadicFloat128 {
63 sign: DyadicSign::Neg,
64 exponent: -118,
65 mantissa: 0x85e8fe45_8ba2e8ba_2e8ba2e8_ba2e8ba3_u128,
66 },
67 DyadicFloat128 {
68 sign: DyadicSign::Pos,
69 exponent: -113,
70 mantissa: 0x8b5a8f33_63c4ec4e_c4ec4ec4_ec4ec4ec_u128,
71 },
72 DyadicFloat128 {
73 sign: DyadicSign::Neg,
74 exponent: -108,
75 mantissa: 0xc7661d79_9d59b555_55555555_55555555_u128,
76 },
77 DyadicFloat128 {
78 sign: DyadicSign::Pos,
79 exponent: -102,
80 mantissa: 0xbbced715_c2897a28_78787878_78787878_u128,
81 },
82 DyadicFloat128 {
83 sign: DyadicSign::Neg,
84 exponent: -96,
85 mantissa: 0xe14b19b4_aae3f7fe_be1af286_bca1af28_u128,
86 },
87 DyadicFloat128 {
88 sign: DyadicSign::Pos,
89 exponent: -89,
90 mantissa: 0xa7af7341_db2192db_975e0c30_c30c30c3_u128,
91 },
92 DyadicFloat128 {
93 sign: DyadicSign::Neg,
94 exponent: -82,
95 mantissa: 0x97a8f676_b349f6fc_5cefd338_590b2164_u128,
96 },
97 DyadicFloat128 {
98 sign: DyadicSign::Pos,
99 exponent: -75,
100 mantissa: 0xa3d299fb_6f304d73_86e15f12_0fd70a3d_u128,
101 },
102 DyadicFloat128 {
103 sign: DyadicSign::Neg,
104 exponent: -68,
105 mantissa: 0xd050b737_cbc044ef_e8807e3c_87f43da1_u128,
106 },
107 DyadicFloat128 {
108 sign: DyadicSign::Pos,
109 exponent: -60,
110 mantissa: 0x9a02379b_daa7e492_854f42de_6d3dffe6_u128,
111 },
112 DyadicFloat128 {
113 sign: DyadicSign::Neg,
114 exponent: -52,
115 mantissa: 0x83011a39_380e467d_de6b70ec_b92ce0cc_u128,
116 },
117 DyadicFloat128 {
118 sign: DyadicSign::Pos,
119 exponent: -45,
120 mantissa: 0xfe16521f_c79e5d9a_a5bed653_e3844e9a_u128,
121 },
122 DyadicFloat128 {
123 sign: DyadicSign::Neg,
124 exponent: -36,
125 mantissa: 0x8b54b13d_3fb3e1c4_15dbb880_0bb32218_u128,
126 },
127 ];
128
129 let x2 = reciprocal * reciprocal;
130
131 let mut p = C[17];
132 for i in (0..17).rev() {
133 p = x2 * p + C[i];
134 }
135
136 p * reciprocal
137}
138
139/**
140Note expansion generation below: this is negative series expressed in Sage as positive,
141so before any real evaluation `x=1/x` should be applied.
142
143Generated by SageMath:
144```python
145def binomial_like(n, m):
146 prod = QQ(1)
147 z = QQ(4)*(n**2)
148 for k in range(1,m + 1):
149 prod *= (z - (2*k - 1)**2)
150 return prod / (QQ(2)**(2*m) * (ZZ(m).factorial()))
151
152R = LaurentSeriesRing(RealField(300), 'x',default_prec=300)
153x = R.gen()
154
155def Pn_asymptotic(n, y, terms=10):
156 # now y = 1/x
157 return sum( (-1)**m * binomial_like(n, 2*m) / (QQ(2)**(2*m)) * y**(QQ(2)*m) for m in range(terms) )
158
159def Qn_asymptotic(n, y, terms=10):
160 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) )
161
162P = Pn_asymptotic(0, x, 50)
163Q = Qn_asymptotic(0, x, 50)
164
165R_series = (-Q/P)
166
167# alpha is atan(R_series) so we're doing Taylor series atan expansion on R_series
168
169arctan_series_Z = sum([QQ(-1)**k * x**(QQ(2)*k+1) / RealField(700)(RealField(700)(2)*k+1) for k in range(25)])
170alpha_series = arctan_series_Z(R_series)
171
172# see the series
173print(alpha_series)
174```
175**/
176#[inline]
177pub(crate) fn bessel_0_asympt_alpha(recip: DoubleDouble) -> DoubleDouble {
178 const C: [(u64, u64); 12] = [
179 (0x0000000000000000, 0x3fc0000000000000),
180 (0x3c55555555555555, 0xbfb0aaaaaaaaaaab),
181 (0x3c5999999999999a, 0x3fcad33333333333),
182 (0xbc92492492492492, 0xbffa358492492492),
183 (0xbcbc71c71c71c71c, 0x403779a1f8e38e39),
184 (0xbd0745d1745d1746, 0xc080bd1fc8b1745d),
185 (0xbd7d89d89d89d89e, 0x40d16b51e66c789e),
186 (0x3dc5555555555555, 0xc128ecc3af33ab37),
187 (0x3e2143c3c3c3c3c4, 0x418779dae2b8512f),
188 (0x3df41e50d79435e5, 0xc1ec296336955c7f),
189 (0x3ef6dcbaf0618618, 0x4254f5ee683b6432),
190 (0x3f503a3102cc7a6f, 0xc2c2f51eced6693f),
191 ];
192
193 // Doing (1/x)*(1/x) instead (1/(x*x)) to avoid spurious overflow/underflow
194 let x2 = DoubleDouble::quick_mult(recip, recip);
195
196 let mut p = DoubleDouble::mul_add(
197 x2,
198 DoubleDouble::from_bit_pair(C[11]),
199 DoubleDouble::from_bit_pair(C[10]),
200 );
201
202 p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[9]));
203 p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[8]));
204 p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[7]));
205 p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[6]));
206 p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[5]));
207 p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[4]));
208 p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[3]));
209 p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[2]));
210 p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[1]));
211 p = DoubleDouble::mul_add_f64(x2, p, f64::from_bits(C[0].1));
212
213 let z = DoubleDouble::quick_mult(p, recip);
214
215 DoubleDouble::from_exact_add(z.hi, z.lo)
216}
217
218/**
219Note expansion generation below: this is negative series expressed in Sage as positive,
220so before any real evaluation `x=1/x` should be applied.
221
222Generated by SageMath:
223```python
224def binomial_like(n, m):
225 prod = QQ(1)
226 z = QQ(4)*(n**2)
227 for k in range(1,m + 1):
228 prod *= (z - (2*k - 1)**2)
229 return prod / (QQ(2)**(2*m) * (ZZ(m).factorial()))
230
231R = LaurentSeriesRing(RealField(300), 'x',default_prec=300)
232x = R.gen()
233
234def Pn_asymptotic(n, y, terms=10):
235 # now y = 1/x
236 return sum( (-1)**m * binomial_like(n, 2*m) / (QQ(2)**(2*m)) * y**(QQ(2)*m) for m in range(terms) )
237
238def Qn_asymptotic(n, y, terms=10):
239 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) )
240
241P = Pn_asymptotic(0, x, 50)
242Q = Qn_asymptotic(0, x, 50)
243
244R_series = (-Q/P)
245
246# alpha is atan(R_series) so we're doing Taylor series atan expansion on R_series
247
248arctan_series_Z = sum([QQ(-1)**k * x**(QQ(2)*k+1) / RealField(700)(RealField(700)(2)*k+1) for k in range(25)])
249alpha_series = arctan_series_Z(R_series)
250
251# see the series
252print(alpha_series)
253```
254**/
255#[inline]
256pub(crate) fn bessel_0_asympt_alpha_fast(recip: DoubleDouble) -> DoubleDouble {
257 const C: [u64; 12] = [
258 0x3fc0000000000000,
259 0xbfb0aaaaaaaaaaab,
260 0x3fcad33333333333,
261 0xbffa358492492492,
262 0x403779a1f8e38e39,
263 0xc080bd1fc8b1745d,
264 0x40d16b51e66c789e,
265 0xc128ecc3af33ab37,
266 0x418779dae2b8512f,
267 0xc1ec296336955c7f,
268 0x4254f5ee683b6432,
269 0xc2c2f51eced6693f,
270 ];
271
272 // Doing (1/x)*(1/x) instead (1/(x*x)) to avoid spurious overflow/underflow
273 let x2 = DoubleDouble::quick_mult(recip, recip);
274
275 let p = f_polyeval9(
276 x2.hi,
277 f64::from_bits(C[3]),
278 f64::from_bits(C[4]),
279 f64::from_bits(C[5]),
280 f64::from_bits(C[6]),
281 f64::from_bits(C[7]),
282 f64::from_bits(C[8]),
283 f64::from_bits(C[9]),
284 f64::from_bits(C[10]),
285 f64::from_bits(C[11]),
286 );
287
288 let mut z = DoubleDouble::mul_f64_add_f64(x2, p, f64::from_bits(C[2]));
289 z = DoubleDouble::mul_add_f64(x2, z, f64::from_bits(C[1]));
290 z = DoubleDouble::mul_add_f64(x2, z, f64::from_bits(C[0]));
291 DoubleDouble::quick_mult(z, recip)
292}