#ifndef lint
static char sccsid[] = "@(#)lgamma.c	1.2	(ucb.beef)	3/1/89";
#endif	/* !defined(lint) */
/* 
 * This routine calculates the log(GAMMA) function for a positive real
 *   argument x.  Computation is based on an algorithm outlined in
 *   references 1 and 2.  The program uses rational functions that
 *   theoretically approximate log(GAMMA) to at least 18 significant
 *   decimal digits.  The approximation for x > 12 is from reference
 *   3, while approximations for x < 12.0 are similar to those in
 *   reference 1, but are unpublished.  The accuracy achieved depends
 *   on the arithmetic system, the compiler, the intrinsic functions,
 *   and proper selection of the machine-dependent constants.
 * 
 **********************************************************************
 **********************************************************************
 * 
 * Explanation of machine-dependent constants
 * 
 * beta   - radix for the floating-point representation
 * maxexp - the smallest positive power of beta that overflows
 * XBIG   - largest argument for which LN(GAMMA(x)) is representable
 *          in the machine, i.e., the solution to the equation
 *                  LN(GAMMA(XBIG)) = beta**maxexp
 * XINF   - largest machine representable floating-point number;
 *          approximately beta**maxexp.
 * EPS    - The smallest positive floating-point number such that
 *          1.0+EPS > 1.0
 * FRTBIG - Rough estimate of the fourth root of XBIG
 * 
 * 
 *     Approximate values for some important machines are:
 * 
 *                           beta      maxexp         XBIG
 * 
 * CRAY-1        (S.P.)        2        8191       9.62E+2461
 * Cyber 180/855
 *   under NOS   (S.P.)        2        1070       1.72E+319
 * IEEE (IBM/XT,
 *   SUN, etc.)  (S.P.)        2         128       4.08E+36
 * IEEE (IBM/XT,
 *   SUN, etc.)  (D.P.)        2        1024       2.55D+305
 * IBM 3033      (D.P.)       16          63       4.29D+73
 * VAX D-Format  (D.P.)        2         127       2.05D+36
 * VAX G-Format  (D.P.)        2        1023       1.28D+305
 *
 * 
 *                           XINF        EPS        FRTBIG
 * 
 * CRAY-1        (S.P.)   5.45E+2465   7.11E-15    3.13E+615
 * Cyber 180/855
 *   under NOS   (S.P.)   1.26E+322    3.55E-15    6.44E+79
 * IEEE (IBM/XT,
 *   SUN, etc.)  (S.P.)   3.40E+38     1.19E-7     1.42E+9
 * IEEE (IBM/XT,
 *   SUN, etc.)  (D.P.)   1.79D+308    2.22D-16    2.25D+76
 * IBM 3033      (D.P.)   7.23D+75     2.22D-16    2.56D+18
 * VAX D-Format  (D.P.)   1.70D+38     1.39D-17    1.20D+9
 * VAX G-Format  (D.P.)   8.98D+307    1.11D-16    1.89D+76
 * 
 ***************************************************************
 ***************************************************************
 * 
 * Error returns
 * 
 *  The program returns the value XINF for x <= 0.0 or when
 *     overflow would occur.  The computation is believed to 
 *     be free of underflow and overflow.
 * 
 * 
 * Intrinsic functions required are:
 * 
 *      log
 * 
 * 
 * References:
 * 
 *  1) W. J. Cody and K. E. Hillstrom, 'Chebyshev Approximations for
 *     the Natural Logarithm of the Gamma Function,' Math. Comp. 21,
 *     1967, pp. 198-203.
 * 
 *  2) K. E. Hillstrom, ANL/AMD Program ANLC366S, DGAMMA/DLGAMA, May,
 *     1969.
 * 
 *  3) Hart, Et. Al., Computer Approximations, Wiley and sons, New
 *     York, 1968.
 * 
 * 
 *  Authors: W. J. Cody and L. Stoltz
 *           Argonne National Laboratory
 * 
 *  Latest modification: June 16, 1988
 */
#if defined(__STDC__) || defined(__GNUC__)
extern double log(double);
#else
extern double log();
#endif
					/* Machine dependent parameters */
#if defined(vax) || defined(tahoe)
#define	XBIG	(double)2.05e36
#define	XINF	(double)1.70e38
#define	EPS	(double)1.39e-17
#define	FRTBIG	(double)1.20e9
#else	/* defined(vax) || defined(tahoe) */
#define	XBIG	(double)2.55e305
#define	XINF	(double)1.79e308
#define	EPS	(double)2.22e-16
#define	FRTBIG	(double)2.25e76
#endif	/* defined(vax) || defined(tahoe) */
					/* Mathematical constants */
#define	ONE	(double)1
#define	HALF	(double)0.5
#define	TWELVE	(double)12
#define	ZERO	(double)0
#define	FOUR	(double)4
#define	THRHAL	(double)1.5
#define	TWO	(double)2
#define	PNT68	(double)0.6796875
#define	SQRTPI	(double)0.9189385332046727417803297

/*
 * Numerator and denominator coefficients for rational minimax Approximation
 * over (0.5,1.5).
 */
