/*-
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* The original code, FreeBSD's old svn r93211, contain the following
* attribution:
*
* This code by P. McIlroy, Oct 1992;
*
* The financial support of UUNET Communications Services is greatfully
* acknowledged.
*
* bsdrc/b_tgamma.c converted to long double by Steven G. Kargl.
*/
#include <sys/cdefs.h>
/*
* See bsdsrc/t_tgamma.c for implementation details.
*/
#include <float.h>
#if LDBL_MAX_EXP != 0x4000
#error "Unsupported long double format"
#endif
#include "math.h"
#include "math_private.h"
/* Used in b_log.c and below. */
struct LDouble {
long double a;
long double b;
};
#include "b_logl.c"
#include "b_expl.c"
static const double zero = 0.;
static const volatile double tiny = 1e-300;
/*
* x >= 6
*
* Use the asymptotic approximation (Stirling's formula) adjusted for
* equal-ripples:
*
* log(G(x)) ~= (x-0.5)*(log(x)-1) + 0.5(log(2*pi)-1) + 1/x*P(1/(x*x))
*
* Keep extra precision in multiplying (x-.5)(log(x)-1), to avoid
* premature round-off.
*
* Accurate to max(ulp(1/128) absolute, 2^-66 relative) error.
*/
/*
* The following is a decomposition of 0.5 * (log(2*pi) - 1) into the
* first 12 bits in ln2pi_hi and the trailing 64 bits in ln2pi_lo. The
* variables are clearly misnamed.
*/
static const union ieee_ext_u
ln2pi_hiu = LD80C(0xd680000000000000, -2, 4.18945312500000000000e-01L),
ln2pi_lou = LD80C(0xe379b414b596d687, -18, -6.77929532725821967032e-06L);
#define ln2pi_hi (ln2pi_hiu.extu_ld)
#define ln2pi_lo (ln2pi_lou.extu_ld)
static struct LDouble
ratfun_gam(long double z, long double c)
{
long double p, q, thi, tlo;
struct LDouble r;
q = 1 + z * (Q1 + z * (Q2 + z * (Q3 + z * (Q4 + z * (Q5 +
z * (Q6 + z * (Q7 + z * Q8)))))));
p = P0 + z * (P1 + z * (P2 + z * (P3 + z * (P4 + z * (P5 +
z * (P6 + z * (P7 + z * P8)))))));
p = p / q;
/* Split z into high and low parts. */
thi = (float)z;
tlo = (z - thi) + c;
tlo *= (thi + z);
/* Split (z+c)^2 into high and low parts. */
thi *= thi;
q = thi;
thi = (float)thi;
tlo += (q - thi);
/* Split p/q into high and low parts. */
r.a = (float)p;
r.b = p - r.a;
tlo = tlo * p + thi * r.b + a0_lo;
thi *= r.a; /* t = (z+c)^2*(P/Q) */
r.a = (float)(thi + a0_hi);
r.b = ((a0_hi - r.a) + thi) + tlo;
return (r); /* r = a0 + t */
}
/*
* x < 6
*
* Use argument reduction G(x+1) = xG(x) to reach the range [1.066124,
* 2.066124]. Use a rational approximation centered at the minimum
* (x0+1) to ensure monotonicity.
*
* Good to < 1 ulp. (provably .90 ulp; .87 ulp on 1,000,000 runs.)
* It also has correct monotonicity.
*/
static const union ieee_ext_u
xm1u = LD80C(0xec5b0c6ad7c7edc3, -2, 4.61632144968362341254e-01L);
#define x0 (xm1u.extu_ld)
static const double
left = -0.3955078125; /* left boundary for rat. approx */
static long double
small_gam(long double x)
{
long double t, y, ym1;
struct LDouble yy, r;