@ohos-ports/stdlib-math-base-special-gammaln 0.3.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/main.c ADDED
@@ -0,0 +1,550 @@
1
+ /**
2
+ * @license Apache-2.0
3
+ *
4
+ * Copyright (c) 2024 The Stdlib Authors.
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ *
18
+ *
19
+ * ## Notice
20
+ *
21
+ * The following copyright, license, and long comment were part of the original implementation available as part of [FreeBSD]{@link https://svnweb.freebsd.org/base/release/12.2.0/lib/msun/src/e_lgamma_r.c}. The implementation follows the original, but has been modified for JavaScript.
22
+ *
23
+ * ```text
24
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
25
+ *
26
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
27
+ * Permission to use, copy, modify, and distribute this
28
+ * software is freely granted, provided that this notice
29
+ * is preserved.
30
+ * ```
31
+ */
32
+
33
+ #include "stdlib/math/base/special/gammaln.h"
34
+ #include "stdlib/math/base/assert/is_nan.h"
35
+ #include "stdlib/math/base/assert/is_infinite.h"
36
+ #include "stdlib/math/base/special/abs.h"
37
+ #include "stdlib/math/base/special/ln.h"
38
+ #include "stdlib/math/base/special/trunc.h"
39
+ #include "stdlib/math/base/special/sinpi.h"
40
+ #include "stdlib/constants/float64/pi.h"
41
+ #include "stdlib/constants/float64/pinf.h"
42
+ #include <stdint.h>
43
+
44
+ static const double A1C = 7.72156649015328655494e-02; // 0x3FB3C467E37DB0C8
45
+ static const double A2C = 3.22467033424113591611e-01; // 0x3FD4A34CC4A60FAD
46
+ static const double RC = 1.0;
47
+ static const double SC = -7.72156649015328655494e-02; // 0xBFB3C467E37DB0C8
48
+ static const double T1C = 4.83836122723810047042e-01; // 0x3FDEF72BC8EE38A2
49
+ static const double T2C = -1.47587722994593911752e-01; // 0xBFC2E4278DC6C509
50
+ static const double T3C = 6.46249402391333854778e-02; // 0x3FB08B4294D5419B
51
+ static const double UC = -7.72156649015328655494e-02; // 0xBFB3C467E37DB0C8
52
+ static const double VC = 1.0;
53
+ static const double WC = 4.18938533204672725052e-01; // 0x3FDACFE390C97D69
54
+ static const double YMIN = 1.461632144968362245;
55
+ static const double TWO52 = 4503599627370496; // 2**52
56
+ static const double TWO56 = 72057594037927936; // 2**56
57
+ static const double TINY = 1.3877787807814457e-17;
58
+ static const double TC = 1.46163214496836224576e+00; // 0x3FF762D86356BE3F
59
+ static const double TF = -1.21486290535849611461e-01; // 0xBFBF19B9BCC38A42
60
+ static const double TT = -3.63867699703950536541e-18; // 0xBC50C7CAA48A971F => TT = -(tail of TF)
61
+
62
+ /* Begin auto-generated functions. The following functions are auto-generated. Do not edit directly. */
63
+
64
+ // BEGIN: polyval_a1
65
+
66
+ /**
67
+ * Evaluates a polynomial.
68
+ *
69
+ * ## Notes
70
+ *
71
+ * - The implementation uses [Horner's rule][horners-method] for efficient computation.
72
+ *
73
+ * [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
74
+ *
75
+ * @param x value at which to evaluate the polynomial
76
+ * @return evaluated polynomial
77
+ */
78
+ static double polyval_a1( const double x ) {
79
+ return 0.06735230105312927 + (x * (0.007385550860814029 + (x * (0.0011927076318336207 + (x * (0.00022086279071390839 + (x * 0.000025214456545125733)))))));
80
+ }
81
+
82
+ // END: polyval_a1
83
+
84
+ // BEGIN: polyval_a2
85
+
86
+ /**
87
+ * Evaluates a polynomial.
88
+ *
89
+ * ## Notes
90
+ *
91
+ * - The implementation uses [Horner's rule][horners-method] for efficient computation.
92
+ *
93
+ * [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
94
+ *
95
+ * @param x value at which to evaluate the polynomial
96
+ * @return evaluated polynomial
97
+ */
98
+ static double polyval_a2( const double x ) {
99
+ return 0.020580808432516733 + (x * (0.0028905138367341563 + (x * (0.0005100697921535113 + (x * (0.00010801156724758394 + (x * 0.000044864094961891516)))))));
100
+ }
101
+
102
+ // END: polyval_a2
103
+
104
+ // BEGIN: polyval_r
105
+
106
+ /**
107
+ * Evaluates a polynomial.
108
+ *
109
+ * ## Notes
110
+ *
111
+ * - The implementation uses [Horner's rule][horners-method] for efficient computation.
112
+ *
113
+ * [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
114
+ *
115
+ * @param x value at which to evaluate the polynomial
116
+ * @return evaluated polynomial
117
+ */
118
+ static double polyval_r( const double x ) {
119
+ return 1.3920053346762105 + (x * (0.7219355475671381 + (x * (0.17193386563280308 + (x * (0.01864591917156529 + (x * (0.0007779424963818936 + (x * 0.000007326684307446256)))))))));
120
+ }
121
+
122
+ // END: polyval_r
123
+
124
+ // BEGIN: polyval_s
125
+
126
+ /**
127
+ * Evaluates a polynomial.
128
+ *
129
+ * ## Notes
130
+ *
131
+ * - The implementation uses [Horner's rule][horners-method] for efficient computation.
132
+ *
133
+ * [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
134
+ *
135
+ * @param x value at which to evaluate the polynomial
136
+ * @return evaluated polynomial
137
+ */
138
+ static double polyval_s( const double x ) {
139
+ return 0.21498241596060885 + (x * (0.325778796408931 + (x * (0.14635047265246445 + (x * (0.02664227030336386 + (x * (0.0018402845140733772 + (x * 0.00003194753265841009)))))))));
140
+ }
141
+
142
+ // END: polyval_s
143
+
144
+ // BEGIN: polyval_t1
145
+
146
+ /**
147
+ * Evaluates a polynomial.
148
+ *
149
+ * ## Notes
150
+ *
151
+ * - The implementation uses [Horner's rule][horners-method] for efficient computation.
152
+ *
153
+ * [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
154
+ *
155
+ * @param x value at which to evaluate the polynomial
156
+ * @return evaluated polynomial
157
+ */
158
+ static double polyval_t1( const double x ) {
159
+ return -0.032788541075985965 + (x * (0.006100538702462913 + (x * (-0.0014034646998923284 + (x * 0.00031563207090362595)))));
160
+ }
161
+
162
+ // END: polyval_t1
163
+
164
+ // BEGIN: polyval_t2
165
+
166
+ /**
167
+ * Evaluates a polynomial.
168
+ *
169
+ * ## Notes
170
+ *
171
+ * - The implementation uses [Horner's rule][horners-method] for efficient computation.
172
+ *
173
+ * [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
174
+ *
175
+ * @param x value at which to evaluate the polynomial
176
+ * @return evaluated polynomial
177
+ */
178
+ static double polyval_t2( const double x ) {
179
+ return 0.01797067508118204 + (x * (-0.0036845201678113826 + (x * (0.000881081882437654 + (x * -0.00031275416837512086)))));
180
+ }
181
+
182
+ // END: polyval_t2
183
+
184
+ // BEGIN: polyval_t3
185
+
186
+ /**
187
+ * Evaluates a polynomial.
188
+ *
189
+ * ## Notes
190
+ *
191
+ * - The implementation uses [Horner's rule][horners-method] for efficient computation.
192
+ *
193
+ * [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
194
+ *
195
+ * @param x value at which to evaluate the polynomial
196
+ * @return evaluated polynomial
197
+ */
198
+ static double polyval_t3( const double x ) {
199
+ return -0.010314224129834144 + (x * (0.0022596478090061247 + (x * (-0.0005385953053567405 + (x * 0.0003355291926355191)))));
200
+ }
201
+
202
+ // END: polyval_t3
203
+
204
+ // BEGIN: polyval_u
205
+
206
+ /**
207
+ * Evaluates a polynomial.
208
+ *
209
+ * ## Notes
210
+ *
211
+ * - The implementation uses [Horner's rule][horners-method] for efficient computation.
212
+ *
213
+ * [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
214
+ *
215
+ * @param x value at which to evaluate the polynomial
216
+ * @return evaluated polynomial
217
+ */
218
+ static double polyval_u( const double x ) {
219
+ return 0.6328270640250934 + (x * (1.4549225013723477 + (x * (0.9777175279633727 + (x * (0.22896372806469245 + (x * 0.013381091853678766)))))));
220
+ }
221
+
222
+ // END: polyval_u
223
+
224
+ // BEGIN: polyval_v
225
+
226
+ /**
227
+ * Evaluates a polynomial.
228
+ *
229
+ * ## Notes
230
+ *
231
+ * - The implementation uses [Horner's rule][horners-method] for efficient computation.
232
+ *
233
+ * [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
234
+ *
235
+ * @param x value at which to evaluate the polynomial
236
+ * @return evaluated polynomial
237
+ */
238
+ static double polyval_v( const double x ) {
239
+ return 2.4559779371304113 + (x * (2.128489763798934 + (x * (0.7692851504566728 + (x * (0.10422264559336913 + (x * 0.003217092422824239)))))));
240
+ }
241
+
242
+ // END: polyval_v
243
+
244
+ // BEGIN: polyval_w
245
+
246
+ /**
247
+ * Evaluates a polynomial.
248
+ *
249
+ * ## Notes
250
+ *
251
+ * - The implementation uses [Horner's rule][horners-method] for efficient computation.
252
+ *
253
+ * [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
254
+ *
255
+ * @param x value at which to evaluate the polynomial
256
+ * @return evaluated polynomial
257
+ */
258
+ static double polyval_w( const double x ) {
259
+ return 0.08333333333333297 + (x * (-0.0027777777772877554 + (x * (0.0007936505586430196 + (x * (-0.00059518755745034 + (x * (0.0008363399189962821 + (x * -0.0016309293409657527)))))))));
260
+ }
261
+
262
+ // END: polyval_w
263
+
264
+ /* End auto-generated functions. */
265
+
266
+ /**
267
+ * Evaluates the natural logarithm of the gamma function.
268
+ *
269
+ * ## Method
270
+ *
271
+ * 1. Argument reduction for \\(0 < x \leq 8\\). Since \\(\Gamma(1+s) = s \Gamma(s)\\), for \\(x \in \[0,8]\\), we may reduce \\(x\\) to a number in \\(\[1.5,2.5]\\) by
272
+ *
273
+ * ```tex
274
+ * \operatorname{lgamma}(1+s) = \ln(s) + \operatorname{lgamma}(s)
275
+ * ```
276
+ *
277
+ * For example,
278
+ *
279
+ * ```tex
280
+ * \begin{align*}
281
+ * \operatorname{lgamma}(7.3) &= \ln(6.3) + \operatorname{lgamma}(6.3) \\
282
+ * &= \ln(6.3 \cdot 5.3) + \operatorname{lgamma}(5.3) \\
283
+ * &= \ln(6.3 \cdot 5.3 \cdot 4.3 \cdot 3.3 \cdot2.3) + \operatorname{lgamma}(2.3)
284
+ * \end{align*}
285
+ * ```
286
+ *
287
+ * 2. Compute a polynomial approximation of \\(\mathrm{lgamma}\\) around its minimum (\\(\mathrm{ymin} = 1.461632144968362245\\)) to maintain monotonicity. On the interval \\(\[\mathrm{ymin} - 0.23, \mathrm{ymin} + 0.27]\\) (i.e., \\(\[1.23164,1.73163]\\)), we let \\(z = x - \mathrm{ymin}\\) and use
288
+ *
289
+ * ```tex
290
+ * \operatorname{lgamma}(x) = -1.214862905358496078218 + z^2 \cdot \operatorname{poly}(z)
291
+ * ```
292
+ *
293
+ * where \\(\operatorname{poly}(z)\\) is a \\(14\\) degree polynomial.
294
+ *
295
+ * 3. Compute a rational approximation in the primary interval \\(\[2,3]\\). Let \\( s = x - 2.0 \\). We can thus use the approximation
296
+ *
297
+ * ```tex
298
+ * \operatorname{lgamma}(x) = \frac{s}{2} + s\frac{\operatorname{P}(s)}{\operatorname{Q}(s)}
299
+ * ```
300
+ *
301
+ * with accuracy
302
+ *
303
+ * ```tex
304
+ * \biggl|\frac{\mathrm{P}}{\mathrm{Q}} - \biggr(\operatorname{lgamma}(x)-\frac{s}{2}\biggl)\biggl| < 2^{-61.71}
305
+ * ```
306
+ *
307
+ * The algorithms are based on the observation
308
+ *
309
+ * ```tex
310
+ * \operatorname{lgamma}(2+s) = s(1 - \gamma) + \frac{\zeta(2) - 1}{2} s^2 - \frac{\zeta(3) - 1}{3} s^3 + \ldots
311
+ * ```
312
+ *
313
+ * where \\(\zeta\\) is the zeta function and \\(\gamma = 0.5772156649...\\) is the Euler-Mascheroni constant, which is very close to \\(0.5\\).
314
+ *
315
+ * 4. For \\(x \geq 8\\),
316
+ *
317
+ * ```tex
318
+ * \operatorname{lgamma}(x) \approx \biggl(x-\frac{1}{2}\biggr) \ln(x) - x + \frac{\ln(2\pi)}{2} + \frac{1}{12x} - \frac{1}{360x^3} + \ldots
319
+ * ```
320
+ *
321
+ * which can be expressed
322
+ *
323
+ * ```tex
324
+ * \operatorname{lgamma}(x) \approx \biggl(x-\frac{1}{2}\biggr)(\ln(x)-1)-\frac{\ln(2\pi)-1}{2} + \ldots
325
+ * ```
326
+ *
327
+ * Let \\(z = \frac{1}{x}\\). We can then use the approximation
328
+ *
329
+ * ```tex
330
+ * f(z) = \operatorname{lgamma}(x) - \biggl(x-\frac{1}{2}\biggr)(\ln(x)-1)
331
+ * ```
332
+ *
333
+ * by
334
+ *
335
+ * ```tex
336
+ * w = w_0 + w_1 z + w_2 z^3 + w_3 z^5 + \ldots + w_6 z^{11}
337
+ * ```
338
+ *
339
+ * where
340
+ *
341
+ * ```tex
342
+ * |w - f(z)| < 2^{-58.74}
343
+ * ```
344
+ *
345
+ * 5. For negative \\(x\\), since
346
+ *
347
+ * ```tex
348
+ * -x \Gamma(-x) \Gamma(x) = \frac{\pi}{\sin(\pi x)}
349
+ * ```
350
+ *
351
+ * where \\(\Gamma\\) is the gamma function, we have
352
+ *
353
+ * ```tex
354
+ * \Gamma(x) = \frac{\pi}{\sin(\pi x)(-x)\Gamma(-x)}
355
+ * ```
356
+ *
357
+ * Since \\(\Gamma(-x)\\) is positive,
358
+ *
359
+ * ```tex
360
+ * \operatorname{sign}(\Gamma(x)) = \operatorname{sign}(\sin(\pi x))
361
+ * ```
362
+ *
363
+ * for \\(x < 0\\). Hence, for \\(x < 0\\),
364
+ *
365
+ * ```tex
366
+ * \mathrm{signgam} = \operatorname{sign}(\sin(\pi x))
367
+ * ```
368
+ *
369
+ * and
370
+ *
371
+ * ```tex
372
+ * \begin{align*}
373
+ * \operatorname{lgamma}(x) &= \ln(|\Gamma(x)|) \\
374
+ * &= \ln\biggl(\frac{\pi}{|x \sin(\pi x)|}\biggr) - \operatorname{lgamma}(-x)
375
+ * \end{align*}
376
+ * ```
377
+ *
378
+ * <!-- <note> -->
379
+ *
380
+ * Note that one should avoid computing \\(\pi (-x)\\) directly in the computation of \\(\sin(\pi (-x))\\).
381
+ *
382
+ * <!-- </note> -->
383
+ *
384
+ * ## Special Cases
385
+ *
386
+ * ```tex
387
+ * \begin{align*}
388
+ * \operatorname{lgamma}(2+s) &\approx s (1-\gamma) & \mathrm{for\ tiny\ s} \\
389
+ * \operatorname{lgamma}(x) &\approx -\ln(x) & \mathrm{for\ tiny\ x} \\
390
+ * \operatorname{lgamma}(1) &= 0 & \\
391
+ * \operatorname{lgamma}(2) &= 0 & \\
392
+ * \operatorname{lgamma}(0) &= \infty & \\
393
+ * \operatorname{lgamma}(\infty) &= \infty & \\
394
+ * \operatorname{lgamma}(-\mathrm{integer}) &= \pm \infty
395
+ * \end{align*}
396
+ * ```
397
+ *
398
+ * @param x input value
399
+ * @return function value
400
+ *
401
+ * @example
402
+ * double out = stdlib_base_gammaln( 1.0 );
403
+ * // returns 0.0
404
+ */
405
+ double stdlib_base_gammaln( const double x ) {
406
+ uint8_t isNegative;
407
+ int32_t flg;
408
+ double nadj;
409
+ double xc;
410
+ double p3;
411
+ double p2;
412
+ double p1;
413
+ double p;
414
+ double q;
415
+ double t;
416
+ double w;
417
+ double y;
418
+ double z;
419
+ double r;
420
+
421
+ // Special cases: NaN, +-infinity
422
+ if ( stdlib_base_is_nan( x ) || stdlib_base_is_infinite( x ) ) {
423
+ return x;
424
+ }
425
+
426
+ // Special case: 0
427
+ if ( x == 0.0 ) {
428
+ return STDLIB_CONSTANT_FLOAT64_PINF;
429
+ }
430
+ xc = x;
431
+ if ( xc < 0.0 ) {
432
+ isNegative = 1;
433
+ xc = -xc;
434
+ } else {
435
+ isNegative = 0;
436
+ }
437
+
438
+ // If |x| < 2**-56, return -ln(|x|)
439
+ if ( xc < TINY ) {
440
+ return -stdlib_base_ln( xc );
441
+ }
442
+ if ( isNegative ) {
443
+ // If |x| >= 2**52, must be -integer
444
+ if ( xc >= TWO52 ) {
445
+ return STDLIB_CONSTANT_FLOAT64_PINF;
446
+ }
447
+ t = stdlib_base_sinpi( xc );
448
+ if ( t == 0.0 ) {
449
+ return STDLIB_CONSTANT_FLOAT64_PINF;
450
+ }
451
+ nadj = stdlib_base_ln( STDLIB_CONSTANT_FLOAT64_PI / stdlib_base_abs( t * xc ) );
452
+ }
453
+
454
+ // If x equals 1 or 2, return 0
455
+ if ( xc == 1.0 || xc == 2.0 ) {
456
+ return 0.0;
457
+ }
458
+
459
+ // If x < 2, use lgamma(x) = lgamma(x+1) - log(x)
460
+ if ( xc < 2.0 ) {
461
+ if ( xc <= 0.9 ) {
462
+ r = -stdlib_base_ln( xc );
463
+ if ( xc >= ( YMIN - 1.0 + 0.27 ) ) { // 0.7316 <= x <= 0.9
464
+ y = 1.0 - xc;
465
+ flg = 0;
466
+ } else if ( xc >= ( YMIN - 1.0 - 0.27 ) ) { // 0.2316 <= x < 0.7316
467
+ y = xc - ( TC - 1.0 );
468
+ flg = 1;
469
+ } else { // 0 < x < 0.2316
470
+ y = xc;
471
+ flg = 2;
472
+ }
473
+ } else {
474
+ r = 0.0;
475
+ if ( xc >= ( YMIN + 0.27 ) ) { // 1.7316 <= x < 2
476
+ y = 2.0 - xc;
477
+ flg = 0;
478
+ } else if ( xc >= ( YMIN - 0.27 ) ) { // 1.2316 <= x < 1.7316
479
+ y = xc - TC;
480
+ flg = 1;
481
+ } else { // 0.9 < x < 1.2316
482
+ y = xc - 1.0;
483
+ flg = 2;
484
+ }
485
+ }
486
+ switch ( flg ) {
487
+ case 0:
488
+ z = y * y;
489
+ p1 = A1C + ( z * polyval_a1( z ) );
490
+ p2 = z * ( A2C + ( z * polyval_a2( z ) ) );
491
+ p = ( y * p1 ) + p2;
492
+ r += ( p - ( 0.5 * y ) );
493
+ break;
494
+ case 1:
495
+ z = y * y;
496
+ w = z * y;
497
+ p1 = T1C + ( w * polyval_t1( w ) );
498
+ p2 = T2C + ( w * polyval_t2( w ) );
499
+ p3 = T3C + ( w * polyval_t3( w ) );
500
+ p = ( z * p1 ) - ( TT - ( w * ( p2 + ( y * p3 ) ) ) );
501
+ r += ( TF + p );
502
+ break;
503
+ case 2:
504
+ p1 = y * ( UC + ( y * polyval_u( y ) ) );
505
+ p2 = VC + ( y * polyval_v( y ) );
506
+ r += ( -0.5 * y ) + ( p1 / p2 );
507
+ break;
508
+ }
509
+ } else if ( xc < 8.0 ) { // 2 <= x < 8
510
+ flg = stdlib_base_trunc( xc );
511
+ y = xc - flg;
512
+ p = y * ( SC + ( y * polyval_s( y ) ) );
513
+ q = RC + ( y * polyval_r( y ) );
514
+ r = ( 0.5 * y ) + ( p / q );
515
+ z = 1.0; // gammaln(1+s) = ln(s) + gammaln(s)
516
+ switch ( flg ) {
517
+ case 7:
518
+ z *= y + 6.0;
519
+
520
+ /* Falls through */
521
+ case 6:
522
+ z *= y + 5.0;
523
+
524
+ /* Falls through */
525
+ case 5:
526
+ z *= y + 4.0;
527
+
528
+ /* Falls through */
529
+ case 4:
530
+ z *= y + 3.0;
531
+
532
+ /* Falls through */
533
+ case 3:
534
+ z *= y + 2.0;
535
+ r += stdlib_base_ln( z );
536
+ }
537
+ } else if ( xc < TWO56 ) { // 8 <= x < 2**56
538
+ t = stdlib_base_ln( xc );
539
+ z = 1.0 / xc;
540
+ y = z * z;
541
+ w = WC + ( z * polyval_w( y ) );
542
+ r = ( ( xc - 0.5 ) * ( t - 1.0 ) ) + w;
543
+ } else { // 2**56 <= x <= Inf
544
+ r = xc * ( stdlib_base_ln( xc ) - 1.0 );
545
+ }
546
+ if ( isNegative ) {
547
+ r = nadj - r;
548
+ }
549
+ return r;
550
+ }