/* * special_functions - special functions (e.g.: gamma, zeta, psi) * * Copyright (C) 2013 Christoph Zurnieden * * special_functions is open software; you can redistribute it and/or modify * it under the terms of the version 2.1 of the GNU Lesser General Public * License as published by the Free Software Foundation. * * special_functions is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * A copy of version 2.1 of the GNU Lesser General Public License is * distributed with calc under the filename COPYING-LGPL. You should have * received a copy with calc; if not, write to Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * @(#) $Revision: 30.4 $ * @(#) $Id: specialfunctions.cal,v 30.4 2013/08/11 08:41:38 chongo Exp $ * @(#) $Source: /usr/local/src/bin/calc/cal/RCS/specialfunctions.cal,v $ * * Under source code control: 2013/08/11 01:31:28 * File existed as early as: 2013 */ /* * hide internal function from resource debugging */ static resource_debug_level; resource_debug_level = config("resource_debug", 0); /* get dependencies */ read -once zeta2; /* zeta2(x,s) is in the extra file "zeta2.cal" because of a different license: GPL instead of LGPL. */ define zeta(s) { /* Not the best way, I'm afraid, but a way. */ return hurwitzzeta(s,1); } define psi0(z){ local i k m x y eps_digits eps ret; /* One can use the Stirling series, too, which might be faster for some values. The series used here converges very fast but needs a lot of bernoulli numbers which are quite expensive to compute. */ eps = epsilon(); epsilon(eps*1e-3); if(isint(z) && z<=0)return newerror("psi(z); z is a negative integer or 0"); if(re(z) < 0){ return ( pi() * cot(pi() * (0-z) ) ) + psi0(1-z); } eps_digits = digits(1/epsilon()); /* R.W. Gosper has r = .41 as the relation, empirical tests showed that for d<100 r = .375 is sufficient and r = .396 for d<200. It does not save much time but for a long series even a little can grow large. */ if(eps_digits < 100) k = 2 * ceil(.375 * eps_digits); else if(eps_digits < 200) k = 2 * ceil(.395 * eps_digits); else k = 2 * ceil(11/27 * eps_digits); m = 0; y = (z+k)^2; x = 0.0; /* There is a chance to speed up the first partial sum with binary splitting. The second partial sum is dominated by the calculation of the Bernoulli numbers but can profit from binary splitting when the Bernoulli numbers are already cached. */ for(i = 1; i <= (k/2); i++){ m = 1 / ( z + 2*i - 1) + 1 / ( z + 2*i -2 ) + m; x = ( x + bernoulli(k-2*i+2) / (k-2*i+2) ) / y ; } ret = ln(z+k) - 1 / ( 2 * (z+k) ) - x - m ; epsilon(eps); return ret; } define psi(z) { return psi0(z); } define polygamma(m,z) { /* TODO: http://functions.wolfram.com/GammaBetaErf/PolyGamma2/16/01/01/0002/ */ if(!isint(m))return newerror("polygamma(m,z); m not an integer"); if( m<0 )return newerror("polygamma(m,z); m is < 0"); /* Reflection formula not implemented yet, needs cot-differentiation http://functions.wolfram.com/ElementaryFunctions/Cot/20/02/0003/ which is not implemented yet. */ if( m == 0){ return psi0(z); } /* use factorial for m a (small) integer? use lngamma for m large? */ if(isodd(m+1)){ return (-1)* gamma(m+1) * hurwitzzeta(m+1,z) } return gamma(m+1) * hurwitzzeta(m+1,z); } /* Cache for the variable independent coefficients in the sum for the Gamma-function. */ static __CZ__Ck; /* Log-Gamma function for Re(z) > 0. */ define __CZ__lngammarp(z) { local epsilon accuracy a factrl sum n ret holds_enough term; epsilon = epsilon(); accuracy = digits(int(1/epsilon)) + 3; epsilon(1e-18); a = ceil(1.252850440912568095810522965 * accuracy); epsilon(epsilon*10^(-(digits(1/epsilon)//2))); holds_enough = 0; if(size( __CZ__Ck) != a) { __CZ__Ck = mat[a]; holds_enough = 1; } factrl = 1.0; __CZ__Ck[0] = sqrt(2*pi()); /* c_0*/ for(n = 1; n < a; n++){ if(holds_enough == 1){ __CZ__Ck[n] = (a - n)^(n - 0.5) * exp(a - n) /factrl; factrl *= -n } } sum = __CZ__Ck[0]; for (n = 1; n < a; n++){ sum += __CZ__Ck[n]/(z+n); } ret = ln(sum)+(-(z+a)) + ln(z+a)*( z+0.5); ret = ret-ln(z); /* Will take some time for large im(z) but almost all time is spend above in that case. */ if(im(ret)) ret = re(ret) + ln( exp( im(ret) *1i ) ); epsilon(epsilon); return ret; } /* Simple lngamma with low precision*/ define __CZ__lngamma_lanczos(z){ local a k g zghalf lanczos; mat lanczos[15] = { 9.9999999999999709182e-1, 5.7156235665862923516e1, -5.9597960355475491248e1, 1.4136097974741747173e1, -4.9191381609762019978e-1, 3.3994649984811888638e-5, 4.6523628927048576010e-5, -9.8374475304879566105e-5, 1.5808870322491249322e-4, -2.1026444172410489480e-4, 2.1743961811521265523e-4, -1.6431810653676390482e-4, 8.4418223983852751308e-5, -2.6190838401581411237e-5, 3.6899182659531626821e-6 }; g = 607/128; z = z-1; a = 0; for(k = 12; k >= 1; k--){ a += lanczos[k]/(z+k); } a += lanczos[0]; zghalf = z + g + .5; return ( ln(sqrt(2*pi())) + ln(a) -zghalf ) + (z+.5)*ln( zghalf ); } /* Prints the Spouge coefficients actually in use. */ define __CZ__print__CZ__Ck(){ local k; if(size(__CZ__Ck) <=1){ __CZ__lngammarp(2.2-2.2i); } for(k=0;k= mat[0,2]*/ static __CZ__stirling_params; define __CZ__lngstirling(z,n){ local k head sum z2 bernterm zz; head = (z-1/2)*ln(z)-z+(ln(2*pi())/2); sum = 0; bernterm=0; zz = z; z2 = z^2; if(size(__CZ__precomp_stirling) 0){ flag = 0; eps = epsilon(); epsilon(eps*1e-3); /* Compute values on the real line with Spouge's algorithm*/ if(!im(z)){# ret = __CZ__lngammarp(z); epsilon(eps); return ret; } /* Do the rest with the Stirling series.*/ /* This code repeats down under. Booh! */ /* Make it a positive im(z) */ if(im(z)<0){ z = conj(z); flag = 1; } /* Evaluate the number of terms needed */ decdigits = floor( digits(1/eps) ); /* 20 dec. digits is the default in calc(?)*/ if(decdigits <= 21){ /* set 20 as the minimum */ epsilon(1e-22); increasedby = 0; /* inflate z */ Z=z; while(abs(z) < 10){ z++; increasedby++; } ret = __CZ__lngstirling(z,11); /* deflate z */ if(increasedby > 1){ for(k=0;k 0 produces the rest as a by-product. Nevertheless we could use a faster asymptotic approximation. We increment the number of terms "k" in R(z,k) until the result quits incrementing (it may even get smaller!). If the result is still smaller than the current precision we increment "z" with fixed "k" untill the result quits incrementing. The results, the current precision, abs(re(z)) and "k" are kept. BTW: incrementing the number of terms might be more costly than incrementing "z" -- computing large Bernoulli numbers vs. computing a large number of complex logarithms is a fight with a hard to know result -- and that the series isn't convergent is of not much help either. E.g: R(25,68) = 71 max R(50,55) = 101 R(50,145) = 140 max R(60,170) = 167 max R(70,209) = 195 max R(75,173) = 200 max R(80,147) = 200 max R(90,124) = 200 max R(100,111) = 200 max Bernoulli(222) has a denominator of 9388 with a 254 digit numerator. Computing up to 100 complex logarithms on the other side ... D.E.G. Hare has found the bounds |im(z)| > .37d or re(z) >= 0 and |z| > .52d to be usefull to compute "z" to d digits accuracy. The numbers correspond to the table above. To avoid repeated expensive computation, the result is cached together with the current precision. It might be a good idea to keep it more permanently in a config-file? */ d37 = decdigits * .37; d52 = decdigits * .52; termcount = ceil(d52); if(abs(z) >= d52){ if(abs(im(z))>= d37 ) termcount = ceil(d37); else termcount = ceil(d52); } Z=z; increasedby = 0; /* inflate z */ if( abs(im(z))>= d37){ while(abs(z) < d52+1){ z++; increasedby++; } } else{ tmp = R(z,termcount); tmp2 = tmp; while(tmp2 < decdigits){ z++; increasedby++; tmp2 = R(z,termcount); if(tmp2 < tmp) return newerror("lngamma(1): something happend that " "should not have happend"); } } corr = ceil( re(z)/2 -3/4 - kroneckerdelta(im(z))/4); ret = __CZ__lngstirling(z,termcount); /* deflate z */ if(increasedby > 1){ for(k=0;k 0) */ else{/* re(z)<0 */ eps = epsilon(); epsilon(eps*1e-3); /* Use Spouge's approximation on the real line */ if(!im(z)){ /* reflection */ ret = ln( pi() / sin(pi() *z ) ) - __CZ__lngammarp(1-z); /* it is log(gamma) and im(log(even(-x))) = k\pi, therefore: */ if(abs(z) <= 1/2) ret = re(ret) - pi()*1i; else if( isodd(floor(abs(re(z)))) ){ ret = re(ret) + ( ceil(abs(z)) * pi() * 1i); } else if( iseven(floor(abs(re(z)))) ){ /* < n+1/2 */ if(iseven(floor(abs(z)))){ ret = re(ret) + ( ceil(abs(z)-1/2 -1 ) * pi() * 1i); } else{ ret = re(ret) + ( ceil(abs(z) -1/2 ) * pi() * 1i); } } epsilon(eps); return ret; }/*if(!im(z))*/ /* Use Stirlinsg approximation for the rest of the complex plane */ else{ /* Make it a positive im(z) */ if(im(z)<0){ z = conj(z); flag = 1; } /* Evaluate the number of terms needed */ decdigits = floor( digits(1/eps) ); /* Evaluate the correction term for the imaginary part needed because of the reflection. See D. E. G. Hare, "Computing the Principal Branch of log-Gamma", Journal of Algorithms 25(2):221-236 (1997) http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.84.2063 */ /* 20 dec. digits is the default in calc(?)*/ if(decdigits <= 21){ /* set 20 as the minimum */ epsilon(1e-22); termcount = 11; increasedby = 0; Z=z; /* inflate z */ if( im(z)>= digits(1/epsilon()) * .37){ while(abs(1-z) < 10){ /* making z more negative makes 1-z more positive. */ z--; increasedby++; } } else{ tmp = R(1-z,termcount); tmp2 = tmp; while(tmp2 < 21){ z--; increasedby++; tmp2 = R(1-z,termcount); if(tmp2 < tmp) return newerror("lngamma(1): something happend " "that should not have happend"); } } corr = ceil( re(z)/2 -3/4 - kroneckerdelta(im(z))/4); /* reflection */ ret = ln( pi() / sin(pi() * z ) ) - __CZ__lngstirling(1-z,termcount); /* deflate z */ if(increasedby > 0){ for(k=0;k= d52){ if(abs(im(z))>= d37 ) termcount = ceil(d37); else termcount = ceil(d52); } increasedby = 0; ##print "Z 1: ",z; Z=z; /* inflate z */ if( abs(im(z))>= d37){ while(abs(1-z) < d52+1){ /* making z more negative makes 1-z more positive. */ z--; increasedby++; } } else{ tmp = R(1-z,termcount); tmp2 = tmp; while(tmp2 < decdigits){ z--; increasedby++; tmp2 = R(1-z,termcount); if(tmp2 < tmp) return newerror("lngamma(1): something happend that " "should not have happend"); } } corr = ceil( re(z)/2 -3/4 - kroneckerdelta(im(z))/4); /* reflection */ ret = ln( pi() / sin(pi() *(z) ) ) - __CZ__lngstirling(1-z,termcount); /* deflate z */ if(increasedby > 0){ for(k=0;k 0){...} else */ }/*if(isint(z)){...} else*/ } /* Warning about large values? */ define gamma(z) { /* exp(log(gamma(z))) = exp(lngamma(z)), so use Spouge here?*/ local ret eps; if(isint(z)){ if(z <=0) return newerror("gamma(z): z is a negative integer"); else{ /* may hold up accuracy a bit longer, but YMMV */ if(z < 20) return (z-1)!*1.0; else{ eps = epsilon(epsilon()*1e-2); ret = exp(lngamma(z)); epsilon(eps); return ret; } } } else{ eps = epsilon(epsilon()*1e-2); ret = exp(lngamma(z)); epsilon(eps); return ret; } } define __CZ__harmonicF( a, b ,s) { local c; if( b == a) return s ; if( b-a > 1){ c= (b + a) >> 1; return( __CZ__harmonicF(a, c,1/a) + __CZ__harmonicF(c+1, b,1/b)); } return (1/a+1/b); } define harmonic(limit) { if( !isint(limit) ) return newerror("harmonic(limit): limit is not an integer"); if( limit <= 0 ) return newerror("harmonic(limit): limit is <=0"); /* The binary splitting algorithm returns 0 for f(1,1,0) */ if( limit == 1 ) return 1; return __CZ__harmonicF( 1, limit ,0); } /* lower incomplete gamma function */ /* lower for z <= 1.1 */ define __CZ__gammainc_series_lower(a,z){ local k ret tmp eps fact; ret = 0; k=0; tmp=1; fact = 1; eps = epsilon(); while(abs(tmp-ret) > eps){ tmp = ret; ret += (z^(k+a))/( (a+k)*fact ); k++; fact *= -k; } return gamma(a)-ret; } /* lower for z > 1.1 */ define __CZ__gammainc_cf(a,z,n){ local ret k num1 denom1 num2 denom2 ; ret = 0; for(k=n+1;k>1;k--){ ret = ((1-k)*(k-1-a))/(2*k-1+z-a+ret); } return ((z^a*exp(-z))/(1+z-a+ret)); } /* G(n,z) lower*/ define __CZ__gammainc_integer_a(a,z){ local k sum fact zz; for(k=0;k G(a,z) */ define __CZ__endcf(n,a,z,P){ local ret; ret = P*ln(10)+ln(4*pi()*sqrt(n)) + re(z+(3/2-a)*ln(z)-lngamma(1-a)); ret = ret /( sqrt( 8*(abs(z)+re(z)) ) ); return ret^2; } /* lower incomplete gamma function */ define gammainc(a,z){ local ret nterms eps epsilon tmp_before tmp_after n A B C sum k; if(z == 0) return 1; if(isint(a)){ if(a>0){ if(a==1) return exp(-z); return __CZ__gammainc_integer_a(a,z); } else{ if(a==0){ return -expoint(-z)+1/2*( ln(-z) - ln(-1/z) ) -ln(z); } else if(a==-1){ return expoint(-z)+1/2*(ln(-1/z)-ln(-z))+ln(z)+(exp(-z)/z); } else{ A = (-1)^((-a)-1)/((-a)!); B = expoint(-z)-1/2*(ln(-z)-ln(1/-z))+ln(z); C = exp(-z); sum =0; for(k=1;k<-a;k++){ sum += (z^(k-a-1))/( fallingfactorial(-a,k) ); } return A * B - C *sum; } } } if(re(z)<=1.1|| re(z) < a+1){##print "series"; eps = epsilon(epsilon()*1e-10); ret = __CZ__gammainc_series_lower(a,z); epsilon(eps); return ret; } else{##print "cf"; eps = epsilon(epsilon()*1e-10); if(abs(exp(-z)) <= eps) return 0; tmp_before = 0; tmp_after = 1; n = 1; while(ceil(tmp_before) != ceil(tmp_after)){ tmp_before = tmp_after; tmp_after = __CZ__endcf(n++,a,z,digits(1/eps)); /* a still quite arbitrary limit */ if(n > 10){ return newerror("gammainc: evaluating limit for continued " "fraction does not converge"); } } ret = __CZ__gammainc_cf(a,z,ceil(tmp_after)); epsilon(eps); return ret; } } define heavisidestep(x){ return (1+sign(x))/2; } define NUMBER_POSITIVE_INFINITY(){ return 1/epsilon();} define NUMBER_NEGATIVE_INFINITY(){ return -(1/epsilon());} static TRUE = 1; static FALSE = 0; define g(prec){ local eps ret; if(!isnull(prec)){ eps = epsilon(prec); ret = -psi(1); epsilon(eps); return ret; } return -psi(1); } define __CZ__series_converged(new,old,max){ local eps; if(isnull(max)) eps = epsilon(); else eps = max; if( abs(re(new - old)) <= eps * abs(re(new)) && abs(im(new - old)) <= eps * abs(im(new))) return TRUE; return FALSE; } define __CZ__ei_power(z){ local tmp ei k old; ei = g() + ln(abs(z)) + sgn(im(z)) * 1i * abs(arg(z)); ##ei = g() + ln(z) -1i*pi()*floor( (arg(z)+pi()) / (2*pi()) ); tmp = 1; k = 1; while(k){ tmp *= z / k; old = ei; ei += tmp / k; if (__CZ__series_converged(ei,old)) break; k++; } return ei; } define __CZ__ei_asymp(z){ local ei old tmp k; ei = sgn(im(z)) * 1i * pi(); tmp = exp(z) / z; for(k=1; k<=floor(abs(z))+1; k++){ old = ei; ei += tmp; if (__CZ__series_converged(ei, old)) return ei; tmp *= k / z; } return newerror("expoint: asymptotic series does not converge"); } define __CZ__ei_cf(z){ local ei c d k old; ei = sgn(im(z)) * 1i * pi(); if(ei != 0){ c = 1 / ei; d = 0; c = 1 / (1 - z - exp(z) * c); d = 1 / (1 - z - exp(z) * d); ei *= d / c; } else{ c = NUMBER_POSITIVE_INFINITY(); d = 0; c = 0; d = 1 / (1 - z - exp(z) * d); ei = d * (- exp(z)); } k = 1; while(1){ c = 1 / (2 * k + 1 - z - k * k * c); d = 1 / (2 * k + 1 - z - k * k * d); old = ei; ei *= d / c; if (__CZ__series_converged(ei, old)) break; k++; } return ei; } define expoint(z){ local ei eps ret; eps=epsilon(epsilon()*1e-5); if(abs(z) >= NUMBER_POSITIVE_INFINITY()){ if (config("user_debug") > 0) { print "expoint: abs(z) > +inf"; } ret = sgn(im(z)) * 1i * pi() + exp(z) / z; epsilon(eps); return ret; } if(abs(z) > 2 - 1.035 * log(epsilon())){ if (config("user_debug") > 0) { print "expoint: using asymptotic series"; } ei = __CZ__ei_asymp(z); if (!iserror(ei)){ ret = ei; epsilon(eps); return ret; } } if(abs(z) > 1 && (re(z) < 0 || abs(im(z)) > 1)){ if (config("user_debug") > 0) { print "expoint: using continued fraction"; } ret = __CZ__ei_cf(z); epsilon(eps); return ret; } if(abs(z) > 0){ if (config("user_debug") > 0) { print "expoint: using power series"; } ret = __CZ__ei_power(z); epsilon(eps); return ret; } if(abs(z) == 0){ if (config("user_debug") > 0) { print "expoint: abs(z) = zero "; } epsilon(eps); return NUMBER_NEGATIVE_INFINITY(); } } define erf(z){ return sqrt(z^2)/z * ( 1-1/sqrt(pi()) *gammainc(1/2,z^2) ); } define erfc(z){ return 1-erf(z); } define erfi(z){ return -1i*erf(1i*z); } define faddeeva(z){ return exp(-z^2)*erfc(-1i*z); } define gammap(a,z){ return gammainc(a,z)/gamma(a); } define gammaq(a,z){ return 1-gammap(a,z); } define lnbeta(a,b){ local ret eps; eps=epsilon(epsilon()*1e-3); ret = (lngamma(a)+lngamma(b))-lngamma(a+b); epsilon(eps); return ret; } define beta(a,b){ return exp(lnbeta(a,b)); } define __CZ__ibetacf_a(a,b,z,n){ local A B m places; if(n==1) return 1; m=n-1; places = highbit(1 + int(1/epsilon())) + 1; A = bround((a+m-1) * (a+b+m-1) * m * (b-m) * z^2,places++); B = bround((a+2*(m)-1)^2,places++); return A/B; } define __CZ__ibetacf_b(a,b,z,n){ local A B m places; places = highbit(1 + int(1/epsilon())) + 1; m=n-1; A = bround((m*(b-m)*z)/(a+2*m-1),places++); B = bround(( (a+m) * (a-(a+b)*z+1+m*(2-z)) )/(a+2*m+1),places++); return m+A+B; } /* Didonato-Morris */ define __CZ__ibeta_cf_var_dm(a,b,z,max){ local m f c d check del h qab qam qap eps places; eps= epsilon(); if(isnull(max)) max = 100; places = highbit(1 + int(1/epsilon())) + 1; f = eps; c = f; d = 0; for(m=1;m<=max;m++){ d =bround( __CZ__ibetacf_b(a,b,z,m)+__CZ__ibetacf_a(a,b,z,m)*d,places++); if(abs(d) max) return newerror("ibeta: continous fraction does not converge"); return f; } define betainc_complex(z,a,b){ local factor ret eps cf sum k N places tmp tmp2; if(z == 0){ if(re(a) > 0) return 0; if(re(a) < 0) return newerror("betainc_complex: z == 0 and re(a) < 0"); } if(z == 1){ if(re(b)>0) return 1; else return newerror("betainc_complex: z == 1 and re(b) < 0"); } if(b<=0){ if(isint(b)) return 0; else return newerror("betainc_complex: b <= 0"); } if(z==1/2 && (a==b)){return 1/2; } ##if(2==1){ if(isint(a) && isint(b)){ eps=epsilon(epsilon()*1e-10); N = a+b-1; sum = 0; for(k=a;k<=N;k++){ tmp = ln(z)*k+ln(1-z)*(N-k); tmp2 = exp(ln(comb(N,k))+tmp); sum += tmp2; } epsilon(eps); return sum } else if(re(z) <= re((a+1)/(a+b+2))){ eps=epsilon(epsilon()*1e-10); places = highbit(1 + int(1/epsilon())) + 1; factor = bround(( ln(z^a*(1-z)^b ) - lnbeta(a,b) ),places); cf =bround( __CZ__ibeta_cf_var_dm(a,b,z),places); ret = factor + ln(cf); if(abs(ret//ln(2)) >= places) ret = 0; else ret = bround(exp(factor + ln(cf)),places); epsilon(eps); return ret; } else if( re(z) > re( (a+1)/(a+b+2) ) || re(1-z) < re( (b+1)/(a+b+2) ) ){ ret = 1 - betainc_complex(1-z,b,a); } return ret; } /******************************************************************************/ /* Purpose: __CZ__ibetaas63 computes the incomplete Beta function ratio. Licensing: This code is distributed under the GNU LGPL license. Modified: 2013-08-03 20:52:05 +0000 Author: Original FORTRAN77 version by KL Majumder, GP Bhattacharjee. C version by John Burkardt Calc version by Christoph Zurnieden Reference: KL Majumder, GP Bhattacharjee, Algorithm AS 63: The incomplete Beta Integral, Applied Statistics, Volume 22, Number 3, 1973, pages 409-411. Parameters: Input, x, the argument, between 0 and 1. Input, a, b, the parameters, which must be positive. Output, the value of the incomplete Beta function ratio. */ define __CZ__ibetaas63(x, a, b,beta){ local ai betain cx indx ns aa asb bb rx temp term value xx acu places; acu = epsilon(); value = x; /* inverse incbeta calculates it already */ if(isnull(beta)) beta = lnbeta(a,b); if ( a <= 0.0 || b <= 0.0 ){ return newerror("betainc: domain error: a < 0 and/or b < 0"); } if ( x < 0.0 || 1.0 < x ){ return newerror("betainc: domain error: x<0 or x>1"); } if ( x == 0.0 || x == 1.0 ){ return value; } asb = a + b; cx = 1.0 - x; if ( a < asb * x ){ xx = cx; cx = x; aa = b; bb = a; indx = 1; } else{ xx = x; aa = a; bb = b; indx = 0; } term = 1.0; ai = 1.0; value = 1.0; ns = floor( bb + cx * asb ); rx = xx / cx; temp = bb - ai; if ( ns == 0 ){ rx = xx; } places = highbit(1 + int(1/acu)) + 1; while(1){ term = bround(term * temp * rx / ( aa + ai ),places++); value = value + term;; temp = abs ( term ); if ( temp <= acu && temp <= abs(acu * value) ){ value = value * exp ( aa * ln ( xx ) + ( bb - 1.0 ) * ln ( cx ) - beta ) / aa; if ( indx ){ value = 1.0 - value; } break; } ai = ai + 1.0; ns = ns - 1; if ( 0 <= ns ) { temp = bb - ai; if ( ns == 0 ) { rx = xx; } } else { temp = asb; asb = asb + 1.0; } } epsilon(acu); return value; } /* z / [ b - 1 a - 1 1/beta(a,b) * I (1 - t) t dt ] / 0 */ define betainc(z,a,b){ local factor ret eps cf sum k N places tmp tmp2; if(im(z) || im(a) || im(b)) return betainc_complex(z,a,b); if(z == 0){ if(re(a) > 0) return 0; if(re(a) < 0) return newerror("betainc: z == 0 and re(a) < 0"); } if(z == 1){ if(re(b)>0) return 1; else return newerror("betainc: z == 1 and re(b) < 0"); } if(b<=0){ if(isint(b)) return 0; else return newerror("betainc: b <= 0"); } if(z==1/2 && a==b){ return 1/2; } return __CZ__ibetaas63(z,a,b); } define __CZ__erfinvapprox(x){ local a; a =0.147; return sgn(x)*sqrt(sqrt((2/(pi()*a)+(ln(1-x^2))/2)^2-(ln(1-x^2))/a)- (2/(pi()*a)+(ln(1-x^2))/2)); } /* complementary inverse errror function, faster at about x < 1-.91 Henry E. Fettis. "A stable algorithm for computing the inverse error function in the 'tail-end' region" Math. Comp., 28:585-587, 1974. */ define __CZ__inverffettis(x,n){ local y sqrtpi oldy k places; if (isnull(n)) n = 205; y = erfinvapprox(1-x); places = highbit(1 + int(1/epsilon())) + 1; sqrtpi = sqrt(pi()); do { oldy = y;k++; y = bround((ln( __CZ__fettiscf(y,n) / (sqrtpi * x)))^(1/2),places); } while( abs(y - oldy)/y > epsilon()); return y; } /* cf for erfc() */ define __CZ__fettiscf(y,n){ local k t tt r a b ; t = 1/y; tt = t^2/2; for (k=n;k> 0;k--){ a = 1; b = k*tt; r = b / (a + r); } return t / (1+r); } /* inverse errror function, faster at about x<=.91*/ define __CZ__inverfbin(x){ local places approx flow fhigh eps high low mid fmid epsilon; approx = erfinvapprox(x); epsilon = epsilon(); high = approx + 1e-4; low = -1; places = highbit(1 + int(1/epsilon)) + 1; fhigh = x-erf(high); flow = x-erf(low); while(1){ mid = bround(high - fhigh * (high - low) / (fhigh - flow), places); if ((mid == low) || (mid == high)) places++; fmid = x-erf(mid); if (abs(fmid) < epsilon) return mid; if (sgn(fmid) == sgn(flow)) { low = mid; flow = fmid; } else { high = mid; fhigh = fmid; } } } define erfinv(x){ local ret approx a eps y old places errfunc sqrtpihalf flag k; if(x<-1 || x > 1) return newerror("erfinv: input out of domain (-1<=x<=1)"); if(x == 0) return 0; if(x == -1) return NUMBER_NEGATIVE_INFINITY(); if(x == +1) return NUMBER_POSITIVE_INFINITY(); if(x<0){ x = -x; flag = 1; } /* No need for full pecision */ eps=epsilon(1e-20); if(eps >= 1e-40){ /* Winitzki, Sergei (6 February 2008). "A handy approximation for the error function and its inverse"*/ a = 0.147; y = sgn(x)*sqrt(sqrt((2/(pi()*a) +(ln(1-x^2))/2)^2 -(ln(1-x^2))/a) -(2/(pi()*a)+(ln(1-x^2))/2)); } else { /* 20 digits instead of 5 */ if(x <= .91) y = __CZ__inverfbin(x); else y = __CZ__inverffettis(1-x); if(eps <= 1e-20){ epsilon(eps); return y; } } epsilon(eps); /* binary digits in number (here: number = epsilon()) */ places = highbit(1 + int(1/eps)) + 1; sqrtpihalf = 2/sqrt(pi()); k = 0; /* Do some Newton-Raphson steps to reach final accuracy. Only a couple of steps are necessary but calculating the error function at higher precision is quite costly; */ do{ old = y; errfunc = bround( erf(y),places); if( abs(errfunc-x) <= eps ) break; y = bround(y-( errfunc -x) / ( sqrtpihalf * exp(-y^2)),places); k++; }while(1); /* This is not really necessary but e.g: ; epsilon(1e-50) 0.00000000000000000000000000000000000000000000000001 ; erfinv(.9999999999999999999999999999999) 8.28769266865549025938 ; erfinv(.999999999999999999999999999999) 8.14861622316986460738453487549552168842204512959346 ; erf(8.28769266865549025938) 0.99999999999999999999999999999990000000000000000000 ; erf(8.14861622316986460738453487549552168842204512959346) 0.99999999999999999999999999999900000000000000000000 The precision "looks too short". */ if(k == 0) y = bround(y-( errfunc -x) / ( sqrtpihalf * exp(-y^2)),places); if(flag == 1) y = -y; return y; } /* * restore internal function from resource debugging */ config("resource_debug", resource_debug_level),; if (config("resource_debug") & 3) { print "zeta(z)"; print "psi(z)"; print "polygamma(m,z)"; print "lngamma(z)"; print "gamma(z)"; print "harmonic(limit)"; print "gammainc(a,z)"; print "heavisidestep(x)"; print "expoint(z)"; print "erf(z)"; print "erfinv(x)"; print "erfc(z)"; print "erfi(z)"; print "erfinv(x)"; print "faddeeva(z)"; print "gammap(a,z)"; print "gammaq(a,z)"; print "beta(a,b)"; print "lnbeta(a,b)"; print "betainc(z,a,b)"; }