static double D1 = -5.772156649015328605195174e-1;
static double P1[] = {
	4.945235359296727046734888e0,
	2.018112620856775083915565e2,
	2.290838373831346393026739e3,
	1.131967205903380828685045e4,
	2.855724635671635335736389e4,
	3.848496228443793359990269e4,
	2.637748787624195437963534e4,
	7.225813979700288197698961e3,
};
static double Q1[] = {
	6.748212550303777196073036e1,
	1.113332393857199323513008e3,
	7.738757056935398733233834e3,
	2.763987074403340708898585e4,
	5.499310206226157329794414e4,
	6.161122180066002127833352e4,
	3.635127591501940507276287e4,
	8.785536302431013170870835e3,
};

/*
 * Numerator and denominator coefficients for rational minimax Approximation
 * over (1.5,4.0).
 */
static double D2 = 4.227843350984671393993777e-1;
static double P2[] = {
	4.974607845568932035012064e0,
	5.424138599891070494101986e2,
	1.550693864978364947665077e4,
	1.847932904445632425417223e5,
	1.088204769468828767498470e6,
	3.338152967987029735917223e6,
	5.106661678927352456275255e6,
	3.074109054850539556250927e6,
};
static double Q2[] = {
	1.830328399370592604055942e2,
	7.765049321445005871323047e3,
	1.331903827966074194402448e5,
	1.136705821321969608938755e6,
	5.267964117437946917577538e6,
	1.346701454311101692290052e7,
	1.782736530353274213975932e7,
	9.533095591844353613395747e6,
};

/*
 * Numerator and denominator coefficients for rational minimax Approximation
 * over (4.0,12.0).
 */
static double D4 = 1.791759469228055000094023e0;
static double P4[] = {
	1.474502166059939948905062e4,
	2.426813369486704502836312e6,
	1.214755574045093227939592e8,
	2.663432449630976949898078e9,
	2.940378956634553899906876e10,
	1.702665737765398868392998e11,
	4.926125793377430887588120e11,
	5.606251856223951465078242e11,
};
static double Q4[] = {
	2.690530175870899333379843e3,
	6.393885654300092398984238e5,
	4.135599930241388052042842e7,
	1.120872109616147941376570e9,
	1.488613728678813811542398e10,
	1.016803586272438228077304e11,
	3.417476345507377132798597e11,
	4.463158187419713286462081e11,
};

/*
 * Coefficients for minimax approximation over (12, INF).
 */
static double C[] = {
	-1.910444077728e-03,
	 8.4171387781295e-04,
	-5.952379913043012e-04,
	 7.93650793500350248e-04,
	-2.777777777777681622553e-03,
	 8.333333333333333331554247e-02,
	 5.7083835261e-03,
};

double
#if defined(__STDC__) || defined(__GNUC__)
lgamma(double x)
#else
lgamma(x)
double x;
#endif
{
	register i;
	double res,corr,xden,xnum,dtmp;

#define	y x
	if (y > ZERO && y <= XBIG) {
		if (y <= EPS)
			res = -log(y);
		else if (y <= THRHAL) {			/*  EPS < x <= 1.5 */
#define	xm1 dtmp
			if (y < PNT68) {
				corr = -log(y);
				xm1 = y;
			}
			else {
				corr = ZERO;
				xm1 = y-HALF; xm1 -= HALF;
			}
			if (y <= HALF || y >= PNT68) {
				xden = ONE;
				xnum = ZERO;
				for (i = 0; i <= 7; i++) {
					xnum = xnum*xm1+P1[i];
					xden = xden*xm1+Q1[i];
				}
				res = xnum/xden; res = corr+xm1*(D1+xm1*res);
#undef	xm1
			}
			else {
#define	xm2 dtmp
				xm2 = y-HALF; xm2 -= HALF;
				xden = ONE;
				xnum = ZERO;
				for (i = 0; i <= 7; i++) {
					xnum = xnum*xm2+P2[i];
					xden = xden*xm2+Q2[i];
				}
				res = xnum/xden; res = corr+xm2*(D2+xm2*res);
			}
		}
		else if (y <= FOUR) {			/*  1.5 < x <= 4.0 */
			xm2 = y-TWO;
			xden = ONE;
			xnum = ZERO;
			for (i = 0; i <= 7; i++) {
				xnum = xnum*xm2+P2[i];
				xden = xden*xm2+Q2[i];
			}
			res = xnum/xden; res = xm2*(D2+xm2*res);
#undef	xm2
		}
		else if (y <= TWELVE) {			/*  4.0 < x <= 12.0 */
#define	xm4 dtmp
			xm4 = y-FOUR;
			xden = -ONE;
			xnum = ZERO;
			for (i = 0; i <= 7; i++) {
				xnum = xnum*xm4+P4[i];
				xden = xden*xm4+Q4[i];
			}
			res = xnum/xden; res = D4+xm4*res;
#undef	xm4
		}
		else {					/*  x >= 12.0 */
			res = ZERO;
			if (y <= FRTBIG) {
				res = C[6];
#define	ysq dtmp
				ysq = y*y;
				for (i = 0; i <= 5; i++)
					res = res/ysq+C[i];
#define	ysq dtmp
			}
			res /= y;
			corr = log(y);
			res += SQRTPI; res -= HALF*corr;
			res += y*(corr-ONE);
		}
#undef	y
	}
	else					/*  Return for bad arguments */
		res = XINF;
	return res;
}
