/*
 * 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<size(__CZ__Ck);k++){
    print __CZ__Ck[k];
  }
}

/*Kronecker delta function */
define kroneckerdelta(i,j){
    if(isnull(j)){
      if(i==0) return 1;
      else return 0;
    }
    if(i!=j) return 0;
    else return 1;
}

/* (Pre-)Computed terms of the Stirling series */
static __CZ__precomp_stirling;
/* Number of terms in mat[0,0], precision in mat[0,1] with |z| >= 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)<n){
    __CZ__precomp_stirling = mat[n];
    for(k=1;k<=n;k++){
      bernterm = bernoulli(2*k)/(   2*k*(2*k-1 ) );
      __CZ__precomp_stirling[k-1] = bernterm;
      sum +=  bernterm /zz;
      zz *= z2;
    }
  }
  else{
    for(k=1;k<=n;k++){
      bernterm = __CZ__precomp_stirling[k-1];
      sum +=  bernterm /zz;
      zz *= z2;
    }
  }
  return head + sum;
}

/* Compute error for Stirling series for "z" with "k" terms*/
define R(z,k){
  local a b ab stirlingterm;
  stirlingterm = bernoulli(2*k)/( 2*k*(2*k-1)*z^(2*k));
  a = abs( stirlingterm );
  b = (cos(.5*arg(z)^(2*k)));
  ab = a/b;
  /* a/b might round to zero */
  if( abs(ab) < epsilon()){
    return digits(1/epsilon());
  }
  return digits(1/(a/b));
  ##return a/b;
}

/*Low precision lngamma(z) for testing the branch correction */
define lngammasmall(z){
  local ret eps flag corr;
  flag = 0;
  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 ln((z-1)!);
      else
        return __CZ__lngamma_lanczos(z);
    }
  }
  else{
    if(re(z)<0){
      if(im(z) && im(z)<0){
        z = conj(z);
        flag = 1;
      }

      ret = ln( pi() / sin(pi() *z ) ) -__CZ__lngamma_lanczos(1-z);

      if(!im(z)){
        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);
          }
        }
      }
      else{
        corr = ceil( re(z)/2 -3/4 - kroneckerdelta(im(z))/4);
        ret = ret +2*(corr *pi() )*1i;
      }
      if(flag == 1)
         ret = conj(ret);
      return ret;
    }
    ret = (__CZ__lngamma_lanczos(z));
    return ret;
  }
}


/*
  The logarithmic gamma function lngamma(z)
  It has a different principal branch than log(gamma(z))
*/
define lngamma(z)
{
  local ret eps  flag  increasedby Z k tmp tmp2 decdigits;
  local corr d37 d52 termcount;
  /* z \in \mathbf{Z}*/
  if(isint(z)){
    /*The gamma-function has poles at zero and the negative integers*/
    if(z <=0)
      return newerror("lngamma(z): z is a negative integer");
    else{
      /* may hold up accuracy a bit longer, but YMMV */
      if(z < 20)
        return ln( (z-1)! );
      else
        return  __CZ__lngammarp(z);
    }
  }
  else{/*if(isint(z))*/
    if(re(z) > 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<increasedby;k++){
            ret = ret - ln(Z+(k));
          }
        }
        ## ret = ret + 2*pi()*1i*ceil(re(x+1)/2-3/4 );
        /* Undo conjugate if one has been applied */
        if(flag == 1)
          ret = conj(ret);
        epsilon(eps);
        return ret;
      }/* if(decdigits <= 20) */
      else{
        /*
           Compute the number of terms for the Stirling-sum first.
           The error is bound by
                                            |T(k)|
              |R(k,z)| <=  ----------------------------------------
                                             2 k                2 k
                              2 k (2 k - 1) z     cos(0.5 arg(z)   )
             with
                                  1 - 2 k
                          B(2 k) z
              T(k) =  ----------------------
                          2 k (2 k - 1)
            the terms of the Stirling-sum and B(n) the n-th Bernoulli number.

            We do know the current precision and the result of the helper
            function __CZ__R(z,k) returns the same number as "decdigits" if
            fed with the correct "z" and "n".
            The largest error is near the real line, so we try Z = re(1-z).
            If Z is small, say < 20 we increment it until it is.
            Computing Bernoulli numbers is expensive but they are small on
            average, get cached and because of the method used, computing
            any Bernoulli number > 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<increasedby;k++){
            ret = ret - ln(Z+(k));
          }
        }
        /* Undo conjugate if one has been applied */
        if(flag == 1)
          ret = conj(ret);
        epsilon(eps);
        return ret;
      }/* if(decdigits <= 20){...} else */
    }/* if(re(z) > 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<increasedby;k++){
              ret = ret + ln(z+(k));
            }
          }
          /* Apply correction term */
          ret = ret +2*(corr *pi() )*1i;
          /* Undo conjugate if one has been applied */
          if(flag == 1)
            ret = conj(ret);

          epsilon(eps);
          return ret;
        }/* if(decdigits <= 20) */
        else{
          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);
          }
          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<increasedby;k++){
              ret = ret + ln(z+(k));
            }
          }
          /* Apply correction term */
          ret = ret +2*((corr) *pi() )*1i;
          /* Undo conjugate if one has been applied */
          if(flag == 1)
            ret = conj(ret);
          epsilon(eps);
          return ret;
        }/* if(decdigits <= 20){...} else */
      }/*if(!im(z)){...} else*/
    }/* if(re(z) > 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<a;k++){
    sum += (z^k)/(k!);
  }
  return  (a-1)!*exp(-z)*sum;
}

/*
  P = precision in dec digits
  n = 1,2,3...
  a,z => 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)<eps ) d=eps;
    c =bround( __CZ__ibetacf_b(a,b,z,m)+__CZ__ibetacf_a(a,b,z,m)/c,places++);
    if(abs(c)<eps ) c=eps;
    d=1/d;
    check = c*d;
    f = f*check;
    if(abs(check-1)<eps)break;
  }
  if(m > 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)";
}
