/*
**
** EMATH.C     A module to be included with EVAL.C that supplies
**             math functions not included in ANSI C.
**
** To add/change the functions in Eval, see funcs.c.
**
** NOTICE about this part of the Eval source code:
** ------------------------------------------------------------------------
** If you add source code to this module that is copied from "Numerical
** Recipes in C" or some other book of C math functions, make sure you
** clearly read the license agreement from that book.  In particular,
** "Numerical Recipes in C" forbids you to distribute their source code.
** Because my license agreement requires free distribution of ALL source
** code, you may not add code from a book like "Numerical Recipes in C" and
** then redistribute any of the source or executables.  The upshot of all
** this is that you must write your own versions of whatever math functions
** you add to this code if you plan to redistribute it.
**
** Eval is a floating point expression evaluator.
** This file last updated in version 1.12
** For the version number, see eval.h
** Copyright (C) 1993-2003 http://willus.com
**
** This program is free software; you can redistribute it and/or modify it
** under the terms of the GNU General Public License as published by the
** Free Software Foundation; either version 2 of the License, or any
** later version.
**
** This program 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
** General Public License for more details.
**
** You should have received a copy of the GNU General Public License along
** with this program; if not, write to the Free Software Foundation, Inc.,
** 675 Mass Ave, Cambridge, MA 02139, USA.
**
**
*/

#include "eval.h"


#define ACCURACY    ((double)1.0e-10)
#define SMALL_PI    3.14
#define MAXRESULT   (DBL_MAX/1.e3)
#define ACC         40.0
#define EXTR        1.0e10
#define MAX         1e14
#define FACTOR      2
#define FIRSTGUESS  .11
#define TINYNUM     1e-6
#define NITER       30

static double x0;

static double bessi0(double x);
static double bessi1(double x);
static double bessj0(double x);
static double bessj1(double x);
static double bessk0(double x);
static double bessk1(double x);
static double hypcos(double x);
static double hypsin(double x);
static double hyptan(double x);

/*
**
**  acosh      Calculates arc-hyperbolic-cosine of a value.
**             Arguments must be >=1.  If not, -1 is returned.
**             Positive values are always returned if the
**             argument is within the correct domain.
**             Abs(Maximum value returned) = 1e14
**
*/
double acosh(double x)

   {
   int     i;
   double  b1,b2;

   if (x<1)
       return(-1.);
   if (x==1.)
       return(0.);
   x0=x;
   if (cosh(.11)>x)
       {
       b1=0.;
       b2=.11;
       }
   else
       {
       for (b2=.1;cosh(b2)<x && b2<MAX;b2*=FACTOR);
       if (b2>MAX)
           return(MAX);
       b1=b2/FACTOR;
       }
   return(root(hypcos,b1,b2,ACCURACY,&i));
   }


static double hypcos(double x)

   {
   return(cosh(x)-x0);
   }


/*
**
**  asinh      Calculates arc-hyperbolic-sine of a value.
**
**             Abs(Maximum value returned) = 1e14
**
*/
double asinh(double x)

   {
   int     i,sign;
   double  b1,b2,acc;

   if (x==0.)
       return(0.);
   if (x>0.)
       sign=1;
   else
       {
       sign=-1;
       x=-x;
       }
   x0=x;
   acc=ACCURACY;
   if (sinh(FIRSTGUESS)>x)
       {
       b1=0.;
       b2=FIRSTGUESS;
       }
   else
       {
       for (b2=.1;sinh(b2)<x && b2<MAX;b2*=FACTOR,acc*=FACTOR);
       if (b2>MAX)
           return(MAX*sign);
       b1=b2/FACTOR;
       }
   return(root(hypsin,b1,b2,acc,&i)*sign);
   }


static double hypsin(double x)

   {
   return(sinh(x)-x0);
   }


