124600Szliu #ifndef lint
2*24705Selefunt static char sccsid[] =
3*24705Selefunt "@(#)lgamma.c	4.4 (Berkeley) 9/11/85; 1.2 (ucb.elefunt) 09/11/85";
424600Szliu #endif not lint
524600Szliu 
624600Szliu /*
724600Szliu 	C program for floating point log Gamma function
824600Szliu 
9*24705Selefunt 	lgamma(x) computes the log of the absolute
1024600Szliu 	value of the Gamma function.
1124600Szliu 	The sign of the Gamma function is returned in the
1224600Szliu 	external quantity signgam.
1324600Szliu 
1424600Szliu 	The coefficients for expansion around zero
1524600Szliu 	are #5243 from Hart & Cheney; for expansion
1624600Szliu 	around infinity they are #5404.
1724600Szliu 
1824600Szliu 	Calls log, floor and sin.
1924600Szliu */
2024600Szliu 
2124600Szliu #include <math.h>
2224600Szliu #ifdef VAX
2324600Szliu #include <errno.h>
2424600Szliu #endif
2524600Szliu int	signgam = 0;
2624600Szliu static double goobie	= 0.9189385332046727417803297;	/* log(2*pi)/2 */
2724600Szliu static double pi	= 3.1415926535897932384626434;
2824600Szliu 
2924600Szliu #define M 6
3024600Szliu #define N 8
3124600Szliu static double p1[] = {
3224600Szliu 	0.83333333333333101837e-1,
3324600Szliu 	-.277777777735865004e-2,
3424600Szliu 	0.793650576493454e-3,
3524600Szliu 	-.5951896861197e-3,
3624600Szliu 	0.83645878922e-3,
3724600Szliu 	-.1633436431e-2,
3824600Szliu };
3924600Szliu static double p2[] = {
4024600Szliu 	-.42353689509744089647e5,
4124600Szliu 	-.20886861789269887364e5,
4224600Szliu 	-.87627102978521489560e4,
4324600Szliu 	-.20085274013072791214e4,
4424600Szliu 	-.43933044406002567613e3,
4524600Szliu 	-.50108693752970953015e2,
4624600Szliu 	-.67449507245925289918e1,
4724600Szliu 	0.0,
4824600Szliu };
4924600Szliu static double q2[] = {
5024600Szliu 	-.42353689509744090010e5,
5124600Szliu 	-.29803853309256649932e4,
5224600Szliu 	0.99403074150827709015e4,
5324600Szliu 	-.15286072737795220248e4,
5424600Szliu 	-.49902852662143904834e3,
5524600Szliu 	0.18949823415702801641e3,
5624600Szliu 	-.23081551524580124562e2,
5724600Szliu 	0.10000000000000000000e1,
5824600Szliu };
5924600Szliu 
6024600Szliu double
61*24705Selefunt lgamma(arg)
6224600Szliu double arg;
6324600Szliu {
6424600Szliu 	double log(), pos(), neg(), asym();
6524600Szliu 
6624600Szliu 	signgam = 1.;
6724600Szliu 	if(arg <= 0.) return(neg(arg));
6824600Szliu 	if(arg > 8.) return(asym(arg));
6924600Szliu 	return(log(pos(arg)));
7024600Szliu }
7124600Szliu 
7224600Szliu static double
7324600Szliu asym(arg)
7424600Szliu double arg;
7524600Szliu {
7624600Szliu 	double log();
7724600Szliu 	double n, argsq;
7824600Szliu 	int i;
7924600Szliu 
8024600Szliu 	argsq = 1./(arg*arg);
8124600Szliu 	for(n=0,i=M-1; i>=0; i--){
8224600Szliu 		n = n*argsq + p1[i];
8324600Szliu 	}
8424600Szliu 	return((arg-.5)*log(arg) - arg + goobie + n/arg);
8524600Szliu }
8624600Szliu 
8724600Szliu static double
8824600Szliu neg(arg)
8924600Szliu double arg;
9024600Szliu {
9124600Szliu 	double t;
9224600Szliu 	double log(), sin(), floor(), pos();
9324600Szliu 
9424600Szliu 	arg = -arg;
9524600Szliu      /*
9624600Szliu       * to see if arg were a true integer, the old code used the
9724600Szliu       * mathematically correct observation:
9824600Szliu       * sin(n*pi) = 0 <=> n is an integer.
9924600Szliu       * but in finite precision arithmetic, sin(n*PI) will NEVER
10024600Szliu       * be zero simply because n*PI is a rational number.  hence
10124600Szliu       *	it failed to work with our newer, more accurate sin()
10224600Szliu       * which uses true pi to do the argument reduction...
10324600Szliu       *	temp = sin(pi*arg);
10424600Szliu       */
10524600Szliu 	t = floor(arg);
10624600Szliu 	if (arg - t  > 0.5e0)
10724600Szliu 	    t += 1.e0;				/* t := integer nearest arg */
10824600Szliu #ifdef VAX
10924600Szliu 	if (arg == t) {
11024600Szliu 	    extern double infnan();
11124600Szliu 	    return(infnan(ERANGE));		/* +INF */
11224600Szliu 	}
11324600Szliu #endif
11424600Szliu 	signgam = (int) (t - 2*floor(t/2));	/* signgam =  1 if t was odd, */
11524600Szliu 						/*            0 if t was even */
11624600Szliu 	signgam = signgam - 1 + signgam;	/* signgam =  1 if t was odd, */
11724600Szliu 						/*           -1 if t was even */
11824600Szliu 	t = arg - t;				/*  -0.5 <= t <= 0.5 */
11924600Szliu 	if (t < 0.e0) {
12024600Szliu 	    t = -t;
12124600Szliu 	    signgam = -signgam;
12224600Szliu 	}
12324600Szliu 	return(-log(arg*pos(arg)*sin(pi*t)/pi));
12424600Szliu }
12524600Szliu 
12624600Szliu static double
12724600Szliu pos(arg)
12824600Szliu double arg;
12924600Szliu {
13024600Szliu 	double n, d, s;
13124600Szliu 	register i;
13224600Szliu 
13324600Szliu 	if(arg < 2.) return(pos(arg+1.)/arg);
13424600Szliu 	if(arg > 3.) return((arg-1.)*pos(arg-1.));
13524600Szliu 
13624600Szliu 	s = arg - 2.;
13724600Szliu 	for(n=0,d=0,i=N-1; i>=0; i--){
13824600Szliu 		n = n*s + p2[i];
13924600Szliu 		d = d*s + q2[i];
14024600Szliu 	}
14124600Szliu 	return(n/d);
14224600Szliu }
143