/* @(#)ben_fpu.c	19.1 (ES0-DMD) 02/25/03 14:35:21 */
/*===========================================================================
  Copyright (C) 1995 European Southern Observatory (ESO)
 
  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 (at your option) 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 Massachusetss Ave, Cambridge, 
  MA 02139, USA.
 
  Corresponding concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.COPYRIGHT  (C)  1991  European Southern Observatory
.IDENT      ben_fpu.c
.AUTHOR     P. Grosbol,  IPG/ESO
.LANGUAGE   C
.PURPOSE    Test the precision of an Floating Point Unit by
            computing and comparing a number of known mathematical
            equalities.
.VERSION    1.0  1989-Jan-09 : Creation,   PJG
.VERSION    1.1  1991-Feb-25 : First argument , number of saples. CG
--------------------------------------------------------------------*/
#include    <math.h>              /* include mathematical functions */

#define     NODEF         20      /* default factor for benchmark   */
#define     MXNO        5000      /* unit no. of samples per test   */
#define     STEP1     1.0e-4      /* Integration step ln(x)         */
#define     STEP2     1.0e-3      /* Integration step cos(x)        */
#define     INTG           1      /* No. of orbits                  */

typedef     int        (*PIF)();

extern      int        f1(),f2(),f();

main(argc, argv)
int argc;
char **argv;
{
  int      n, mxno, m;
  double   x, y, d, s, ss, dm, z[4];
  double   pi, e, ham, xe, step;
  int      irnd(), intgr();
  double   urnd();

  if (argc > 1 ) m=atoi(argv[1]);
  else m = NODEF;

  printf("Benchmark of FP-unit: %d times\n",m);

  mxno = m * MXNO;
  pi = 4.0 * atan(1.0); e = exp(1.0);

  irnd(30173,7,198,0.0,2.0*pi);
  s = 0.0; ss = 0.0; dm = 0.0; n = mxno;
  while (n--) {
    x = urnd();
    d = 1.0 / (sin(x)*sin(x) + cos(x)*cos(x)) - 1.0;
    s += d; ss += d*d; if (d<0.0) d = -d;
    if (dm<d) dm = d;
  }
  s /= mxno; ss = sqrt(ss/mxno);
  printf("sin,cos : %3.6e, %3.6e, %3.6e\n",s,ss,dm);

  irnd(27193,291,3918,0.0,2.0*pi);
  s = 0.0; ss = 0.0; dm = 0.0; n = mxno;
  while (n--) {
    x = urnd(); y = urnd();
    d = 2.0*sin(x)*cos(y) / (sin(x+y) + sin(x-y)) - 1.0;
    s += d; ss += d*d; if (d<0.0) d = -d;
    if (dm<d) dm = d;
  }
  s /= mxno; ss = sqrt(ss/mxno);
  printf("2sincos : %3.6e, %3.6e, %3.6e\n",s,ss,dm);

  irnd(23913,97,2211,-0.2499*pi,0.2499*pi);
  s = 0.0; ss = 0.0; dm = 0.0; n = mxno;
  while (n--) {
    x = urnd(); y = tan(x);
    d = (y+y) / (tan(x+x)*(1.0-y*y)) - 1.0;
    s += d; ss += d*d; if (d<0.0) d = -d;
    if (dm<d) dm = d;
  }
  s /= mxno; ss = sqrt(ss/mxno);
  printf("tan2t   : %3.6e, %3.6e, %3.6e\n",s,ss,dm);

  irnd(31931,197,198,-e,2.0*e);
  s = 0.0; ss = 0.0; dm = 0.0; n = mxno;
  while (n--) {
    x = urnd(); y = urnd();
    d = exp(x+y) / (exp(x)*exp(y)) - 1.0;
    s += d; ss += d*d; if (d<0.0) d = -d;
    if (dm<d) dm = d;
  }
  s /= mxno; ss = sqrt(ss/mxno);
  printf("exp     : %3.6e, %3.6e, %3.6e\n",s,ss,dm);

  irnd(31193,317,20191,1.0e-6,1.0e2);
  s = 0.0; ss = 0.0; dm = 0.0; n = mxno;
  while (n--) {
    x = urnd(); y = urnd();
    d = (log(x) + log(y)) / log(x*y) - 1.0;
    s += d; ss += d*d; if (d<0.0) d = -d;
    if (dm<d) dm = d;
  }
  s /= mxno; ss = sqrt(ss/mxno);
  printf("log     : %3.6e, %3.6e, %3.6e\n",s,ss,dm);

  x = 1.0; y = 0.0; step = STEP1 / m;
  n = intgr(&x,e,step,1,&y,f1);        /* integrate 1/x i.e. ln(x) */
  d = 1.0 / y - 1.0;
  printf("intg 1/x: %3.6e\n",d);

  x = 0.0; y = 0.0; step = STEP2 / m;
  n = intgr(&x,2.0*pi,step,1,&y,f2);        /* integrate cos(x)    */
  printf("intg cos: %3.6e\n",y);

  x = 0.0; xe = 2.0; n = m * INTG;
  z[0] = 0.0; z[1] = 1.0; z[2] = 1.0; z[3] = -0.3;
  y = z[3]; d = z[2]/z[1];
  ham = 0.5*(y*y+d*d) - 1.0/z[1];
  d = 0.0;
  printf("kepler  : %5.1f, %12.8f %12.8f %12.8f, %3.6e\n", 
          x,z[0],z[1],z[3],d);
  while (n--) {
     intgr(&x,xe,1.0e-4,4,z,f);
     y = z[3]; d = z[2]/z[1];
     d = ham / (0.5*(y*y+d*d) - 1.0/z[1]) - 1.0;
     xe += 2.0;
  }
  printf("kepler  : %5.1f, %12.8f %12.8f %12.8f, %3.6e\n", 
	 x,z[0],z[1],z[3],d);
  exit(0);
}

static unsigned int   ia,ib,ix;   /* seeds for random numbers       */
static double         rz,rr;      /* zero and range of numbers      */

int irnd(na,nb,nx,z,r)
int     na;
int     nb;
int     nx;
double  z;
double  r;
{
  unsigned int  msk;

  msk = 0x0000FFFF;
  if (0<na) ia = msk & na;        /* initiate random seeds          */
  if (0<nb) ib = msk & nb;
  if (0<nx) ix = msk & nx;
  rz = z; rr = r / (msk+1);       /* set zero and range             */
  return 0;
}

double urnd()                     /* compute new random number      */
{
  ix = 0x0000FFFF & (ix*ia + ib);
  return (rz + rr*ix);
}

int intgr(x,xe,h,no,y,func)
double   *x;
double   xe;
double   h;
int      no;
double   *y;
PIF      func;
{
  int    cont,rk4o();

  if (h==0.0) return 1;
  if ((xe - *x)*h<0.0) h = -h;
  cont = 1;
 
  do {
     if ((xe - h - *x)*h<0.0) {
        h = xe - *x;
        cont = 0;
     }
     if (rk4o(x,h,no,y,func)) return 1;
  } while (cont);
  return 0;
}

#define    MXP            4   /* max. no. of differential equations */

int rk4o(x,h,no,y,func)
double   *x;
double   h;
int      no;
double   *y;
PIF      func;
{
  int    n;
  double xx,h2;
  double yy[MXP],yk1[MXP],yk2[MXP],yk3[MXP],yk4[MXP];

  xx = *x; h2 = 0.5 * h;
  if ((*func)(xx,y,yk1)) return 1;
  xx += h2;
  for (n=0; n<no; n++) yy[n] = y[n] + h2*yk1[n];
  if ((*func)(xx,yy,yk2)) return 1;
  for (n=0; n<no; n++) yy[n] = y[n] + h2*yk2[n];
  if ((*func)(xx,yy,yk3)) return 1;
  xx = *x + h;
  for (n=0; n<no; n++) yy[n] = y[n] + h*yk3[n];
  if ((*func)(xx,yy,yk4)) return 1;
  *x += h;
  for (n=0; n<no; n++) 
      y[n] += h*(yk1[n]+yk4[n]+2.0*(yk2[n]+yk3[n]))/6.0;
  return 0;
}

int f1(x,y,yk)
double   x;
double   *y;
double   *yk;
{
  if (x<=0.0) return 1;

  *yk = 1.0/x;
  return 0;
}

int f2(x,y,yk)
double   x;
double   *y;
double   *yk;
{
  *yk = cos(x);
  return 0;
}

int f(x,y,yk)
double   x;
double   y[];
double   yk[];
{
  double r,a;

  r = y[1];
  if (r<=0.0) return 1;
  a = y[2]/r;

  yk[0] = a/r;
  yk[1] = y[3];
  yk[2] = 0.0;
  yk[3] = (a*a - 1.0/r)/r;
  return 0;
}