/*
**
**  atanh      Calculates arc-hyperbolic-tangent of a value.
**             Value must be between but not including -1 and 1
**             or else 0 is returned.
**
**             Abs(Maximum value returned) = 1e14
**
*/
double atanh(double x)

   {
   int     i,sign;
   double  b1,b2;

   if (x<=-1 || x>=1)
       return(0.);
   if (x==0.)
       return(0.);
   if (x>0.)
       sign=1;
   else
       {
       sign=-1;
       x=-x;
       }
   x0=x;
   if (tanh(.11)>x)
       {
       b1=0.;
       b2=.11;
       }
   else
       {
       for (b2=.1;tanh(b2)<x && b2<MAX;b2*=FACTOR);
       if (b2>MAX)
           return(MAX*sign);
       b1=b2/FACTOR;
       }
   return(root(hyptan,b1,b2,ACCURACY,&i)*sign);
   }


static double hyptan(double x)

   {
   return(tanh(x)-x0);
   }


/*
**
** bessi    Calculates the value of Im(x) using a downward recurrence
**          (Miller's) formula.  Calls bessi0 to normalize.
**
** The idea for Miller's algorithm, and where to start the series,
** was taken from Numerical Recipes in C, but this function is NOT
** a copy of their function, and, I believe, does not infringe on
** their copyright.
**
** Properties:  Im(x) is a solution to the modified (hyperbolic)
**              Bessel equation.
**              Im(x) is monotonically increasing and diverges
**              at infinity.
**
**              I(-m)(x) = Im(x)
**              Im(-x)   = (-1)^m*Im(x)
**              I(m+1)(x) = -m*2/x*Im(x) + I(m-1)(x)
**              I(m-1)(x) = m*2/x*Im(x) + I(m+1)(x)
**
**              Limit as x goes to zero:
**              Im(x) --> (0.5*x)^m / m!
**
*/
double bessi(int m,double x)

    {
    int     i,negx;
    double  c,ans,nextterm,seed0,seed1;

    if (m<0)
        m=-m;
    if (m==0)
        return(bessi0(x));
    if (m==1)
        return(bessi1(x));
    if (x==0.)
        return(0.);
    negx=(x<0);
    x=fabs(x);
    if (x==0.)
        return(0.);
    c=2./x;
    seed1=1.0;  /* Initial seeds don't matter.  Series converges. */
    seed0=0.0;
    nextterm=0.;
    ans=0.;
    for (i=(int)(m+sqrt(64.*m))&(~1);i>0;i--)
        {
        nextterm=i*c*seed1+seed0;
        if (fabs(nextterm)>EXTR)
            {
            nextterm/=EXTR;
            ans/=EXTR;
            seed1/=EXTR;
            }
        if (i==m)
            ans=seed1;
        seed0=seed1;
        seed1=nextterm;
        }
    ans*=bessi0(x)/nextterm;
    return(negx && (m&1) ? -ans : ans);
    }


/*
** bessi0   Calculate I0(x) from Abromowitz and Stegun, p. 378.
**          Accuracy is better than 2e-7.
*/
static double bessi0(double x)

    {
    double  t,t2,t3,t4,t8,ans;

    x=fabs(x);
    if (x<3.75)
        {
        /* Eq 9.8.1 */
        t=(x/3.75);
        t2=t*t;
        t4=t2*t2;
        t8=t4*t4;
        ans=1+3.5156229*t2+3.0899424*t4+1.2067492*t4*t2+.2659732*t8
             +.0360768*t8*t2+.0045813*t4*t8;
        }
    else
        {
        /* Eq 9.8.2 */
        t=3.75/x;
        t2=t*t;
        t3=t2*t;
        t4=t2*t2;
        ans=.39894228+.01328592*t+.00225319*t2-.00157565*t3
            +.00916281*t4-.02057706*t*t4+.02635537*t2*t4
            -.01647633*t3*t4+.00392377*t4*t4;
        ans=exp(x)*ans/sqrt(x);
        }
    return(ans);
    }


