Sindbad~EG File Manager
/* M. Beeson, following ideas in Numerical Recipes pp. 108-109
compute factorials, Gamma function, binomial coefficients
producing decimal answers
12.17.91 original date
3.24.95 modified
1.7.97 modified dbinomial to avoid warnings in VC
5.13.13 modified dbinomial to compute 1 for x choose 0 and work on non-integer inputs
*/
#include <math.h>
#include <assert.h>
#include "terms.h"
#include "deval.h"
#include "baker.h" /* for PI_DECIMAL */
#define BADVAL 2.0E300
double gammln(double);
int gamma2(double, double *); /* called by deval.c */
int dfactorial(double, double *);
int dbinomial(double,double, double *);
static double factln(int n);
/*_____________________________________________________________________*/
#define TABLEDIM 33
int dfactorial(double x, double *ansp)
/* compute factorial of x if x > 0 -- integer or not */
/* if isn't an int, but is very close to an int k, will not fail but
will compute k factorial */
/* return 0 to indicate success */
/* return 18 if factorial would overflow a double */
{ static int ntop = 4;
long k;
static double a[TABLEDIM] = {1.0, 1.0, 2.0, 6.0, 24.0}; /* fill in as needed */
int j;
if(x<0)
return 19;
if(x > 170 ) /* determined by exact calculation of 170! */
return 18;
if(x > 32)
{ *ansp = exp(gammln(x + 1.0));
return 0;
}
if(nearint(x,&k) && ((k >> 17) == 0))
{ x = (double) k; /* fix a slight deviation from an integer,
which would probably be due to roundoff error. */
if(k < TABLEDIM) /* fill in the static lookup table */
{ while(ntop < k)
{ j = ntop++;
a[ntop] = a[j]*ntop;
}
*ansp = a[(int) k];
return 0;
}
}
*ansp = exp(gammln(x+1.0));
return 0;
}
/*___________________________________________________________________*/
#define NFTABLE 100 /* was 100 in Numerical Recipes; until 5.13.2013 was 25 in MathXpert */
static double factln(int n)
/* returns ln(n!) */
/* never overflows */
/* n is assumed to be positive */
{ static double a[NFTABLE]; /* table of function values */
if(n <= 1)
return 0.0;
if(n < NFTABLE) /* use or add to the table */
return a[n] ? a[n] : (a[n]=gammln(n+1.0));
return gammln(n+1.0); /* out of range of the table */
}
#undef NFTABLE
/*___________________________________________________________________*/
int dbinomial( double x, double y, double *ansp)
/* compute x choose y; x and y do not have to be integers, but y has to be nonnegative */
{ double arg;
long xx,yy;
int err;
if(fabs(y) < 1.0e-7) // y is zero within roundoff error
{ *ansp = 1.0; // x choose 0 is always 1
return 0;
}
if(y < 0.0 )
return 1;
if (x > y)
y = x-y; // take advantage of symmetry. This is valid even if the arguments are not integers.
if (nearint(x,&xx) && xx >= 0 && (xx >> 17) == 0 /* x has only 16 bits */
&& nearint(y,&yy) && (yy >> 17) == 0 /* and y also has only 16 bits */
)
{ arg = factln((int) xx) - factln((int)yy) - factln((int)(xx-yy));
if(arg >= 709) /* 308 ln 10 */
{ *ansp = BADVAL;
return 16;
}
*ansp = floor(0.5 + exp(arg));
return 0;
}
else if(nearint(x,&xx) && xx < 0)
{ // then you can't use gammln because the gamma function has singularities at negative integers
// but the answer is binomial(|xx| + y-1,y), up to sign
if(!nearint(y,&yy))
{ *ansp = BADVAL;
return 1;
}
err = dbinomial(-x + y-1,y,ansp);
if(err)
return err;
if(yy % 2)
*ansp = -*ansp;
return 0;
}
else
{ arg = gammln(x+1) - gammln(y+1) - gammln(x-y+1);
if(arg != arg) // check for a QNaN error ("quiet NaN", causes no exception)
// such an error should only happen if the function is incorrectly called so that x-y+1 is negative
{ *ansp = BADVAL;
return 16;
}
if(arg >= 709) /* 308 ln 10 */
{ *ansp = BADVAL;
return 16;
}
if(x-y+1 > 0 || (((int) floor(y-x-1 + 0.001)) % 2) ==1)
*ansp = exp(arg);
else *ansp = -exp(arg);
if(*ansp != *ansp) // check for a QNaN error
{ *ansp = BADVAL;
return 16;
}
return 0;
}
}
/* ______________________________________________________*/
double gammln(double xx)
/* computes ln of the gamma function, for xx > 1;
see Numerical Recipes in C, 2nd edition, pages 213--14.
For xx < 1 it computes ln |gamma(x)|, using formula 6.14, p. 213 of
Numerical Recipes, 2nd ed. This is used in dbinomial (only).
*/
{ double x,tmp,ser;
static double cof[6]={76.18009172947146,-86.50532032941677,24.01409824083091,
-1.23173951572450155,0.1208650973866179e-2,-0.5395239384953e-5};
int j;
long k;
if(nearint(xx,&k) && k==1)
{ // gamma(1)= 0! = 1, so its log is 0
return 0.0;
}
if(xx < 1.0)
{ double y = sin(PI_DECIMAL*xx);
if(nearint(y,&k) && k==0)
return BADVAL; // gamma has singularities at negative integer values;
if(xx > 0.0)
return log((1.0-xx)*PI_DECIMAL) - gammln(2.0-xx) - log(y);
else /* xx < 0.0 */
return log(PI_DECIMAL) - gammln(1.0-xx) - log(fabs(y));
return 0;
}
x=xx-1.0;
tmp=x+5.5;
tmp -= (x+0.5)*log(tmp);
ser=1.0;
for (j=0;j<=5;j++)
{ x += 1.0;
ser += cof[j]/x;
}
return -tmp+log(2.50662827465*ser);
}
/*_____________________________________________________________*/
int gamma2 (double x, double *ansp)
{ long k;
double y;
if( x > 1.0)
{ *ansp = exp(gammln(x));
return 0;
}
if(nearint(x,&k) && k==1)
{ *ansp = 1.0;
return 0;
}
/* and now for x<1 : Gamma(x) = pi x/(Gamma(2-x)sin(pi x))
or pi /(Gamma(1-x)sin( pi x))
(p. 167 Numerical Recipes 1st ed., p. 213 2nd edition.) */
y = sin(PI_DECIMAL*x);
if(nearint(y,&k) && k==0)
{ *ansp = BADVAL;
return 26;
}
if(x > 0.0)
*ansp = (1.0-x) * PI_DECIMAL * exp(- gammln(2.0-x))/y;
else /* x < 0.0 */
*ansp = PI_DECIMAL * exp(- gammln(1.0-x))/y;
return 0;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists