/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pxfm-0.1.25/src/gamma/trigamma.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::common::is_integer; |
30 | | use crate::double_double::DoubleDouble; |
31 | | use crate::sincospi::f_fast_sinpi_dd; |
32 | | |
33 | | // Generated in Wolfram Mathematica: |
34 | | // <<FunctionApproximations` |
35 | | // ClearAll["Global`*"] |
36 | | // f[x_]:=PolyGamma[1, x+1] |
37 | | // {err0,approx}=MiniMaxApproximation[f[z],{z,{-0.99999999,0},11,11},WorkingPrecision->75,MaxIterations->100] |
38 | | // num=Numerator[approx][[1]]; |
39 | | // poly=num; |
40 | | // coeffs=CoefficientList[poly,z]; |
41 | | // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
42 | | static P_1: [(u64, u64); 12] = [ |
43 | | (0x3c81873d89121fec, 0x3ffa51a6625307d3), |
44 | | (0x3cb78bf7af507504, 0x4016a65cbac476ca), |
45 | | (0xbc94179c65e021c2, 0x40218234a0a79582), |
46 | | (0x3cb842a8ab5e0994, 0x401fde32175f8515), |
47 | | (0xbc8768b33f5776b7, 0x4012de6bde49abff), |
48 | | (0x3c8e06354a27f081, 0x3ffe5d6ef3a2eac6), |
49 | | (0xbc8b391d09fed17a, 0x3fe0d186688252cf), |
50 | | (0xbc59a5c46bb8b8cc, 0x3fb958f9a0f156b7), |
51 | | (0x3c2ac44c6a197244, 0x3f88e605f24e1a89), |
52 | | (0x3bd2d05fa8be27f2, 0x3f4cd369f5d68104), |
53 | | (0x3b84ad0a748fdd22, 0x3efde955ebb17874), |
54 | | (0xb96aa8b9a65e0899, 0xbce053d04459ead7), |
55 | | ]; |
56 | | |
57 | | // Generated by Wolfram Mathematica: |
58 | | // <<FunctionApproximations` |
59 | | // ClearAll["Global`*"] |
60 | | // f[x_]:=PolyGamma[1, x+1] |
61 | | // {err0,approx}=MiniMaxApproximation[f[z],{z,{0,3},11,11},WorkingPrecision->75,MaxIterations->100] |
62 | | // num=Numerator[approx][[1]]; |
63 | | // poly=num; |
64 | | // coeffs=CoefficientList[poly,z]; |
65 | | // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
66 | | static P_2: [(u64, u64); 12] = [ |
67 | | (0x3c81873d8912236c, 0x3ffa51a6625307d3), |
68 | | (0xbcbc731a62342288, 0x40176c23f7ea51e6), |
69 | | (0xbcc45a6fd00e67a8, 0x4022cb5ae67657ef), |
70 | | (0x3cc0876fde7fe4e6, 0x4021d766062b9550), |
71 | | (0xbcaec4a4859cba1d, 0x401629f91cd4f291), |
72 | | (0x3c76184014e4d7e3, 0x4002d43da3352004), |
73 | | (0x3c812c7609483e0e, 0x3fe62e3266eef8c7), |
74 | | (0xbc5f991047f52d2b, 0x3fc1eacb910b951c), |
75 | | (0x3c28b9f38d603f2f, 0x3f930960a301df34), |
76 | | (0x3bf9b620eb930504, 0x3f5814f8e057b14b), |
77 | | (0xbb990860b88b54e4, 0x3f0b9f67c71aa3bf), |
78 | | (0x38e5cb6acfbaab77, 0xbc4194b8c01afe9a), |
79 | | ]; |
80 | | |
81 | | // Generated by Wolfram Mathematica: |
82 | | // <<FunctionApproximations` |
83 | | // ClearAll["Global`*"] |
84 | | // f[x_]:=PolyGamma[1, x+1] |
85 | | // {err0,approx,err1}=MiniMaxApproximation[f[z],{z,{3,9},11,11},WorkingPrecision->75,MaxIterations->100] |
86 | | // num=Numerator[approx]; |
87 | | // poly=num; |
88 | | // coeffs=CoefficientList[poly,z]; |
89 | | // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
90 | | static P_3: [(u64, u64); 12] = [ |
91 | | (0x3c9cd56dbb295efc, 0x3ffa51a662556db9), |
92 | | (0x3c9f4ee74f5f9daf, 0x4018ff913088cb34), |
93 | | (0x3ccf08737350609c, 0x402593d55686b8b1), |
94 | | (0xbcc6cd4ed33afebb, 0x402641d10de4def5), |
95 | | (0xbcb24d1957c1303c, 0x401e682c37e8e2cf), |
96 | | (0x3ca30ac79162ceb2, 0x400ccfc7c4566f55), |
97 | | (0x3c9efea5ff293dc9, 0x3ff33eb2c6e89d0b), |
98 | | (0x3c74670a11068abc, 0x3fd1fbf456e5c6f0), |
99 | | (0x3c47b5dcdea19c36, 0x3fa6a6a2148c482c), |
100 | | (0xbc14642012a1cc1e, 0x3f71851e927f52e7), |
101 | | (0x3bc7db88a4ec5478, 0x3f29a45059a43475), |
102 | | (0xb7bc31e55271eab0, 0xbb375518529c52fb), |
103 | | ]; |
104 | | |
105 | | // Generated in Wolfram Mathematica: |
106 | | // <<FunctionApproximations` |
107 | | // ClearAll["Global`*"] |
108 | | // f[x_]:=PolyGamma[1, x+1] |
109 | | // {err0,approx}=MiniMaxApproximation[f[z],{z,{-0.99999999,0},11,11},WorkingPrecision->75,MaxIterations->100] |
110 | | // den=Denominator[approx][[1]]; |
111 | | // poly=den; |
112 | | // coeffs=CoefficientList[poly,z]; |
113 | | // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
114 | | static Q_1: [(u64, u64); 12] = [ |
115 | | (0x0000000000000000, 0x3ff0000000000000), |
116 | | (0xbcb84c43a11fc28a, 0x40139d9587da0fb5), |
117 | | (0x3ca1cf3dbcddbb57, 0x402507cb6225f0f0), |
118 | | (0x3cb01aa6ddcc3cfd, 0x402a1b416d0ed4e6), |
119 | | (0xbcbc31c216b5ff66, 0x4024ec8829e535d4), |
120 | | (0xbcb335c23022f43e, 0x4016d2ba6d1a18e6), |
121 | | (0x3cafbfffc03ad28a, 0x400158c4611ed51f), |
122 | | (0xbc8d1fb10a031a27, 0x3fe26f15bb52f89b), |
123 | | (0x3c56a9fea160eecb, 0x3fbaec13f663049d), |
124 | | (0xbc2f4ee869ba9364, 0x3f89cde0500cd68f), |
125 | | (0x3be3f23afc9398b6, 0x3f4d4b0f4dcf3eb8), |
126 | | (0x3b67a3ed4795d33e, 0x3efde955eafac9c2), |
127 | | ]; |
128 | | |
129 | | // Generated by Wolfram Mathematica: |
130 | | // <<FunctionApproximations` |
131 | | // ClearAll["Global`*"] |
132 | | // f[x_]:=PolyGamma[1, x+1] |
133 | | // {err0,approx}=MiniMaxApproximation[f[z],{z,{0,3},11,11},WorkingPrecision->75,MaxIterations->100] |
134 | | // den=Denominator[approx][[1]]; |
135 | | // poly=den; |
136 | | // coeffs=CoefficientList[poly,z]; |
137 | | // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
138 | | static Q_2: [(u64, u64); 12] = [ |
139 | | (0x0000000000000000, 0x3ff0000000000000), |
140 | | (0xbc81a3e4e026b7b1, 0x401415d1a20a9339), |
141 | | (0x3cc279576dfe3ec9, 0x402627c1a95d33d2), |
142 | | (0x3c9a94b5cf0cae88, 0x402c724dc5cf4577), |
143 | | (0xbc8aa1fa0c3820a8, 0x4027b7a332bb07f4), |
144 | | (0xbc96968367088d66, 0x401b14376177bdd7), |
145 | | (0x3ca2d3dfa5847f4d, 0x4005b0511cd98f2c), |
146 | | (0xbc8cfad394d41dd1, 0x3fe877bc2d02c7f3), |
147 | | (0xbc51592b8ec81a92, 0x3fc31f52afc72b95), |
148 | | (0x3c2cbef277d587e9, 0x3f93cb2f0e574376), |
149 | | (0xbbfbb670fd94f6ba, 0x3f5883767f745a92), |
150 | | (0xbb931b04d74e5893, 0x3f0b9f67c71a60f3), |
151 | | ]; |
152 | | |
153 | | // Generated by Wolfram Mathematica: |
154 | | // <<FunctionApproximations` |
155 | | // ClearAll["Global`*"] |
156 | | // f[x_]:=PolyGamma[1, x+1] |
157 | | // {err0,approx,err1}=MiniMaxApproximation[f[z],{z,{3,9},11,11},WorkingPrecision->75,MaxIterations->100] |
158 | | // den=Denominator[approx]; |
159 | | // poly=den; |
160 | | // coeffs=CoefficientList[poly,z]; |
161 | | // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
162 | | static Q_3: [(u64, u64); 12] = [ |
163 | | (0x0000000000000000, 0x3ff0000000000000), |
164 | | (0x3cbafcb4b6d646d9, 0x40150b12a79fc9cf), |
165 | | (0x3c989ef814b8dd2a, 0x40288c1d26ffdca5), |
166 | | (0x3cb0282cfea9c473, 0x4030d737c893cd5f), |
167 | | (0x3cc955b8aaadb37d, 0x402e5c289b6de3e0), |
168 | | (0x3cb377161f8861d2, 0x4022fb66d87bd522), |
169 | | (0xbcb4b0e4cff46ad6, 0x4010e5d13c2a5907), |
170 | | (0xbc8824539e4b1bd6, 0x3ff58c8fe8f26fca), |
171 | | (0xbc7d34220d810ea0, 0x3fd36c1351f43e66), |
172 | | (0xbc4cbdbe85570017, 0x3fa7c1170466605e), |
173 | | (0xbc0c3afb98775c53, 0x3f71ebafd3e5e3b9), |
174 | | (0x3bc0b0b7f16afd0a, 0x3f29a45059a43475), |
175 | | ]; |
176 | | |
177 | | #[inline] |
178 | 0 | fn approx_trigamma(x: f64) -> DoubleDouble { |
179 | 0 | if x <= 10. { |
180 | 0 | let (p, q) = if x <= 1. { |
181 | 0 | (&P_1, &Q_1) |
182 | 0 | } else if x <= 4. { |
183 | 0 | (&P_2, &Q_2) |
184 | | } else { |
185 | 0 | (&P_3, &Q_3) |
186 | | }; |
187 | 0 | let x2 = DoubleDouble::from_exact_mult(x, x); |
188 | 0 | let x4 = DoubleDouble::quick_mult(x2, x2); |
189 | 0 | let x8 = DoubleDouble::quick_mult(x4, x4); |
190 | | |
191 | 0 | let e0 = DoubleDouble::mul_f64_add( |
192 | 0 | DoubleDouble::from_bit_pair(p[1]), |
193 | 0 | x, |
194 | 0 | DoubleDouble::from_bit_pair(p[0]), |
195 | | ); |
196 | 0 | let e1 = DoubleDouble::mul_f64_add( |
197 | 0 | DoubleDouble::from_bit_pair(p[3]), |
198 | 0 | x, |
199 | 0 | DoubleDouble::from_bit_pair(p[2]), |
200 | | ); |
201 | 0 | let e2 = DoubleDouble::mul_f64_add( |
202 | 0 | DoubleDouble::from_bit_pair(p[5]), |
203 | 0 | x, |
204 | 0 | DoubleDouble::from_bit_pair(p[4]), |
205 | | ); |
206 | 0 | let e3 = DoubleDouble::mul_f64_add( |
207 | 0 | DoubleDouble::from_bit_pair(p[7]), |
208 | 0 | x, |
209 | 0 | DoubleDouble::from_bit_pair(p[6]), |
210 | | ); |
211 | 0 | let e4 = DoubleDouble::mul_f64_add( |
212 | 0 | DoubleDouble::from_bit_pair(p[9]), |
213 | 0 | x, |
214 | 0 | DoubleDouble::from_bit_pair(p[8]), |
215 | | ); |
216 | 0 | let e5 = DoubleDouble::mul_f64_add( |
217 | 0 | DoubleDouble::from_bit_pair(p[11]), |
218 | 0 | x, |
219 | 0 | DoubleDouble::from_bit_pair(p[10]), |
220 | | ); |
221 | | |
222 | 0 | let f0 = DoubleDouble::mul_add(x2, e1, e0); |
223 | 0 | let f1 = DoubleDouble::mul_add(x2, e3, e2); |
224 | 0 | let f2 = DoubleDouble::mul_add(x2, e5, e4); |
225 | | |
226 | 0 | let g0 = DoubleDouble::mul_add(x4, f1, f0); |
227 | | |
228 | 0 | let p_num = DoubleDouble::mul_add(x8, f2, g0); |
229 | | |
230 | 0 | let rcp = DoubleDouble::from_quick_recip(x); |
231 | 0 | let rcp2 = DoubleDouble::quick_mult(rcp, rcp); |
232 | | |
233 | 0 | let e0 = DoubleDouble::mul_f64_add_f64( |
234 | 0 | DoubleDouble::from_bit_pair(q[1]), |
235 | 0 | x, |
236 | 0 | f64::from_bits(0x3ff0000000000000), |
237 | | ); |
238 | 0 | let e1 = DoubleDouble::mul_f64_add( |
239 | 0 | DoubleDouble::from_bit_pair(q[3]), |
240 | 0 | x, |
241 | 0 | DoubleDouble::from_bit_pair(q[2]), |
242 | | ); |
243 | 0 | let e2 = DoubleDouble::mul_f64_add( |
244 | 0 | DoubleDouble::from_bit_pair(q[5]), |
245 | 0 | x, |
246 | 0 | DoubleDouble::from_bit_pair(q[4]), |
247 | | ); |
248 | 0 | let e3 = DoubleDouble::mul_f64_add( |
249 | 0 | DoubleDouble::from_bit_pair(q[7]), |
250 | 0 | x, |
251 | 0 | DoubleDouble::from_bit_pair(q[6]), |
252 | | ); |
253 | 0 | let e4 = DoubleDouble::mul_f64_add( |
254 | 0 | DoubleDouble::from_bit_pair(q[9]), |
255 | 0 | x, |
256 | 0 | DoubleDouble::from_bit_pair(q[8]), |
257 | | ); |
258 | 0 | let e5 = DoubleDouble::mul_f64_add( |
259 | 0 | DoubleDouble::from_bit_pair(q[11]), |
260 | 0 | x, |
261 | 0 | DoubleDouble::from_bit_pair(q[10]), |
262 | | ); |
263 | | |
264 | 0 | let f0 = DoubleDouble::mul_add(x2, e1, e0); |
265 | 0 | let f1 = DoubleDouble::mul_add(x2, e3, e2); |
266 | 0 | let f2 = DoubleDouble::mul_add(x2, e5, e4); |
267 | | |
268 | 0 | let g0 = DoubleDouble::mul_add(x4, f1, f0); |
269 | | |
270 | 0 | let p_den = DoubleDouble::mul_add(x8, f2, g0); |
271 | | |
272 | 0 | let q = DoubleDouble::div(p_num, p_den); |
273 | 0 | let r = DoubleDouble::quick_dd_add(q, rcp2); |
274 | 0 | return r; |
275 | 0 | } |
276 | | // asymptotic expansion Trigamma[x] = 1/x + 1/x^2 + sum(Bernoulli(2*k)/x^(2*k + 1)) |
277 | | // Generated in SageMath: |
278 | | // var('x') |
279 | | // def bernoulli_terms(x, N): |
280 | | // S = 0 |
281 | | // for k in range(1, N+1): |
282 | | // B = bernoulli(2*k) |
283 | | // term = B*x**(-(2*k+1)) |
284 | | // S += term |
285 | | // return S |
286 | | // |
287 | | // terms = bernoulli_terms(x, 10) |
288 | | // coeffs = [RealField(150)(terms.coefficient(x, n)) for n in range(0, terms.degree(x)+1, 1)] |
289 | | // for k in range(0, 14): |
290 | | // c = terms.coefficient(x, -k) # coefficient of x^(-k) |
291 | | // if c == 0: |
292 | | // continue |
293 | | // print("f64::from_bits(" + double_to_hex(c) + "),") |
294 | | const C: [(u64, u64); 10] = [ |
295 | | (0x3c65555555555555, 0x3fc5555555555555), |
296 | | (0xbc21111111111111, 0xbfa1111111111111), |
297 | | (0x3c38618618618618, 0x3f98618618618618), |
298 | | (0xbc21111111111111, 0xbfa1111111111111), |
299 | | (0xbc4364d9364d9365, 0x3fb364d9364d9365), |
300 | | (0xbc6981981981981a, 0xbfd0330330330330), |
301 | | (0xbc95555555555555, 0x3ff2aaaaaaaaaaab), |
302 | | (0xbcb7979797979798, 0xc01c5e5e5e5e5e5e), |
303 | | (0xbcac3b070ec1c3b0, 0x404b7c4f8f13e3c5), |
304 | | (0x3cc8d3018d3018d3, 0xc08088fe72cfe72d), |
305 | | ]; |
306 | | |
307 | 0 | let rcp = DoubleDouble::from_quick_recip(x); |
308 | | |
309 | 0 | let q = DoubleDouble::quick_mult(rcp, rcp); |
310 | | |
311 | 0 | let q2 = DoubleDouble::quick_mult(q, q); |
312 | 0 | let q4 = q2 * q2; |
313 | 0 | let q8 = q4 * q4; |
314 | | |
315 | 0 | let e0 = DoubleDouble::quick_mul_add( |
316 | 0 | DoubleDouble::from_bit_pair(C[1]), |
317 | 0 | q, |
318 | 0 | DoubleDouble::from_bit_pair(C[0]), |
319 | | ); |
320 | 0 | let e1 = DoubleDouble::quick_mul_add( |
321 | 0 | DoubleDouble::from_bit_pair(C[3]), |
322 | 0 | q, |
323 | 0 | DoubleDouble::from_bit_pair(C[2]), |
324 | | ); |
325 | 0 | let e2 = DoubleDouble::quick_mul_add( |
326 | 0 | DoubleDouble::from_bit_pair(C[5]), |
327 | 0 | q, |
328 | 0 | DoubleDouble::from_bit_pair(C[4]), |
329 | | ); |
330 | 0 | let e3 = DoubleDouble::quick_mul_add( |
331 | 0 | DoubleDouble::from_bit_pair(C[7]), |
332 | 0 | q, |
333 | 0 | DoubleDouble::from_bit_pair(C[6]), |
334 | | ); |
335 | 0 | let e4 = DoubleDouble::quick_mul_add( |
336 | 0 | DoubleDouble::from_bit_pair(C[9]), |
337 | 0 | q, |
338 | 0 | DoubleDouble::from_bit_pair(C[8]), |
339 | | ); |
340 | | |
341 | 0 | let q0 = DoubleDouble::quick_mul_add(q2, e1, e0); |
342 | 0 | let q1 = DoubleDouble::quick_mul_add(q2, e3, e2); |
343 | | |
344 | 0 | let r0 = DoubleDouble::quick_mul_add(q4, q1, q0); |
345 | 0 | let mut p = DoubleDouble::quick_mul_add(q8, e4, r0); |
346 | | |
347 | 0 | let q_over_2 = DoubleDouble::quick_mult_f64(q, 0.5); |
348 | 0 | p = DoubleDouble::quick_mult(p, q); |
349 | 0 | p = DoubleDouble::quick_mult(p, rcp); |
350 | 0 | p = DoubleDouble::quick_dd_add(q_over_2, p); |
351 | 0 | p = DoubleDouble::quick_dd_add(p, rcp); |
352 | 0 | p |
353 | 0 | } |
354 | | |
355 | | #[inline] |
356 | 0 | fn approx_trigamma_dd(x: DoubleDouble) -> DoubleDouble { |
357 | 0 | if x.hi <= 10. { |
358 | 0 | let (p, q) = if x.hi <= 1. { |
359 | 0 | (&P_1, &Q_1) |
360 | 0 | } else if x.hi <= 4. { |
361 | 0 | (&P_2, &Q_2) |
362 | | } else { |
363 | 0 | (&P_3, &Q_3) |
364 | | }; |
365 | 0 | let x2 = DoubleDouble::quick_mult(x, x); |
366 | 0 | let x4 = DoubleDouble::quick_mult(x2, x2); |
367 | 0 | let x8 = DoubleDouble::quick_mult(x4, x4); |
368 | | |
369 | 0 | let e0 = DoubleDouble::mul_add( |
370 | 0 | DoubleDouble::from_bit_pair(p[1]), |
371 | 0 | x, |
372 | 0 | DoubleDouble::from_bit_pair(p[0]), |
373 | | ); |
374 | 0 | let e1 = DoubleDouble::mul_add( |
375 | 0 | DoubleDouble::from_bit_pair(p[3]), |
376 | 0 | x, |
377 | 0 | DoubleDouble::from_bit_pair(p[2]), |
378 | | ); |
379 | 0 | let e2 = DoubleDouble::mul_add( |
380 | 0 | DoubleDouble::from_bit_pair(p[5]), |
381 | 0 | x, |
382 | 0 | DoubleDouble::from_bit_pair(p[4]), |
383 | | ); |
384 | 0 | let e3 = DoubleDouble::mul_add( |
385 | 0 | DoubleDouble::from_bit_pair(p[7]), |
386 | 0 | x, |
387 | 0 | DoubleDouble::from_bit_pair(p[6]), |
388 | | ); |
389 | 0 | let e4 = DoubleDouble::mul_add( |
390 | 0 | DoubleDouble::from_bit_pair(p[9]), |
391 | 0 | x, |
392 | 0 | DoubleDouble::from_bit_pair(p[8]), |
393 | | ); |
394 | 0 | let e5 = DoubleDouble::mul_add( |
395 | 0 | DoubleDouble::from_bit_pair(p[11]), |
396 | 0 | x, |
397 | 0 | DoubleDouble::from_bit_pair(p[10]), |
398 | | ); |
399 | | |
400 | 0 | let f0 = DoubleDouble::mul_add(x2, e1, e0); |
401 | 0 | let f1 = DoubleDouble::mul_add(x2, e3, e2); |
402 | 0 | let f2 = DoubleDouble::mul_add(x2, e5, e4); |
403 | | |
404 | 0 | let g0 = DoubleDouble::mul_add(x4, f1, f0); |
405 | | |
406 | 0 | let p_num = DoubleDouble::mul_add(x8, f2, g0); |
407 | | |
408 | 0 | let rcp = x.recip(); |
409 | 0 | let rcp2 = DoubleDouble::quick_mult(rcp, rcp); |
410 | | |
411 | 0 | let e0 = DoubleDouble::mul_add_f64( |
412 | 0 | DoubleDouble::from_bit_pair(q[1]), |
413 | 0 | x, |
414 | 0 | f64::from_bits(0x3ff0000000000000), |
415 | | ); |
416 | 0 | let e1 = DoubleDouble::mul_add( |
417 | 0 | DoubleDouble::from_bit_pair(q[3]), |
418 | 0 | x, |
419 | 0 | DoubleDouble::from_bit_pair(q[2]), |
420 | | ); |
421 | 0 | let e2 = DoubleDouble::mul_add( |
422 | 0 | DoubleDouble::from_bit_pair(q[5]), |
423 | 0 | x, |
424 | 0 | DoubleDouble::from_bit_pair(q[4]), |
425 | | ); |
426 | 0 | let e3 = DoubleDouble::mul_add( |
427 | 0 | DoubleDouble::from_bit_pair(q[7]), |
428 | 0 | x, |
429 | 0 | DoubleDouble::from_bit_pair(q[6]), |
430 | | ); |
431 | 0 | let e4 = DoubleDouble::mul_add( |
432 | 0 | DoubleDouble::from_bit_pair(q[9]), |
433 | 0 | x, |
434 | 0 | DoubleDouble::from_bit_pair(q[8]), |
435 | | ); |
436 | 0 | let e5 = DoubleDouble::mul_add( |
437 | 0 | DoubleDouble::from_bit_pair(q[11]), |
438 | 0 | x, |
439 | 0 | DoubleDouble::from_bit_pair(q[10]), |
440 | | ); |
441 | | |
442 | 0 | let f0 = DoubleDouble::mul_add(x2, e1, e0); |
443 | 0 | let f1 = DoubleDouble::mul_add(x2, e3, e2); |
444 | 0 | let f2 = DoubleDouble::mul_add(x2, e5, e4); |
445 | | |
446 | 0 | let g0 = DoubleDouble::mul_add(x4, f1, f0); |
447 | | |
448 | 0 | let p_den = DoubleDouble::mul_add(x8, f2, g0); |
449 | | |
450 | 0 | let q = DoubleDouble::div(p_num, p_den); |
451 | 0 | let r = DoubleDouble::quick_dd_add(q, rcp2); |
452 | 0 | return r; |
453 | 0 | } |
454 | | // asymptotic expansion Trigamma[x] = 1/x + 1/x^2 + sum(Bernoulli(2*k)/x^(2*k + 1)) |
455 | | // Generated in SageMath: |
456 | | // var('x') |
457 | | // def bernoulli_terms(x, N): |
458 | | // S = 0 |
459 | | // for k in range(1, N+1): |
460 | | // B = bernoulli(2*k) |
461 | | // term = B*x**(-(2*k+1)) |
462 | | // S += term |
463 | | // return S |
464 | | // |
465 | | // terms = bernoulli_terms(x, 10) |
466 | | // coeffs = [RealField(150)(terms.coefficient(x, n)) for n in range(0, terms.degree(x)+1, 1)] |
467 | | // for k in range(0, 14): |
468 | | // c = terms.coefficient(x, -k) # coefficient of x^(-k) |
469 | | // if c == 0: |
470 | | // continue |
471 | | // print("f64::from_bits(" + double_to_hex(c) + "),") |
472 | | const C: [(u64, u64); 10] = [ |
473 | | (0x3c65555555555555, 0x3fc5555555555555), |
474 | | (0xbc21111111111111, 0xbfa1111111111111), |
475 | | (0x3c38618618618618, 0x3f98618618618618), |
476 | | (0xbc21111111111111, 0xbfa1111111111111), |
477 | | (0xbc4364d9364d9365, 0x3fb364d9364d9365), |
478 | | (0xbc6981981981981a, 0xbfd0330330330330), |
479 | | (0xbc95555555555555, 0x3ff2aaaaaaaaaaab), |
480 | | (0xbcb7979797979798, 0xc01c5e5e5e5e5e5e), |
481 | | (0xbcac3b070ec1c3b0, 0x404b7c4f8f13e3c5), |
482 | | (0x3cc8d3018d3018d3, 0xc08088fe72cfe72d), |
483 | | ]; |
484 | | |
485 | 0 | let rcp = x.recip(); |
486 | | |
487 | 0 | let q = DoubleDouble::quick_mult(rcp, rcp); |
488 | | |
489 | 0 | let q2 = DoubleDouble::quick_mult(q, q); |
490 | 0 | let q4 = q2 * q2; |
491 | 0 | let q8 = q4 * q4; |
492 | | |
493 | 0 | let e0 = DoubleDouble::quick_mul_add( |
494 | 0 | DoubleDouble::from_bit_pair(C[1]), |
495 | 0 | q, |
496 | 0 | DoubleDouble::from_bit_pair(C[0]), |
497 | | ); |
498 | 0 | let e1 = DoubleDouble::quick_mul_add( |
499 | 0 | DoubleDouble::from_bit_pair(C[3]), |
500 | 0 | q, |
501 | 0 | DoubleDouble::from_bit_pair(C[2]), |
502 | | ); |
503 | 0 | let e2 = DoubleDouble::quick_mul_add( |
504 | 0 | DoubleDouble::from_bit_pair(C[5]), |
505 | 0 | q, |
506 | 0 | DoubleDouble::from_bit_pair(C[4]), |
507 | | ); |
508 | 0 | let e3 = DoubleDouble::quick_mul_add( |
509 | 0 | DoubleDouble::from_bit_pair(C[7]), |
510 | 0 | q, |
511 | 0 | DoubleDouble::from_bit_pair(C[6]), |
512 | | ); |
513 | 0 | let e4 = DoubleDouble::quick_mul_add( |
514 | 0 | DoubleDouble::from_bit_pair(C[9]), |
515 | 0 | q, |
516 | 0 | DoubleDouble::from_bit_pair(C[8]), |
517 | | ); |
518 | | |
519 | 0 | let q0 = DoubleDouble::quick_mul_add(q2, e1, e0); |
520 | 0 | let q1 = DoubleDouble::quick_mul_add(q2, e3, e2); |
521 | | |
522 | 0 | let r0 = DoubleDouble::quick_mul_add(q4, q1, q0); |
523 | 0 | let mut p = DoubleDouble::quick_mul_add(q8, e4, r0); |
524 | | |
525 | 0 | let q_over_2 = DoubleDouble::quick_mult_f64(q, 0.5); |
526 | 0 | p = DoubleDouble::quick_mult(p, q); |
527 | 0 | p = DoubleDouble::quick_mult(p, rcp); |
528 | 0 | p = DoubleDouble::quick_dd_add(q_over_2, p); |
529 | 0 | p = DoubleDouble::quick_dd_add(p, rcp); |
530 | 0 | p |
531 | 0 | } |
532 | | |
533 | | /// Computes the trigamma function ψ₁(x). |
534 | | /// |
535 | | /// The trigamma function is the second derivative of the logarithm of the gamma function. |
536 | 0 | pub fn f_trigamma(x: f64) -> f64 { |
537 | 0 | let xb = x.to_bits(); |
538 | 0 | if !x.is_normal() { |
539 | 0 | if x.is_infinite() { |
540 | 0 | return if x.is_sign_negative() { |
541 | 0 | f64::NEG_INFINITY |
542 | | } else { |
543 | 0 | 0. |
544 | | }; |
545 | 0 | } |
546 | 0 | if x.is_nan() { |
547 | 0 | return f64::NAN; |
548 | 0 | } |
549 | 0 | if xb == 0 { |
550 | 0 | return f64::INFINITY; |
551 | 0 | } |
552 | 0 | } |
553 | | |
554 | 0 | let x_e = (x.to_bits() >> 52) & 0x7ff; |
555 | | |
556 | | const E_BIAS: u64 = (1u64 << (11 - 1u64)) - 1u64; |
557 | | |
558 | 0 | if x_e < E_BIAS - 52 { |
559 | | // |x| < 2^-52 |
560 | 0 | let dx = x; |
561 | 0 | return 1. / (dx * dx); |
562 | 0 | } |
563 | | |
564 | 0 | if x < 0. { |
565 | 0 | if is_integer(x) { |
566 | 0 | return f64::INFINITY; |
567 | 0 | } |
568 | | // reflection formula |
569 | | // Trigamma[1-x] + Trigamma[x] = PI^2 / sinpi^2(x) |
570 | | const SQR_PI: DoubleDouble = |
571 | | DoubleDouble::from_bit_pair((0x3cc692b71366cc05, 0x4023bd3cc9be45de)); // pi^2 |
572 | 0 | let sinpi_ax = f_fast_sinpi_dd(-x); |
573 | 0 | let dx = DoubleDouble::from_full_exact_sub(1., x); |
574 | 0 | let result = DoubleDouble::div(SQR_PI, DoubleDouble::quick_mult(sinpi_ax, sinpi_ax)); |
575 | 0 | let trigamma_x = approx_trigamma_dd(dx); |
576 | 0 | return DoubleDouble::quick_dd_sub(result, trigamma_x).to_f64(); |
577 | 0 | } |
578 | | |
579 | 0 | approx_trigamma(x).to_f64() |
580 | 0 | } |
581 | | |
582 | | #[cfg(test)] |
583 | | mod tests { |
584 | | |
585 | | use super::*; |
586 | | |
587 | | #[test] |
588 | | fn test_trigamma() { |
589 | | assert_eq!(f_trigamma(-27.058018), 300.35629698636757); |
590 | | assert_eq!(f_trigamma(27.058018), 0.037648965757704725); |
591 | | assert_eq!(f_trigamma(8.058018), 0.13211796975281037); |
592 | | assert_eq!(f_trigamma(-8.058018), 300.2758629255111); |
593 | | assert_eq!(f_trigamma(2.23432), 0.5621320243666134); |
594 | | assert_eq!(f_trigamma(-2.4653), 9.653674003034206); |
595 | | assert_eq!(f_trigamma(0.123541), 66.91128231455282); |
596 | | assert_eq!(f_trigamma(-0.54331), 9.154415950366596); |
597 | | assert_eq!(f_trigamma(-5.), f64::INFINITY); |
598 | | assert_eq!(f_trigamma(f64::INFINITY), 0.0); |
599 | | assert_eq!(f_trigamma(f64::NEG_INFINITY), f64::NEG_INFINITY); |
600 | | assert!(f_trigamma(f64::NAN).is_nan()); |
601 | | } |
602 | | } |