/*
** bessi1   Calculate I1(x) from Abromowitz and Stegun, p. 378.
**          Accuracy is better than 3e-7.
*/
static double bessi1(double x)

    {
    int     negx;
    double  t,t2,t3,t4,t8,ans;

    negx=(x<0.);
    x=fabs(x);
    if (x<3.75)
        {
        /* Eq 9.8.3 */
        t=(x/3.75);
        t2=t*t;
        t4=t2*t2;
        t8=t4*t4;
        ans=.5+.87890594*t2+.51498869*t4+.15084934*t4*t2+.02658733*t8
              +.00301532*t8*t2+.00032411*t8*t4;
        ans*=x;
        }
    else
        {
        /* Eq 9.8.4 */
        t=3.75/x;
        t2=t*t;
        t3=t2*t;
        t4=t2*t2;
        ans=.39894228-.03988024*t-.00362018*t2+.00163801*t3
            -.01031555*t4+.02282967*t*t4-.02895312*t4*t2
            +.01787654*t4*t3-.00420059*t4*t4;
        ans=exp(x)*ans/sqrt(x);
        }
    return(negx ? -ans : ans);
    }


/*
**
** bessj    Calculates the value of Jm(x) using the bessj0 and
**          bessj0 functions and the Bessel function recurrence relation.
**
** The idea for Miller's algorithm, when to use it, and where to start the
** series, was taken from Numerical Recipes in C, but this function is NOT
** a copy of their function, and, I believe, does not infringe on their
** copyright.
**
** Properties:  Jm(x) is a solution to the Bessel function.
**              It is oscillatory in nature and does not diverge at
**              any point.
**
**              J(-m)(x) = (-1)^m*Jm(x)
**              Jm(-x)   = (-1)^m*Jm(x)
**              J(m+1)(x) = m*2/x*Jm(x) - J(m-1)(x)
**              J(m-1)(x) = m*2/x*Jm(x) - J(m+1)(x)
**
**
*/
double bessj(int m,double x)

    {
    int     i,negm,negx;
    double  c,j0,j1,ans,nextterm,seed1,seed0;

    ans=nextterm=0.; /* Avoid a compiler warning */
    negm=(m<0);
    if (m<0)
        m=-m;
    if (!(m&1))
        negm=0;
    if (m==0)
        return(bessj0(x));
    if (m==1)
        return(negm ? -bessj1(x) : bessj1(x));
    negx=(x<0);
    x=fabs(x);
    if (x==0.)
        return(0.);
    c=2./x;
    if (x>m)
        {
        /* Use forward recurrence relation */
        j0=bessj0(x);
        j1=bessj1(x);
        for (i=1;i<m;i++)
            {
            ans=i*c*j1-j0;
            j0=j1;
            j1=ans;
            }
        }
    else
        {
        /* Use Miller's algorithm of downward recurrence for x<m */
        seed1=1.; /* Start with any seed value--series will converge */
        seed0=0.;
        ans=0.;
        for (i=(int)(m+sqrt(64.*m))&(~1);i>0;i--)
            {
            nextterm=i*c*seed1-seed0;
            if (fabs(nextterm)>EXTR)
                {
                nextterm/=EXTR;
                ans/=EXTR;
                seed1/=EXTR;
                }
            if (i==m)
                ans=seed1;
            seed0=seed1;
            seed1=nextterm;
            }
        /* Normalize with bessj0 */
        ans*=(bessj0(x)/nextterm);
        }
    if (negx && (m&1))
        ans=-ans;
    return(negm ? -ans : ans);
    }


/*
** bessj0   Calculate J0(x) from Abromowitz and Stegun, p. 369.
**          Accuracy is better than 5e-8.
*/
static double bessj0(double x)

    {
    double  ans;
    double  x2,x4,x8;
    double  f0,theta0,thox,thox2,thox4;

    x=fabs(x);
    if (x<3)
        {
        /* Eq 9.4.1 */

        x2=(x/3)*(x/3);
        x4=x2*x2;
        x8=x4*x4;
        ans=1-2.2499997*x2+1.2656208*x4-.3163866*x2*x4
             +.04444479*x8-.0039444*x8*x2+.0002100*x8*x4;
        }
    else
        {
        /* Eq 9.4.3 */

        thox=3./x;
        thox2=thox*thox;
        thox4=thox2*thox2;
        f0=.79788456-.00000077*thox-.00552740*thox2-.00009512*thox2*thox
           +.00137237*thox4-.00072805*thox4*thox+.00014476*thox4*thox2;
        theta0=x-.78539816-.04166397*thox-.00003954*thox2+.00262573*thox2*thox
               -.00054125*thox4-.00029333*thox4*thox+.00013558*thox2*thox4;
        ans=f0*cos(theta0)/sqrt(x);
        }
    return(ans);
    }


/*
** bessj1   Calculate J1(x) from Abromowitz and Stegun, p. 370.
**          Accuracy is better than 5e-8.
*/
static double bessj1(double x)

    {
    int     negx;
    double  ans;
    double  x2,x4,x8;
    double  f1,theta1,thox,thox2,thox4;

    negx=(x<0);
    x=fabs(x);
    if (x<3)
        {
        /* Eq 9.4.4 */

        x2=(x/3)*(x/3);
        x4=x2*x2;
        x8=x4*x4;
        ans=0.5-.56249985*x2+.21093573*x4-.03954289*x4*x2+.00443319*x8
            -.00031761*x8*x2+.00001109*x4*x8;
        ans*=x;
        }
    else
        {
        /* Eq 9.4.6 */

        thox=3./x;
        thox2=thox*thox;
        thox4=thox2*thox2;
        f1=.79788456+.00000156*thox+.01659667*thox2+.00017105*thox*thox2
           -.00249511*thox4+.00113653*thox4*thox-.00020033*thox2*thox4;
        theta1=x-2.35619449+.12499612*thox+.00005650*thox2-.00637879*thox*thox2
               +.00074348*thox4+.00079824*thox4*thox-.00029166*thox4*thox2;
        ans=f1*cos(theta1)/sqrt(x);
        }
    return(negx ? -ans : ans);
    }


/*
**
** bessk   Calculates the value of Km(x) using Bessel function
**         recurrence relations.
**
** Properties:  Km(x) is a solution to the modified
**              (hyperbolic) Bessel equation.  It diverges
**              at zero and is monotonically decreasing.
**
**              Km(x) can also be written as:
**
**                           pi   I(-v)(x) - Iv(x)
**              lim (v->m)  ---- ------------------
**                            2       sin(v*x)
**
**              (only when m is an integer)
**
**
**              K(-m)(x) = Km(x)
**              Km(-x)   = (-1)^m*Km(x)
**              K(m+1)(x) = m*2/x*Km(x) + K(m-1)(x)
**
** Limit as x -> 0:
**              K0(x) -> -ln(x)
**              Km(x) -> 0.5*(m-1)!*(0.5*x)^(-m), m >= 1
**
*/
double bessk(int m,double x)

    {
    int     i,negx;
    double  c,k0,k1,ans;

    ans=0.; /* Avoid a compiler warning. */
    if (m<0)
        m=-m;
    if (m==0)
        return(bessk0(x));
    if (m==1)
        return(bessk1(x));
    negx=(x<0.);
    x=fabs(x);
    if (x==0.)
        return(DBL_MAX);
    c=2./x;
    k0=bessk0(x);
    k1=bessk1(x);
    for (i=1;i<m;i++)
        {
        ans=c*i*k1+k0;
        k0=k1;
        k1=ans;
        }
    return(negx && (m&1) ? -ans : ans);
    }


