#include "mconf.h"
#ifdef DEC
#define EPS 1.0e-14
#define EPS2 1.0e-11
#endif
#ifdef IBMPC
#define EPS 1.0e-13
#define EPS2 1.0e-10
#endif
#ifdef MIEEE
#define EPS 1.0e-13
#define EPS2 1.0e-10
#endif
#ifdef UNK
#define EPS 1.0e-13
#define EPS2 1.0e-10
#endif
#define ETHRESH 1.0e-12
#ifdef ANSIPROT
extern
double
md_fabs (
double
);
extern
double
md_pow (
double
,
double
);
extern
double
md_round (
double
);
extern
double
md_gamma (
double
);
extern
double
md_log (
double
);
extern
double
md_exp (
double
);
extern
double
psi (
double
);
static
double
hyt2f1(
double
,
double
,
double
,
double
,
double
*);
static
double
hys2f1(
double
,
double
,
double
,
double
,
double
*);
double
hyp2f1(
double
,
double
,
double
,
double
);
#else
double
md_fabs(), md_pow(), md_round(), md_gamma(), md_log(), md_exp(), psi();
static
double
hyt2f1();
static
double
hys2f1();
double
hyp2f1();
#endif
extern
double
MAXNUM, MACHEP;
double
hyp2f1( a, b, c, x )
double
a, b, c, x;
{
double
d, d1, d2, e;
double
p, q, r, s, y, ax;
double
ia, ib, ic, id, err;
int
flag, i, aid;
err = 0.0;
ax = md_fabs(x);
s = 1.0 - x;
flag = 0;
ia = md_round(a);
ib = md_round(b);
if
( a <= 0 )
{
if
( md_fabs(a-ia) < EPS )
flag |= 1;
}
if
( b <= 0 )
{
if
( md_fabs(b-ib) < EPS )
flag |= 2;
}
if
( ax < 1.0 )
{
if
( md_fabs(b-c) < EPS )
{
y = md_pow( s, -a );
goto
hypdon;
}
if
( md_fabs(a-c) < EPS )
{
y = md_pow( s, -b );
goto
hypdon;
}
}
if
( c <= 0.0 )
{
ic = md_round(c);
if
( md_fabs(c-ic) < EPS )
{
if
( (flag & 1) && (ia > ic) )
goto
hypok;
if
( (flag & 2) && (ib > ic) )
goto
hypok;
goto
hypdiv;
}
}
if
( flag )
goto
hypok;
if
( ax > 1.0 )
goto
hypdiv;
p = c - a;
ia = md_round(p);
if
( (ia <= 0.0) && (md_fabs(p-ia) < EPS) )
flag |= 4;
r = c - b;
ib = md_round(r);
if
( (ib <= 0.0) && (md_fabs(r-ib) < EPS) )
flag |= 8;
d = c - a - b;
id = md_round(d);
q = md_fabs(d-id);
if
( md_fabs(ax-1.0) < EPS )
{
if
( x > 0.0 )
{
if
( flag & 12 )
{
if
( d >= 0.0 )
goto
hypf;
else
goto
hypdiv;
}
if
( d <= 0.0 )
goto
hypdiv;
y = md_gamma(c)*md_gamma(d)/(md_gamma(p)*md_gamma(r));
goto
hypdon;
}
if
( d <= -1.0 )
goto
hypdiv;
}
if
( d < 0.0 )
{
y = hyt2f1( a, b, c, x, &err );
if
( err < ETHRESH )
goto
hypdon;
err = 0.0;
aid = 2 - id;
e = c + aid;
d2 = hyp2f1(a,b,e,x);
d1 = hyp2f1(a,b,e+1.0,x);
q = a + b + 1.0;
for
( i=0; i<aid; i++ )
{
r = e - 1.0;
y = (e*(r-(2.0*e-q)*x)*d2 + (e-a)*(e-b)*x*d1)/(e*r*s);
e = r;
d1 = d2;
d2 = y;
}
goto
hypdon;
}
if
( flag & 12 )
goto
hypf;
hypok:
y = hyt2f1( a, b, c, x, &err );
hypdon:
if
( err > ETHRESH )
{
mtherr(
"hyp2f1"
, PLOSS );
}
return
(y);
hypf:
y = md_pow( s, d ) * hys2f1( c-a, c-b, c, x, &err );
goto
hypdon;
hypdiv:
mtherr(
"hyp2f1"
, OVERFLOW );
return
( MAXNUM );
}
static
double
hyt2f1( a, b, c, x, loss )
double
a, b, c, x;
double
*loss;
{
double
p, q, r, s, t, y, d, err, err1;
double
ax, id, d1, d2, e, md_y1;
int
i, aid;
err = 0.0;
s = 1.0 - x;
if
( x < -0.5 )
{
if
( b > a )
y = md_pow( s, -a ) * hys2f1( a, c-b, c, -x/s, &err );
else
y = md_pow( s, -b ) * hys2f1( c-a, b, c, -x/s, &err );
goto
done;
}
d = c - a - b;
id = md_round(d);
if
( x > 0.9 )
{
if
( md_fabs(d-id) > EPS )
{
y = hys2f1( a, b, c, x, &err );
if
( err < ETHRESH )
goto
done;
q = hys2f1( a, b, 1.0-d, s, &err );
q *= md_gamma(d) /(md_gamma(c-a) * md_gamma(c-b));
r = md_pow(s,d) * hys2f1( c-a, c-b, d+1.0, s, &err1 );
r *= md_gamma(-d)/(md_gamma(a) * md_gamma(b));
y = q + r;
q = md_fabs(q);
r = md_fabs(r);
if
( q > r )
r = q;
err += err1 + (MACHEP*r)/y;
y *= md_gamma(c);
goto
done;
}
else
{
if
( id >= 0.0 )
{
e = d;
d1 = d;
d2 = 0.0;
aid = id;
}
else
{
e = -d;
d1 = 0.0;
d2 = d;
aid = -id;
}
ax = md_log(s);
y = psi(1.0) + psi(1.0+e) - psi(a+d1) - psi(b+d1) - ax;
y /= md_gamma(e+1.0);
p = (a+d1) * (b+d1) * s / md_gamma(e+2.0);
t = 1.0;
do
{
r = psi(1.0+t) + psi(1.0+t+e) - psi(a+t+d1)
- psi(b+t+d1) - ax;
q = p * r;
y += q;
p *= s * (a+t+d1) / (t+1.0);
p *= (b+t+d1) / (t+1.0+e);
t += 1.0;
}
while
( md_fabs(q/y) > EPS );
if
( id == 0.0 )
{
y *= md_gamma(c)/(md_gamma(a)*md_gamma(b));
goto
psidon;
}
md_y1 = 1.0;
if
( aid == 1 )
goto
nosum;
t = 0.0;
p = 1.0;
for
( i=1; i<aid; i++ )
{
r = 1.0-e+t;
p *= s * (a+t+d2) * (b+t+d2) / r;
t += 1.0;
p /= t;
md_y1 += p;
}
nosum:
p = md_gamma(c);
md_y1 *= md_gamma(e) * p / (md_gamma(a+d1) * md_gamma(b+d1));
y *= p / (md_gamma(a+d2) * md_gamma(b+d2));
if
( (aid & 1) != 0 )
y = -y;
q = md_pow( s, id );
if
( id > 0.0 )
y *= q;
else
md_y1 *= q;
y += md_y1;
psidon:
goto
done;
}
}
y = hys2f1( a, b, c, x, &err );
done:
*loss = err;
return
(y);
}
static
double
hys2f1( a, b, c, x, loss )
double
a, b, c, x;
double
*loss;
{
double
f, g, h, k, m, s, u, umax;
int
i;
i = 0;
umax = 0.0;
f = a;
g = b;
h = c;
s = 1.0;
u = 1.0;
k = 0.0;
do
{
if
( md_fabs(h) < EPS )
{
*loss = 1.0;
return
( MAXNUM );
}
m = k + 1.0;
u = u * ((f+k) * (g+k) * x / ((h+k) * m));
s += u;
k = md_fabs(u);
if
( k > umax )
umax = k;
k = m;
if
( ++i > 10000 )
{
*loss = 1.0;
return
(s);
}
}
while
( md_fabs(u/s) > MACHEP );
*loss = (MACHEP*umax)/md_fabs(s) + (MACHEP*i);
return
(s);
}