Coverage Report

Created: 2025-10-14 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pxfm-0.1.25/src/gamma/digammaf.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::{f_fmla, is_integerf};
30
use crate::logs::simple_fast_log;
31
use crate::polyeval::{
32
    f_estrin_polyeval7, f_estrin_polyeval8, f_estrin_polyeval9, f_polyeval4, f_polyeval6,
33
    f_polyeval10,
34
};
35
use crate::tangent::cotpif_core;
36
37
#[inline]
38
0
fn approx_digamma(x: f64) -> f64 {
39
0
    if x <= 1. {
40
        // Generated in Wolfram Mathematica:
41
        // <<FunctionApproximations`
42
        // ClearAll["Global`*"]
43
        // f[x_]:=PolyGamma[x + 1]
44
        // {err0,approx, err1}=MiniMaxApproximation[f[z],{z,{-0.99999999,0},9,8},WorkingPrecision->75,MaxIterations->100]
45
        // num=Numerator[approx];
46
        // den=Denominator[approx];
47
        // poly=num;
48
        // coeffs=CoefficientList[poly,z];
49
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
50
        // poly=den;
51
        // coeffs=CoefficientList[poly,z];
52
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
53
0
        let p_num = f_polyeval10(
54
0
            x,
55
0
            f64::from_bits(0xbfe2788cfc6fb619),
56
0
            f64::from_bits(0x3fca347925788707),
57
0
            f64::from_bits(0x3ff887e0f068df69),
58
0
            f64::from_bits(0x3ff543446198b4d2),
59
0
            f64::from_bits(0x3fe03e4455fbad95),
60
0
            f64::from_bits(0x3fb994be8389e4f6),
61
0
            f64::from_bits(0x3f84eb98b830c9b1),
62
0
            f64::from_bits(0x3f4025193ac4ad97),
63
0
            f64::from_bits(0x3ee18c1d683d866a),
64
0
            f64::from_bits(0x3e457cb5b4a07c95),
65
        );
66
0
        let p_den = f_estrin_polyeval9(
67
0
            x,
68
0
            f64::from_bits(0x3ff0000000000000),
69
0
            f64::from_bits(0x4003f5f42d95aca8),
70
0
            f64::from_bits(0x4002f96e541d0513),
71
0
            f64::from_bits(0x3ff22c34843313fa),
72
0
            f64::from_bits(0x3fd33574180109bf),
73
0
            f64::from_bits(0x3fa6c07b99ebb277),
74
0
            f64::from_bits(0x3f6cdd7b8fa68926),
75
0
            f64::from_bits(0x3f212b74d39e287f),
76
0
            f64::from_bits(0x3ebabd247f366583),
77
        );
78
0
        return p_num / p_den - 1. / x;
79
0
    } else if x < 1.461632144 {
80
        // exception
81
0
        if x == 1.4616321325302124 {
82
0
            return -1.2036052e-8;
83
0
        }
84
        // Generated in Wolfram Mathematica:
85
        // <<FunctionApproximations`
86
        // ClearAll["Global`*"]
87
        // f[x_]:=PolyGamma[x+1]
88
        // {err0,approx,err1}=MiniMaxApproximation[f[z],{z,{0,0.461632144},8,8},WorkingPrecision->75,MaxIterations->100]
89
        // num=Numerator[approx];
90
        // den=Denominator[approx];
91
        // poly=num;
92
        // coeffs=CoefficientList[poly,z];
93
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
94
        // poly=den;
95
        // coeffs=CoefficientList[poly,z];
96
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
97
0
        let p_num = f_estrin_polyeval9(
98
0
            x,
99
0
            f64::from_bits(0xbfe2788cfc6fb619),
100
0
            f64::from_bits(0x3fd0ad221e8c3b8b),
101
0
            f64::from_bits(0x3ff813be4dee2e90),
102
0
            f64::from_bits(0x3ff2f64cbfa7d1a4),
103
0
            f64::from_bits(0x3fd9a8c4798f426c),
104
0
            f64::from_bits(0x3fb111a34898f6bf),
105
0
            f64::from_bits(0x3f75dd3efac1e579),
106
0
            f64::from_bits(0x3f272596b2582f0d),
107
0
            f64::from_bits(0x3eb9b074f4ca6263),
108
        );
109
0
        let p_den = f_estrin_polyeval9(
110
0
            x,
111
0
            f64::from_bits(0x3ff0000000000000),
112
0
            f64::from_bits(0x40032fd3a1fe3a25),
113
0
            f64::from_bits(0x40012969bcd7fef3),
114
0
            f64::from_bits(0x3fee1a267ee7a97a),
115
0
            f64::from_bits(0x3fcc1522178a69a6),
116
0
            f64::from_bits(0x3f9bd89421334af0),
117
0
            f64::from_bits(0x3f5b40bc3203df4c),
118
0
            f64::from_bits(0x3f05ac6be0b79fac),
119
0
            f64::from_bits(0x3e9047c3d8071f18),
120
        );
121
0
        return p_num / p_den - 1. / x;
122
0
    } else if x <= 2. {
123
        // Generated in Wolfram Mathematica:
124
        // <<FunctionApproximations`
125
        // ClearAll["Global`*"]
126
        // f[x_]:=PolyGamma[x+1]
127
        // {err0,approx,err1}=MiniMaxApproximation[f[z],{z,{0.461632144,1},7,6},WorkingPrecision->75,MaxIterations->100]
128
        // num=Numerator[approx];
129
        // den=Denominator[approx];
130
        // poly=num;
131
        // coeffs=CoefficientList[poly,z];
132
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
133
        // poly=den;
134
        // coeffs=CoefficientList[poly,z];
135
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
136
0
        let p_num = f_estrin_polyeval8(
137
0
            x,
138
0
            f64::from_bits(0xbfe2788cfc6fb613),
139
0
            f64::from_bits(0x3fd690553caa1c6b),
140
0
            f64::from_bits(0x3ff721cf4d9e008f),
141
0
            f64::from_bits(0x3fee9f4096f34b09),
142
0
            f64::from_bits(0x3fd055e88830fc71),
143
0
            f64::from_bits(0x3f9e66bceee16960),
144
0
            f64::from_bits(0x3f55d436778b3403),
145
0
            f64::from_bits(0x3eeef6bc214819b3),
146
        );
147
0
        let p_den = f_estrin_polyeval8(
148
0
            x,
149
0
            f64::from_bits(0x3ff0000000000000),
150
0
            f64::from_bits(0x4001e96eaab05729),
151
0
            f64::from_bits(0x3ffcb1aa289077da),
152
0
            f64::from_bits(0x3fe5499e89b757b6),
153
0
            f64::from_bits(0x3fbee531a912bca9),
154
0
            f64::from_bits(0x3f84d46f2121ceb7),
155
0
            f64::from_bits(0x3f35abd7eb7440e6),
156
0
            f64::from_bits(0x3ec43bf7c110aad1),
157
        );
158
0
        return p_num / p_den - 1. / x;
159
0
    } else if x <= 3. {
160
        // Generated in Wolfram Mathematica:
161
        // <<FunctionApproximations`
162
        // ClearAll["Global`*"]
163
        // f[x_]:=PolyGamma[x+1]
164
        // {err0,approx}=MiniMaxApproximation[f[z],{z,{1,2},7,6},WorkingPrecision->75,MaxIterations->100]
165
        // num=Numerator[approx][[1]];
166
        // den=Denominator[approx][[1]];
167
        // poly=num;
168
        // coeffs=CoefficientList[poly,z];
169
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
170
        // poly=den;
171
        // coeffs=CoefficientList[poly,z];
172
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
173
0
        let p_num = f_estrin_polyeval8(
174
0
            x,
175
0
            f64::from_bits(0xbfe2788cfc63695f),
176
0
            f64::from_bits(0x3fdb63791eb688ea),
177
0
            f64::from_bits(0x3ff625ed84968583),
178
0
            f64::from_bits(0x3fe900ea36e59e02),
179
0
            f64::from_bits(0x3fc5319409f4fec6),
180
0
            f64::from_bits(0x3f8b6e7cacff2a59),
181
0
            f64::from_bits(0x3f34a7e591bf2af3),
182
0
            f64::from_bits(0x3e9c323866d138db),
183
        );
184
0
        let p_den = f_estrin_polyeval7(
185
0
            x,
186
0
            f64::from_bits(0x3ff0000000000000),
187
0
            f64::from_bits(0x4000ddf448e34181),
188
0
            f64::from_bits(0x3ff87188f2414f79),
189
0
            f64::from_bits(0x3fdeff74d18f811a),
190
0
            f64::from_bits(0x3fb1a0cddeb3a320),
191
0
            f64::from_bits(0x3f701050c1344800),
192
0
            f64::from_bits(0x3f10480a4ea8cf57),
193
        );
194
0
        return p_num / p_den - 1. / x;
195
0
    } else if x <= 8. {
196
        // Generated in Wolfram Mathematica:
197
        // <<FunctionApproximations`
198
        // ClearAll["Global`*"]
199
        // f[x_]:=PolyGamma[x + 1]
200
        // {err0,approx}=MiniMaxApproximation[f[z],{z,{2,7},7,7},WorkingPrecision->75,MaxIterations->100]
201
        // num=Numerator[approx][[1]];
202
        // den=Denominator[approx][[1]];
203
        // poly=num;
204
        // coeffs=CoefficientList[poly,z];
205
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
206
        // poly=den;
207
        // coeffs=CoefficientList[poly,z];
208
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
209
0
        let p_num = f_estrin_polyeval8(
210
0
            x,
211
0
            f64::from_bits(0xbfe2788c3725fd5e),
212
0
            f64::from_bits(0x3fde39f54e5a651a),
213
0
            f64::from_bits(0x3ff56983b839b94f),
214
0
            f64::from_bits(0x3fe60d6118d8fc08),
215
0
            f64::from_bits(0x3fc0e889ace69a30),
216
0
            f64::from_bits(0x3f844e10e399bd93),
217
0
            f64::from_bits(0x3f3099741afda7cb),
218
0
            f64::from_bits(0x3eb74a15144af8e9),
219
        );
220
0
        let p_den = f_estrin_polyeval8(
221
0
            x,
222
0
            f64::from_bits(0x3ff0000000000000),
223
0
            f64::from_bits(0x4000409ed08c0553),
224
0
            f64::from_bits(0x3ff63746cb6183e3),
225
0
            f64::from_bits(0x3fda1196b1a75351),
226
0
            f64::from_bits(0x3fab4ba9fad2d187),
227
0
            f64::from_bits(0x3f67de6e6987e3a3),
228
0
            f64::from_bits(0x3f0c9d85ca31182e),
229
0
            f64::from_bits(0x3e8b269f154c8f12),
230
        );
231
0
        return p_num / p_den - 1. / x;
232
0
    } else if x <= 15. {
233
        // Generated in Wolfram Mathematica:
234
        // <<FunctionApproximations`
235
        // ClearAll["Global`*"]
236
        // f[x_]:=PolyGamma[x + 1]
237
        // {err0,approx}=MiniMaxApproximation[f[z],{z,{7,14},7,7},WorkingPrecision->75,MaxIterations->100]
238
        // num=Numerator[approx][[1]];
239
        // den=Denominator[approx][[1]];
240
        // poly=num;
241
        // coeffs=CoefficientList[poly,z];
242
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
243
        // poly=den;
244
        // coeffs=CoefficientList[poly,z];
245
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
246
0
        let p_num = f_estrin_polyeval8(
247
0
            x,
248
0
            f64::from_bits(0xbfe272e75f131710),
249
0
            f64::from_bits(0x3fe53fce507de081),
250
0
            f64::from_bits(0x3ff182957d4f961a),
251
0
            f64::from_bits(0x3fd6d1652dea00e9),
252
0
            f64::from_bits(0x3fa45e16488abe0f),
253
0
            f64::from_bits(0x3f5a52a8f3f3663f),
254
0
            f64::from_bits(0x3ef5b767554d208e),
255
0
            f64::from_bits(0x3e6d2393b100353d),
256
        );
257
0
        let p_den = f_estrin_polyeval8(
258
0
            x,
259
0
            f64::from_bits(0x3ff0000000000000),
260
0
            f64::from_bits(0x3ffb1f295c6e5fc5),
261
0
            f64::from_bits(0x3feb88eb913eb117),
262
0
            f64::from_bits(0x3fc570f3aed83ff7),
263
0
            f64::from_bits(0x3f8afe819fdfa5a5),
264
0
            f64::from_bits(0x3f3a2cec9041f361),
265
0
            f64::from_bits(0x3ed0549335964bb9),
266
0
            f64::from_bits(0x3e3ebdcb0002d63e),
267
        );
268
0
        return p_num / p_den - 1. / x;
269
0
    }
270
    // digamma asymptotic expansion
271
    // digamma(x) ~ ln(z)+1/(2z)-sum_(n=1)^(infty)(Bernoulli(2n))/(2nz^(2n))
272
    // Generated in SageMath:
273
    // var('x')
274
    //
275
    // def bernoulli_terms(x, N):
276
    //     S = 0
277
    //     S += QQ(1)/QQ(2)/x
278
    //     for k in range(1, N+1):
279
    //         B = bernoulli(2*k)
280
    //         term = (B / QQ(2*k))*x**(-2*k)
281
    //         S += term
282
    //     return S
283
    //
284
    // terms = bernoulli_terms(x, 5)
285
    //
286
    // coeffs = [RealField(150)(terms.coefficient(x, n)) for n in range(0, terms.degree(x)+1, 1)]
287
    // for k in range(1, 13):
288
    //     c = terms.coefficient(x, -k)  # coefficient of x^(-k)
289
    //     if c == 0:
290
    //         continue
291
    //     print("f64::from_bits(" + double_to_hex(c) + "),")
292
0
    let rcp = 1. / x;
293
0
    let rcp_sqr = rcp * rcp;
294
0
    let p = f_polyeval6(
295
0
        rcp_sqr,
296
0
        f64::from_bits(0x3fb5555555555555),
297
0
        f64::from_bits(0xbf81111111111111),
298
0
        f64::from_bits(0x3f70410410410410),
299
0
        f64::from_bits(0xbf71111111111111),
300
0
        f64::from_bits(0x3f7f07c1f07c1f08),
301
0
        f64::from_bits(0xbf95995995995996),
302
    );
303
0
    let v_log = simple_fast_log(x);
304
0
    v_log - f_fmla(rcp, f64::from_bits(0x3fe0000000000000), p * rcp_sqr)
305
0
}
306
307
/// Computes digamma(x)
308
0
pub fn f_digammaf(x: f32) -> f32 {
309
0
    let xb = x.to_bits();
310
    // filter out exceptional cases
311
0
    if xb >= 0xffu32 << 23 || xb == 0 {
312
0
        if x.is_infinite() {
313
0
            return if x.is_sign_negative() {
314
0
                f32::NAN
315
            } else {
316
0
                f32::INFINITY
317
            };
318
0
        }
319
0
        if x.is_nan() {
320
0
            return f32::NAN;
321
0
        }
322
0
        if xb == 0 {
323
0
            return f32::INFINITY;
324
0
        }
325
0
    }
326
327
0
    let ax = x.to_bits() & 0x7fff_ffff;
328
329
0
    if ax <= 0x32abcc77u32 {
330
        // |x| < 2e-8
331
        // digamma near where x -> 1 ~ Digamma[x] = -euler + O(x)
332
        // considering identity Digamma[x+1] = Digamma[x] + 1/x,
333
        // hence x < ulp(1) then x+1 ~= 1 that gives
334
        // Digamma[x] = Digamma[x+1] - 1/x = -euler - 1/x
335
        const EULER: f64 = f64::from_bits(0x3fe2788cfc6fb619);
336
0
        return (-EULER - 1. / x as f64) as f32;
337
0
    } else if ax <= 0x377ba882u32 {
338
        // |x| <= 0.000015
339
        // Laurent series of digamma(x)
340
        // Generated by SageMath:
341
        // from mpmath import mp
342
        // mp.prec = 150
343
        // R = RealField(150)
344
        // var('x')
345
        // def laurent_terms(x, N):
346
        //     S = 0
347
        //     S += -1/x - R(mp.euler)
348
        //     S1 = 0
349
        //     for k in range(1, N+1):
350
        //         zet = R(mp.zeta(k + 1))
351
        //         term = zet*(-x)**k
352
        //         S1 += term
353
        //     return S - S1
354
        //
355
        // terms = laurent_terms(x, 4)
356
        //
357
        // coeffs = [RealField(150)(terms.coefficient(x, n)) for n in range(0, terms.degree(x)+1, 1)]
358
        // for k in range(1, 13):
359
        //     c = terms.coefficient(x, k)  # coefficient of x^(-k)
360
        //     if c == 0:
361
        //         continue
362
        //     print("f64::from_bits(" + double_to_hex(c) + "),")
363
        const EULER: f64 = f64::from_bits(0x3fe2788cfc6fb619);
364
0
        let dx = x as f64;
365
0
        let start = -1. / dx;
366
0
        let neg_dx = -dx;
367
0
        let z = f_polyeval4(
368
0
            neg_dx,
369
0
            f64::from_bits(0x3ffa51a6625307d3),
370
0
            f64::from_bits(0xbff33ba004f00621),
371
0
            f64::from_bits(0x3ff151322ac7d848),
372
0
            f64::from_bits(0xbff097418eca7cce),
373
        );
374
0
        let r = f_fmla(z, dx, -EULER) + start;
375
0
        return r as f32;
376
0
    }
377
378
0
    let mut dx = x as f64;
379
0
    let mut result: f64 = 0.;
380
0
    if x < 0. {
381
        // at negative integers it's inf
382
0
        if is_integerf(x) {
383
0
            return f32::NAN;
384
0
        }
385
386
        // reflection Gamma(1-x) + Gamma(x) = Pi/tan(PI*x)
387
        const PI: f64 = f64::from_bits(0x400921fb54442d18);
388
0
        let cot_x_angle = -dx;
389
0
        dx = 1. - dx;
390
0
        result = PI * cotpif_core(cot_x_angle);
391
0
    }
392
0
    let approx = approx_digamma(dx);
393
0
    result += approx;
394
0
    result as f32
395
0
}
396
397
#[cfg(test)]
398
mod tests {
399
    use super::*;
400
401
    #[test]
402
    fn test_digamma() {
403
        assert_eq!(f_digammaf(-13.999000000012591), -996.9182);
404
        assert_eq!(f_digammaf(15.3796425), 2.700182);
405
        assert_eq!(f_digammaf(0.0005187988), -1928.1058);
406
        assert_eq!(f_digammaf(0.0019531252), -512.574);
407
        assert_eq!(f_digammaf(-96.353516), 6.1304626);
408
        assert_eq!(f_digammaf(-31.06964), 17.582127);
409
        assert_eq!(f_digammaf(-0.000000000000001191123), 839543830000000.);
410
        assert_eq!(f_digammaf(f32::INFINITY), f32::INFINITY);
411
        assert_eq!(f_digammaf(0.), f32::INFINITY);
412
        assert_eq!(f_digammaf(-0.), f32::INFINITY);
413
        assert!(f_digammaf(f32::NEG_INFINITY).is_nan());
414
        assert!(f_digammaf(f32::NAN).is_nan());
415
    }
416
}