/*
** bessk0   Calculate K0(x) from Abromowitz and Stegun, p. 379.
**          Accuracy is better than 2e-7.
*/
static double bessk0(double x)

    {
    double  ans,t,t2,t4,t8;

    x=fabs(x);
    if (x==0.)
        return(MAXRESULT);
    if (x<2)
        {
        /* Eq 9.8.5 */
        t=x/2.;
        t2=t*t;
        t4=t2*t2;
        t8=t4*t4;
        ans=-.57721566+.42278420*t2+.23069756*t4+.03488590*t4*t2
            +.00262698*t8+.00010750*t8*t2+.00000740*t8*t4;
        ans-=log(x/2)*bessi0(x);
        }
    else
        {
        /* Eq 9.8.6 */
        t=2./x;
        t2=t*t;
        t4=t2*t2;
        ans=1.25331414-.07832358*t+.02189568*t2-.01062446*t2*t
            +.00587872*t4-.00251540*t4*t+.00053208*t4*t2;
        ans*=exp(-x)/sqrt(x);
        }
    return(ans);
    }


/*
** bessk1   Calculate K1(x) from Abromowitz and Stegun, p. 379.
**          Accuracy is better than 3e-7.
*/
static double bessk1(double x)

    {
    int     negx;
    double  ans,t,t2,t4,t8;

    negx=(x<0.);
    x=fabs(x);
    if (x==0.)
        return(MAXRESULT);
    if (x<2)
        {
        /* Eq 9.8.7 */
        t=x/2.;
        t2=t*t;
        t4=t2*t2;
        t8=t4*t4;
        ans=1+.15443144*t2-.67278579*t4-.18156897*t4*t2
             -.01919402*t8-.00110404*t8*t2-.00004686*t8*t4;
        ans=(ans/x)+log(x/2)*bessi1(x);
        }
    else
        {
        /* Eq 9.8.8 */
        t=2./x;
        t2=t*t;
        t4=t2*t2;
        ans=1.25331414+.23498619*t-.03655620*t2+.01504268*t2*t
            -.00780353*t4+.00325614*t4*t-.00068245*t4*t2;
        ans*=exp(-x)/sqrt(x);
        }
    return(negx ? -ans : ans);
    }


/*
**
**  dbessi      Calculates the value of Im'(x) using bessi and
**              Bessel function recurrence relations.
**  Im'(x) = m/x*Im(x) + I(m+1)(x)
**
*/
double dbessi(int m,double x)

    {
    if (fabs(x)<TINYNUM)
        x=x<0 ? -TINYNUM : TINYNUM;
    return(m*bessi(m,x)/x+bessi(m+1,x));
    }


/*
**
**  dbessj      Calculates the value of Jm'(x) using bessj and
**              Bessel function recurrence relations.
**  Jm'(x) = m/x*Jm(x) - J(m+1)(x)
**
*/
double dbessj(int m,double x)

    {
    if (fabs(x)<TINYNUM)
        x=x<0 ? -TINYNUM : TINYNUM;
    return(m*bessj(m,x)/x-bessj(m+1,x));
    }


/*
**
**  dbessk      Calculates the value of Km'(x) using bessk and
**              Bessel function recurrence relations.
**  Km'(x) = m/x*Km(x) - K(m+1)(x)
**
*/
double dbessk(int m,double x)

    {
    if (fabs(x)<TINYNUM)
        x=x<0 ? -TINYNUM : TINYNUM;
    return(m*bessk(m,x)/x-bessk(m+1,x));
    }


/*
**
** djroot       Calculates the nth non-zero root of the mth
**              order bessel function's derivative, Jm'(x)
**
*/
double djroot(int m,int n)

   {
   double  f0,f1,step,x0,x1;
   int     flag,nroot;

   if (n<1)
       return(0.);
   x0=(double)m+.5;
   step= n==1 ? SMALL_PI/4. : SMALL_PI;
   x1=x0+step;
   f0=dbessj(m,x0);
   f1=dbessj(m,x1);
   for (nroot=0 ;1; x0=x1,f0=f1,x1+=step,f1=dbessj(m,x1))
       {
       if (SGN(f0)*SGN(f1)<0)
           {
           nroot++;
           if (nroot==n)
               break;
           if (nroot==n-1)
               step/=4.;
           }
       }
   return(root2(dbessj,m,x0,x1,ACCURACY,&flag));
   }


/*
** factorial
*/
double factorial(int m)

    {
    int     i;
    double  ans;

    if (m<2)
        return(1.);
    ans=1.0;
    for (i=2;i<=m;i++)
        ans*=i;
    return(ans);
    }


/*
**
**  jroot       Calculates the nth non-zero root of the mth
**              order bessel function, Jm(x)
**
*/
double jroot(int m,int n)

   {
   double  f0,f1,step,x0,x1;
   int     flag,nroot;

   if (n<1)
       return(0.);
   x0=(double)m+2.;
   step= n==1 ? SMALL_PI/4. : SMALL_PI;
   x1=x0+step;
   f0=bessj(m,x0);
   f1=bessj(m,x1);
   for (nroot=0 ;1; x0=x1,f0=f1,x1+=step,f1=bessj(m,x1))
       {
       if (SGN(f0)*SGN(f1)<0)
           {
           nroot++;
           if (nroot==n)
               break;
           if (nroot==n-1)
               step/=4.;
           }
       }
   return(root2(bessj,m,x0,x1,ACCURACY,&flag));
   }


/*
**
** root     Finds root of function F(x) between x0 and x1
**          to precision: F(root) = 0. +/- xacc
**
** Returns:  The root of the function.
**
** Also returns status in "flag" variable.
** flag=0 if root found.
** flag=1 if root not found.  This could be due to NITER iterations being
**        exceeded (a diverging solution pehaps?) or x0 and x1 not bracketing
**        the root.
**
*/
double root(double(*F)(double),double x0,double x1,double xacc,int *flag)

   {
   int     n;
   double  f2,f3,f4,x2,x3,x4;

   (*flag)=1;
   if (x0==x1)
       return(0.);
   x2=x0;
   x3=x1;
   f2=(*F)(x2);
   f3=(*F)(x3);
   if (SGN(f2)*SGN(f3)>0)
       return(0.);
   for (n=1;n<=NITER;n++)
       {
       if (n<=4)
           x4=(x2+x3)/2.;
       else
           x4=(f3*x2-f2*x3)/(f3-f2);
       f4=(*F)(x4);
       if (ABS(f4)<xacc)
           {
           (*flag)=0;
           return(x4);
           }
       if (SGN(f2)*SGN(f4)<0)
           {
           if (n>4 && ABS(f4)>ABS(f3))
               return(0.);
           f3=f4;
           x3=x4;
           continue;
           }
       if (n>4 && ABS(f4)>ABS(f2))
           return(0.);
       f2=f4;
       x2=x4;
       }
   return(0.);
   }


/*
**  root2   Finds root of function F(m,x).  Works the same way as root.
*/
double root2(double(*F)(int,double),int m,double x0,double x1,double xacc,
             int *flag)

   {
   int     n;
   double  f2,f3,f4,x2,x3,x4;

   (*flag)=1;
   if (x0==x1)
       return(0.);
   x2=x0;
   x3=x1;
   f2=(*F)(m,x2);
   f3=(*F)(m,x3);
   if (SGN(f2)*SGN(f3)>0)
       return(0.);
   for (n=1;n<=NITER;n++)
       {
       if (n<=4)
           x4=(x2+x3)/2.;
       else
           x4=(f3*x2-f2*x3)/(f3-f2);
       f4=(*F)(m,x4);
       if (ABS(f4)<xacc)
           {
           (*flag)=0;
           return(x4);
           }
       if (SGN(f2)*SGN(f4)<0)
           {
           if (n>4 && ABS(f4)>ABS(f3))
               return(0.);
           f3=f4;
           x3=x4;
           continue;
           }
       if (n>4 && ABS(f4)>ABS(f2))
           return(0.);
       f2=f4;
       x2=x4;
       }
   return(0.);
   }
