/*   quartic.c
     version 54

     a test of relative accuracy of various quartic routines.

     use:
       quartic [-a] [-c n] [-q n] [-d n]
       (no parameters)  : a loop through 10,000 prechosen quartic coefficients
       -a               : improve cubic roots (default: no iteration)
       -c  n            : keep reading n cubic roots from standard input
                          (if n=0 read coefficients), terminate with 'q'.
       -d  n            : set debug value to 'n' (default: 5)
       -q  n            : keep reading n quartic roots from standard input
                          (if n=0 read coefficients), terminate with 'q'.

     debug <= 1 : print cases used

     method-
      http://linus.socs.uts.edu.au/~don/pubs/solving.html

      4 Apr 2004 bringing yacfraid notation into line with solving.html
      4 Apr 2004 fixed bug in final root calculation in yacfraid
      8 Mar 2004 corrected modified yacfraid algorithm
      8 Mar 2004 modified yacfraid algorithm
     31 Jan 2004 printing results when number of roots vary 
     30 Jan 2004 printing error of chosen cubic if debug<1
     27 Jan 2004 seeking worst coefficients for each algorithm
     27 Jan 2004 choosing best route for k in chris
     26 Jan 2004 conforming variables to solving.html
     26 Jan 2004 fixed bug in yacfraid for multiplicity 3
     25 Jan 2004 speeding up chris
     24 Jan 2004 fixed chris error (A <-> B)
     23 Jan 2004 use debug<1 for diagnostics
     21 Jan 2004 solve cubic in neumark, yacfraid and chris if d=0
     20 Jan 2004 3 roots to cubic in chris if e1=0
     19 Jan 2004 debugging Christianson routine
     14 Jan 2004 adding Christianson, cut determinant in quadratic call
      5 Jan 2004 improving cubic roots by Newton-Raphson iteration
     23 Dec 2003 seeking snag in yacfraid
     21 Dec 2003 putting diagnostic printout in yacfraid
     18 Dec 2003 allowing input of equation coefficients
     18 Dec 2003 recording cubic root giving least worst error
     17 Dec 2003 recording cubic root giving the most quartic roots
     16 Dec 2003 using the cubic root giving the most quartic roots
     16 Dec 2003 trying consistency of 3 cubic roots where available
     15 Dec 2003 trying all 3 cubic roots where available
     13 Dec 2003 trying to fix Neumark
     13 Dec 2003 cleaning up diagnostic format
     12 Dec 2003 initialising n,m,po3 in cubic
     12 Dec 2003 allow cubic to return 3 zero roots if p=q=r=0
     10 Dec 2003 added setargs
      2 Dec 2003 finding worst cases
      2 Dec 2003 negating j if p>0 in cubic
      1 Dec 2003 changing v in cubic from (sinsqk > doub0) to (sinsqk >= doub0)
      1 Dec 2003 test changing v in cubic from po3sq+po3sq to doub2*po3sq
     30 Nov 2003 counting cases in all solving routines
     29 Nov 2003 testing wsq >= doub0
     29 Nov 2003 mult by doub2 for v in cubic
     29 Nov 2003 cut po3cu from cubic
     29 Nov 2003 better quadratic
     29 Nov 2003 count agreements
     17 Nov 2003 count special cases
     16 Nov 2003 option of loop or read coefficients from input
     15 Nov 2003 fixed cubic() bug
     11 Nov 2003 added Brown and Yacoub & Fraidenraich's algorithms
     21 Jan 1989 quartic selecting Descartes, Ferrari, or Neumark algorithms 
     16 Jul 1981 Don Herbison-Evans

"Solving Quartics and Cubics for Graphics", D. Herbison-Evans,
Graphics Gems V (ed.: A. Paeth) Academic Press (Chesnut Hill), pp. 3-15 (1995). 

"Solving Quartics and Cubics for Graphics", D. Herbison-Evans,
Research Report CS-86-56, Department of Computer Science, University of Waterloo (1986)
 
"Caterpillars and the Inaccurate Solution of Cubic and Quartic Equations", 
D. Herbison-Evans, Australian Computer Science Communications, 
Vol. 5, No. 1, pp. 80-90 (1983)

   subroutines:
      setcns      - set constants
      setargs     - determine which equations to test
      looptest    - test 10000 coefficient combinations
      cases       - print statistics
      docoeffs    - read coefficients of a particular equation to solve
      cubictest   - test cubic solutions
      quartictest - test quartic solutions
      compare     - check accuracy of results
      errors      - find errors in a set of roots
      acos3       - find arcos(x/3)
      curoot      - find cube root
      quadratic   - solve a quadratic
      cubic       - solve a cubic
      cubnewton   - improve cubic roots by iteration
      quartic     - solve a quartic
      descartes   - use Descartes' algorithm to solve a quartic  
      ferrari     - use Ferrari's algorithm to solve a quartic 
      neumark     - use Neumark's algorithm to solve a quartic 
      yacfraid    - use Yacoub & Fraidenraich's algorithm to solve a quartic 
      chris       - use Christianson's algorithm to solve a quartic
*/

#include <stdio.h>
#define TRUE 1
#define FALSE 0
#define NCASES 40

double a,b,c,d;           /* quartic coefficients */
double cc[4],cd[4],cf[4],cn[4],cy[4];
double d3o8,d3o256;       /* double precision constants */
double doub0;
double doub1,doub2;
double doub3,doub4;
double doub5,doub6;
double doub8,doub9,doub12;
double doub16,doub24;
double doub27,doub64;
double doubmax;           /* approx square root of max double number */
double doubmin;           /* smallest double number */
double doubtol;           /* tolerance of double numbers */
double errq;
double inv2,inv3,inv4;
double inv8,inv16,inv32,inv64,inv128;
double maxc,maxd,maxf,maxn,maxy;
double p,q,r;             /* cubic coefficients */
double qrts[4][3];        /* quartic roots for each cubic root */
double rt3;               /* square root of 3 */
double rtsc[4],rtsd[4],rtsf[4],rtsn[4],rtsq[4],rtsy[4]; /* roots given by each algorithm */
double rterc[4],rterd[4],rterf[4],rtern[4],rterq[4],rtery[4]; /* errors of roots */
double worstc,worstd,worstf,worstn,worsty; /* worst error for each algotithm */
double worst3[3];         /* worst error for a given cubic root */
double x0,x1,x2,x3;

int agr,dis,zer;
int agreecd,agreecf,agreecn,agreecy;
int agreedf,agreedn,agreedy;
int agreefn,agreefy,agreeny;
int debug;                /* <1 for lots of diagnostics, >5 for none */   
int docubic,doquartic;
int iterate;
int j3;
int n1,n2,n3,n4[3];
int n,nc,nd,nf,nn,ny;
int nrtsc,nrtsd,nrtsf,nrtsn,nrtsq,nrtsy;
int nt3,nt2,nt2sq,nden,nm4,ndet1,ndet2,ndet3,ndet4,ndet5,nn;
int nqud[NCASES];
int ncub[NCASES];
int nchr[NCASES];
int ndes[NCASES];
int nfer[NCASES];
int nneu[NCASES];
int nyac[NCASES];
int nzc,nzd,nzf,nzn,nzy;
int zerodf,zerodn,zerody,zerofn,zerofy,zerony;
int zerodfn,zerodfy,zerodny,zerofny;
int tot;

double exp(),log(),sqrt(),cos(),acos(),fabs(),errors();
int cubic() ;
int descartes(),ferrari(),neumark(),yacfraid();

/***********************************/

main(argc,argv)
int argc;
char *argv[];
{
   double fabs();

   printf("quartic version 54\n");
   debug = 5;
   setcns();
   setargs(argc,argv);

more: if (argc > 1)
   {
      docoeffs();
      if ((fabs(x0)+fabs(x1)+fabs(x2)+fabs(x3)) != doub0)
         goto more;
      exit(0);
   }
   else
   {
      looptest();
      exit(0);
   }
} /* main */
/****************************************/

setcns()
/* 
     set up constants.

     called by main, quartictest.
*/
{
      int j;

      doub0 = (double)0;
      doub1 = (double)1;
      doub2 = (double)2;
      doub3 = (double)3;
      doub4 = (double)4;
      doub5 = (double)5;
      doub6 = (double)6;
      doub8 = (double)8;
      doub9 = (double)9;
      doub12 = (double)12;
      doub16 = (double)16;
      doub24 = (double)24;
      doub27 = (double)27;
      doub64 = (double)64;
      inv2 = doub1/doub2;
      inv3 = doub1/doub3;
      inv4 = doub1/doub4;
      inv8 = doub1/doub8;
      inv16 = doub1/doub16;
      inv32 = doub1/(double)32;
      inv64 = doub1/doub64;
      inv128 = doub1/(double)128;
      d3o8 = doub3/doub8;
      d3o256 = doub3/(double)256;
      rt3 = sqrt(doub3);

      doubtol = doub1;
      for (j = 1; doub1+doubtol > doub1; ++ j)
      {
          doubtol *= inv2;
      }
      doubtol = sqrt(doubtol);

      doubmin = inv2;
      for (j = 1; j <= 100; ++ j)
      {
          doubmin=doubmin*doubmin;
          if ((doubmin*doubmin) <= (doubmin*doubmin*inv2))
              break;
      }
      doubmax=doub1/sqrt(doub2*doubmin);

} /* setcns */
/***********************************/

setargs(argc,argv)
int argc;
char *argv[];
/*
   determine whether to do looptest or just individual equation

   10 Dec 2003 Don Herbison-Evans

   called by main
*/
{
   int j;

   docubic = FALSE;
   doquartic = FALSE;
   iterate = FALSE;
   for (j = 1; j < argc; ++j)
   {
      if (argv[j][0] == '-')
      {
         if (argv[j][1] == 'a')
         {
            iterate = TRUE;
            printf("iterate cubics\n");
         }
         else
         if (argv[j][1] == 'c')
         {
            docubic = TRUE;
            sscanf(argv[++j],"%d",&n);
            printf("do cubic %d\n",n);
         }
         else
         if (argv[j][1] == 'q') 
         {
            doquartic = TRUE;
            sscanf(argv[++j],"%d",&n);
            printf("do quartic %d\n",n);
         }
         else
         if (argv[j][1] == 'd')
         {
            sscanf(argv[++j],"%d",&debug);
            printf("debug %d\n",debug);
         }
      }
   }
} /* setargs */
/********************************************************/

looptest()
/*
   loop through a range of coefficient values 
   comparing the algorithms
  
   called by main.
   calls compare.
*/
{
   double ten4,ten8;
   double v[10];
   int j,k,l,m;

   printf("quartic loop test\n");
   nc = 0;
   nd = 0;
   nf = 0;
   nn = 0;
   ny = 0;
   maxc = doub0;
   maxd = doub0;
   maxf = doub0;
   maxn = doub0;
   maxy = doub0;
   tot = 0;
   zer = 0;
   agr = 0;
   dis = 0;
   agreecd = 0;
   agreecf = 0;
   agreecn = 0;
   agreecy = 0;
   agreedf = 0;
   agreedn = 0;
   agreedy = 0;
   agreefn = 0;
   agreefy = 0;
   agreeny = 0;
   for (j = 0; j < NCASES; ++j)
   {
      nqud[j] = 0;
      ncub[j] = 0;
      ndes[j] = 0;
      nfer[j] = 0;
      nneu[j] = 0;
      nyac[j] = 0;
   }
   ten4 = (double)10000;
   ten8 = ten4*ten4;
   j = 0;
   for (a = ten8; a > inv2/ten8; a /= ten4)
   {
      v[j] = a;
      ++j;
   }
   for (j = 0; j < 5; ++j)
      v[j+5] = -v[j];
   for (j = 0; j < 10; ++j)
   {
      a = v[j];
      for (k = 0; k < 10; ++k)
      {
         b = v[k];
         for (l = 0; l < 10; ++l)
         {
            c = v[l];
            for (m = 0; m < 10; ++m)
            {
               d = v[m];
               compare();
            } /* for m */
         } /* for l */
      } /* for k */
   } /* for j */
   printf("total cases: %d\n",
          tot);
   printf("number of real roots: five agree %d, disagree %d\n",
          agr,dis);
   printf("two agree: cd %d, cf %d, cn %d, cy %d\n",
          agreecd,agreecf,agreecn,agreecy);
   printf("           df %d, dn %d, dy %d, fn %d, fy %d, ny %d\n",
          agreedf,agreedn,agreedy,agreefn,agreefy,agreeny);
   printf("5 agree on no real roots: %d\n",
          zer);
   printf("no real roots: chris %d, desc %d, ferr %d, neum %d, yacf %d\n",
          nzc,nzd,nzf,nzn,nzy);
   printf("Christianson : best %d, worst %g %g %g %g, error %g\n",
          nc,cc[0],cc[1],cc[2],cc[3],maxc);
   printf("Descartes : best %d, worst %g %g %g %g, error %g\n",
          nd,cd[0],cd[1],cd[2],cd[3],maxd);
   printf("Ferrari : best %d, worst %g %g %g %g, error %g\n",
          nf,cf[0],cf[1],cf[2],cf[3],maxf);
   printf("Neumark : best %d, worst %g %g %g %g, error %g\n",
          nn,cn[0],cn[1],cn[2],cn[3],maxn);
   printf("Yacoub  : best %d, worst %g %g %g %g, error %g\n",
          ny,cy[0],cy[1],cy[2],cy[3],maxy);
   cases();
} /* looptest */
/********************************************/

cases()
/*
   print statistics

    2 Dec 2003 Don Herbison-Evans

   called by looptest, quartictest.
*/
{
   int j;

   printf("quadratic cases:\n");
   for (j = 0; j < NCASES; ++j)
   {
      printf("  %2d %d,",j,nqud[j]);
      if (j%5 == 4) printf("\n");
   }
   printf("\n");

   printf("cubic cases:\n");
   for (j = 0; j < NCASES; ++j)
   {
      printf("  %2d %d,",j,ncub[j]);
      if (j%5 == 4) printf("\n");
   }
   printf("\n");

   printf("descartes cases:\n");
   for (j = 0; j < NCASES; ++j)
   {
      printf("  %2d %d,",j,ndes[j]);
      if (j%5 == 4) printf("\n");
   }
   printf("\n");

   printf("ferrari cases:\n");
   for (j = 0; j < NCASES; ++j)
   {
      printf("  %2d %d,",j,nfer[j]);
      if (j%5 == 4) printf("\n");
   }
   printf("\n");

   printf("neumark cases:\n");
   for (j = 0; j < NCASES; ++j)
   {
      printf("  %2d %d,",j,nneu[j]);
      if (j%5 == 4) printf("\n");
   }
   printf("\n");

   printf("yacfraid cases:\n");
   for (j = 0; j < NCASES; ++j)
   {
      printf("  %2d %d,",j,nyac[j]);
      if (j%5 == 4) printf("\n");
   }
   printf("\n");

   printf("chris cases:\n");
   for (j = 0; j < NCASES; ++j)
   {
      printf("  %2d %d,",j,nchr[j]);
      if (j%5 == 4) printf("\n");
   }
   printf("\n");

} /* cases */
/************************************/

docoeffs()
/*
    read a set of coefficients from standard input
    and compare results of the different algorithms.

    16 Nov 2003 Don Herbison-Evans

    called by main.
    calls cubictest, quartictest.
*/
{
   char buf[256];
   int ix0,ix1,ix2,ix3;
   int descartes(),ferrari(),neumark(),yacfraid(),chris();
   float fa,fb,fc,fd;
   float fp,fq,fr;
   double fabs();

   printf("docoeffsa %d\n",n);
   x0 = doub0;
   x1 = doub0;
   x2 = doub0;
   x3 = doub0;  
   if (NULL != gets(buf))
   {
      if (buf[0] == 'q')
         exit(0);
      if (n == 0)
      {
         x0 = doub1;
         if (docubic == TRUE)
         {
            sscanf(buf,"%g %g %g",&fp,&fq,&fr);
            p = (double)fp;
            q = (double)fq;
            r = (double)fr;
         }
         else
         {
            sscanf(buf,"%g %g %g %g",&fa,&fb,&fc,&fd);
            printf("\n%g %g %g %g\n",fa,fb,fc,fd);
            a = (double)fa;
            b = (double)fb;
            c = (double)fc;
            d = (double)fd;
            printf("\n%g %g %g %g\n",a,b,c,d);
         }
      }
      else
      if (n == 1) 
      {
         sscanf(buf,"%d\n",&ix0);
         x0 = (double)ix0;
      }
      else
      if (n == 2)
      {
         sscanf(buf,"%d %d\n",&ix0,&ix1);
         x0 = (double)ix0;
         x1 = (double)ix1;
      }
      else
      if (n == 3)
      {
         sscanf(buf,"%d %d %d\n",&ix0,&ix1,&ix2);
         x0 = (double)ix0;
         x1 = (double)ix1;
         x2 = (double)ix2;
      }
      else
      if (n == 4)
      {
         sscanf(buf,"%d %d %d %d\n",&ix0,&ix1,&ix2,&ix3);
         x0 = (double)ix0;
         x1 = (double)ix1;
         x2 = (double)ix2;
         x3 = (double)ix3;
      }
   }
   if ((fabs(x0)+fabs(x1)+fabs(x2)+fabs(x3)) != doub0)
   {
      if (docubic == TRUE) cubictest();
      if (doquartic == TRUE) quartictest();
   }
} /* docoeffs */
/*****************************************/

cubictest()
/*
   called by docoeffs.
*/
{
   int j;
   int nrtsc;
   double rtsc[4];

   if (n == 1)
   {
      p = -x0;
      q = doub1;
      r = -x0;
   }
   else
   if (n == 3)
   {
      p = -(x0+x1+x2);
      q = x0*x1 + x0*x2 + x1*x2;
      r = -x0*x1*x2;
   }
   printf("\ncubic test: %g %g %g\n",
         x0,x1,x2);
   printf(" x^3 + %gx^2 + %gx + %g\n",
         p,q,r);
   nrtsc = cubic(p,q,r,rtsc);
   printf("%d roots\n",nrtsc);
   for (j = 0; j < nrtsc; ++j)
      printf("%g  ",rtsc[j]);
   printf("\n");
} /* cubictest */
/******************************************/

quartictest()
/*
   called by docoeffs.
   calls descartes, ferrari, neumark, yacfraid, 
         errors, cases, setcns.
*/
{
   int j;

   printf("quartictest %d\n",n);
   if (n == 4)
   {
      a = -(x0 + x1 + x2 + x3);
      b = x0*x1 + x0*x2 + x0*x3 + x1*x2 + x1*x3 + x2*x3;
      c = -(x0*x1*x2 + x0*x1*x3 + x0*x2*x3 + x1*x2*x3);
      d = x0*x1*x2*x3;
   }
   else
   if (n == 2)
   {
      a = -(x0 + x1);
      b = x0*x1;
      c = a;
      d = b;
   }
   printf("\nquartic test: %g %g %g %g\n",
         x0,x1,x2,x3);
   printf("x^4 + %gx^3 + %gx^2 + %gx + %g\n",
         a,b,c,d);

   setcns();
   printf("\nDescartes\n");
   nrtsd = descartes(a,b,c,d,rtsd);
   worstd = errors(a,b,c,d,rtsd,rterd,nrtsd);
   if (nrtsd > 0)
         for (j = 0; j < nrtsd; ++j)
            printf(" %g %g\n",rtsd[j],rterd[j]);
   else 
         printf("no real roots found\n");

   setcns();
   printf("\nFerrari\n");
   nrtsf = ferrari(a,b,c,d,rtsf);
   worstf = errors(a,b,c,d,rtsf,rterf,nrtsf);
   if (nrtsf > 0)
         for (j = 0; j < nrtsf; ++j)
            printf(" %g %g\n",rtsf[j],rterf[j]);
   else 
         printf("no real roots found\n");

   setcns();
   printf("\nNeumark\n");
   nrtsn = neumark(a,b,c,d,rtsn);
   worstn = errors(a,b,c,d,rtsn,rtern,nrtsn);
   if (nrtsn > 0)
         for (j = 0; j < nrtsn; ++j)
            printf(" %g %g\n",rtsn[j],rtern[j]);
   else 
         printf("no real roots found\n");

   setcns();
   printf("\nYacoub and Fraidenraich\n");
   nrtsy = yacfraid(a,b,c,d,rtsy);
   worsty = errors(a,b,c,d,rtsy,rtery,nrtsy);
   if (nrtsy > 0)
        for (j = 0; j < nrtsy; ++j)
            printf(" %g %g\n",rtsy[j],rtery[j]);
   else 
         printf("no real roots found\n");

   setcns();
   printf("\nChristianson\n");
   nrtsc = chris(a,b,c,d,rtsc);
   worstc = errors(a,b,c,d,rtsc,rterc,nrtsc);
   if (nrtsc > 0)
         for (j = 0; j < nrtsc; ++j)
            printf(" %g %g\n",rtsc[j],rterc[j]);
   else 
         printf("no real roots found\n");
   printf("\n x^4 + %gx^3 + %gx^2 + %gx + %g\n",
         a,b,c,d);
   if (debug < 2) cases();
} /* quartictest */
/***************************************/

compare()
/*
    called by looptest.
    calls errors, descartes, ferrari, neumark, yacfraid, chris.
*/
{
   int n;
   double errors();

   ++tot;
   nrtsd = descartes(a,b,c,d,rtsd);
   worstd = errors(a,b,c,d,rtsd,rterd,nrtsd);
   nrtsf = ferrari(a,b,c,d,rtsf);
   worstf = errors(a,b,c,d,rtsf,rterf,nrtsf);
   nrtsn = neumark(a,b,c,d,rtsn,worstn);
   worstn = errors(a,b,c,d,rtsn,rtern,nrtsn);
   nrtsy = yacfraid(a,b,c,d,rtsy);
   worsty = errors(a,b,c,d,rtsy,rtery,nrtsy);
   nrtsc = chris(a,b,c,d,rtsc);
   worstc = errors(a,b,c,d,rtsc,rterc,nrtsc);

   if (nrtsd == 0) ++nzd;
   if (nrtsf == 0) ++nzf;
   if (nrtsn == 0) ++nzn;
   if (nrtsy == 0) ++nzy;
   if (nrtsc == 0) ++nzc;

   if (nrtsc == nrtsd) ++agreecd;
   if (nrtsc == nrtsf) ++agreecf;
   if (nrtsc == nrtsn) ++agreecn;
   if (nrtsc == nrtsy) ++agreecy;
   if (nrtsd == nrtsf) ++agreedf;
   if (nrtsd == nrtsn) ++agreedn;
   if (nrtsd == nrtsy) ++agreedy;
   if (nrtsf == nrtsn) ++agreefn;
   if (nrtsf == nrtsy) ++agreefy;
   if (nrtsn == nrtsy) ++agreeny;

   if ((nrtsc == nrtsd) && (nrtsc == nrtsf)
    && (nrtsc == nrtsn) && (nrtsc == nrtsy))
   {
      if (nrtsf == 0) ++zer;
      ++agr;
   }
   else
      ++dis;

     
   if ((nrtsc == nrtsd) && (nrtsc == nrtsf)
    && (nrtsc == nrtsn) && (nrtsc == nrtsy) && (nrtsc != 0))
   {
       if ((worstd < worstf) && (worstd < worstn)
        && (worstd < worsty) && (worstd < worstc)) ++nd;

       if ((worstf < worstd) && (worstf < worstn)
        && (worstf < worsty) && (worstf < worstc)) ++nf;

       if ((worstn < worstd) && (worstn < worstf) 
        && (worstn < worsty) && (worstn < worstc)) ++nn;

       if ((worsty < worstd) && (worsty < worstf)
        && (worsty < worstn) && (worsty < worstc)) ++ny;

       if ((worstc < worstd) && (worstc < worstf) 
        && (worstc < worstn) && (worstc < worsty)) ++nc;

       if (maxd < worstd)
       {
          maxd = worstd;
          cd[0]=a; cd[1]=b; cd[2]=c; cd[3]=d;
       }
       if (maxf < worstf)
       {
          maxf = worstf;
          cf[0]=a; cf[1]=b; cf[2]=c; cf[3]=d;
       }
       if (maxn < worstn)
       {
          maxn = worstn;
          cn[0]=a; cn[1]=b; cn[2]=c; cn[3]=d;
       }
       if (maxy < worsty)
       {
          maxy = worsty;
          cy[0]=a; cy[1]=b; cy[2]=c; cy[3]=d;
       }
       if (maxc < worstc)
       {
           maxc = worstc;
           cc[0]=a; cc[1]=b; cc[2]=c; cc[3]=d;
       }

   } /* nrts agree */
} /* compare */
/****************************************************/

double errors(a,b,c,d,rts,rterr,nrts)
double a,b,c,d,rts[4],rterr[4];
int nrts;
/*
   find the errors

   called by quartictest, docoeff, compare,
             chris, descartes, ferrari, neumark, yacfraid.
*/
{
   int k;
   double deriv,test,worst;
   double fabs(),sqrt(),curoot();

   worst = doubmax;
   if (nrts > 0)
   {
      worst =  doub0;
      for (  k = 0 ; k < nrts ; ++ k )
      {
         test = (((rts[k]+a)*rts[k]+b)*rts[k]+c)*rts[k]+d;
         if (test == doub0) rterr[k] = doub0;
         else
         {
            deriv =
               ((doub4*rts[k]+doub3*a)*rts[k]+doub2*b)*rts[k]+c;
            if (deriv != doub0)
               rterr[k] = fabs(test/deriv);
            else
            {
               deriv = (doub12*rts[k]+doub6*a)*rts[k]+doub2*b;
               if (deriv != doub0)
                   rterr[k] = sqrt(fabs(test/deriv));
               else
               {
                  deriv = doub24*rts[k]+doub6*a;
                  if (deriv != doub0)
                     rterr[k] = curoot(fabs(test/deriv));
                  else
                     rterr[k] = sqrt(sqrt(fabs(test)/doub24));
               }
            }
         }
         if (rts[k] != doub0) rterr[k] /= rts[k];
         if (rterr[k] < doub0) rterr[k] = -rterr[k];
         if (rterr[k] > worst) worst = rterr[k];
      }
   }
   return(worst);
} /* errors */
/**********************************************/

double acos3(x)
   double x ;
/* 
     find cos(acos(x)/3) 
    
     16 Jul 1981   Don Herbison-Evans 

     called by cubic . 
*/
{
   double value;
   double acos(),cos();

   value = cos(acos(x)*inv3);
   return(value);
} /* acos3 */
/***************************************/

double curoot(x)
   double x ;
/* 
     find cube root of x.

     30 Jan 1989   Don Herbison-Evans 

     called by cubic . 
*/
{
   double exp(),log();
   double value;
   double absx;
   int neg;

   neg = 0;
   absx = x;
   if (x < doub0)
   {
      absx = -x;
      neg = 1;
   }
   if (absx != doub0) value = exp( log(absx)*inv3 );
      else value = doub0;
   if (neg == 1) value = -value;
   return(value);
} /* curoot */
/****************************************************/

int quadratic(b,c,rts)
   double b,c,rts[4];
/* 
     solve the quadratic equation - 

         x**2 + b*x + c = 0 

     14 Jan 2004   cut determinant in quadratic call
     29 Nov 2003   improved
     16 Jul 1981   Don Herbison-Evans

     called by  cubic,quartic,chris,descartes,ferrari,neumark.
*/
{
   int j,nquad;
   double dis,rtdis ;
   double ans[2] ;

   dis = b*b - doub4*c;
   rts[0] = doub0;
   rts[1] = doub0;
   if (b == doub0)
   {
      if (c == doub0)
      {
         nquad = 2;
         ++nqud[0];
      }
      else
      {
         if (c < doub0)
         {
            nquad = 2;
            rts[0] = sqrt(-c);
            rts[1] = -rts[0];
            ++nqud[1];
         }
         else
         {
            nquad = 0;
            ++nqud[2];
         }         
      }
   }
   else
   if (c == doub0)
   {
      nquad = 2;
      rts[0] = -b;
      ++nqud[3];
   }
   else
   if (dis >= doub0)
   {
      nquad = 2 ;
      rtdis = sqrt(dis);
      if (b > doub0) 
      {
         rts[0] = ( -b - rtdis)*inv2;
         ++nqud[4];
      }
      else
      {
         rts[0] = ( -b + rtdis)*inv2;
         ++nqud[5];
      }
      if (rts[0] == doub0)
      {
         rts[1] =  -b;
         ++nqud[6];
      }
      else
      {
         rts[1] = c/rts[0];
         ++nqud[7];
      }
   }
   else
   {
      nquad = 0;
      ++nqud[8];
   }
   if (debug < 1)
   {
      printf("quad  b %g   c %g  dis %g\n",
         b,c,dis);
      printf("      %d %g %g\n",
         nquad,rts[0],rts[1]);
   }
   return(nquad);
} /* quadratic */
/**************************************************/

int cubic(p,q,r,v3)
double p,q,r,v3[4];
/* 
   find the real roots of the cubic - 
       x**3 + p*x**2 + q*x + r = 0 

     12 Dec 2003 initialising n,m,po3
     12 Dec 2003 allow return of 3 zero roots if p=q=r=0
      2 Dec 2003 negating j if p>0
      1 Dec 2003 changing v from (sinsqk > doub0) to (sinsqk >= doub0)
      1 Dec 2003 test changing v from po3sq+po3sq to doub2*po3sq
     16 Jul 1981 Don Herbison-Evans

   input parameters - 
     p,q,r - coeffs of cubic equation. 

   output- 
     the number of real roots
     v3 - the roots. 

   global constants -
     rt3 - sqrt(3) 
     inv3 - 1/3 
     doubmax - square root of largest number held by machine 

     method - 
     see D.E. Littlewood, "A University Algebra" pp.173 - 6 

     15 Nov 2003 output 3 real roots: Don Herbison-Evans
        Apr 1981 initial version: Charles Prineas

     called by  cubictest,quartic,chris,yacfraid,neumark,descartes,ferrari.
     calls      quadratic,acos3,curoot,cubnewton. 
*/
{
   int    j,n3;
   double po3,po3sq,qo3,po3q;
   double uo3,u2o3,uo3sq4,uo3cu4;
   double v,vsq,wsq;
   double m1,m2,mcube;
   double muo3,s,scube,t,cosk,rt3sink,sinsqk;
   double ans;
   double curoot();
   double acos3();
   double sqrt(),fabs();
   int quadratic();

   m1=doub0;  m2=doub0;  po3=doub0;
   v=doub0;  uo3=doub0; cosk=doub0;
   if (r == doub0)
   {
      ++ncub[0];
      n3 = quadratic(p,q,v3);
      v3[n3++] = doub0;
      goto done;
   }
   if ((p == doub0) && (q == doub0))
   {
      ++ncub[1];
      v3[0] = curoot(-r);
      v3[1] = v3[0];
      v3[2] = v3[0];
      n3 = 3;
      goto done;
   }
   n3 = 1;
   if ((p > doubmax) || (p <  -doubmax))
   {
      v3[0] = -p;
      ++ncub[2];
      goto done;
   }
   if ((q > doubmax) || (q <  -doubmax))
   {
       if (q > doub0)
       {
          v3[0] =  -r/q;
          ++ncub[3];
          goto done;
       }
       else
       if (q < doub0)
       {
          v3[0] = -sqrt(-q);
          ++ncub[4];
          goto done; 
       }
       else
       {
          v3[0] = doub0;
          ++ncub[5];
          goto done;
       }
   }
   else
   if ((r > doubmax)|| (r < -doubmax))
   {
      v3[0] =  -curoot(r);
      ++ncub[6];
      goto done;
   }
   else
   {
      po3 = p*inv3;
      po3q = po3*q;
      po3sq = po3*po3;
      if (po3sq > doubmax)
      {
         v3[0] = -p;
         ++ncub[7];
         goto done;
      }
      else
      {
         v = r + po3*(po3sq+po3sq - q);
         if ((v > doubmax) || (v < -doubmax))
         {
            v3[0] = -p;
            ++ncub[8];
            goto done;
         }
         else
         {
            vsq = v*v;
            qo3 = q*inv3;
            uo3 = qo3 - po3sq;
            u2o3 = uo3 + uo3;
            if ((u2o3 > doubmax) || (u2o3 < -doubmax))
            {
               if (p == doub0)
               {
                  if (q > doub0)
                  {
                     v3[0] =  -r/q;
                     ++ncub[9];
                     goto done;
                  }
		      else
                  if (q < doub0)
                  {
                     v3[0] =  -sqrt(-q);
                     ++ncub[10];
                     goto done;
                  }
                  else
                  {
                     v3[0] = doub0;
                     ++ncub[11];
                     goto done;
                  }
               }
               else
               {
                  v3[0] = -q/p;
                  ++ncub[12];
                  goto done;
               }
            }
            uo3sq4 = u2o3*u2o3;
            if (uo3sq4 > doubmax)
            {
               if (p == doub0)
               {
                  if (q > doub0)
                  {
                     v3[0] = -r/q;
                     ++ncub[13];
                     goto done;
                  }
                  else
	  	      if (q < doub0)
                  {
                     v3[0] = -sqrt(-q);
                     ++ncub[14];
                     goto done;
                  }
		      else
                  {
                     v3[0] = doub0;
                     ++ncub[15];
                     goto done;
                  }
               }
               else
               {
                  v3[0] = -q/p;
                  ++ncub[16];
                  goto done;
               }
            }
            uo3cu4 = uo3sq4*uo3;
            wsq = uo3cu4 + vsq;
            if (wsq > doub0)
            {
/* 
     cubic has one real root -
*/
               if (v <= doub0)
               {
                  mcube = ( -v + sqrt(wsq))*inv2;
                  ++ncub[17];
               }
               else
               {
                  mcube = ( -v - sqrt(wsq))*inv2;
                  ++ncub[18];
               }
               m1 = curoot(mcube);
               if (m1 != doub0)
               {
                  m2 = -uo3/m1;
                  ++ncub[19];
               }
               else
               {
                  m2 = doub0;
                  ++ncub[20];
               }
               v3[0] = m1 + m2 - po3;
            }
            else
            {
/* 
     cubic has three real roots -
*/
               if (uo3 < doub0)
               {
                  muo3 = -uo3;
                  if (muo3 > doub0)
                  {
                     s = sqrt(muo3);
                     ++ncub[21];
                     if (p > doub0)
                     {
                        s = -s;
                        ++ncub[22];
                     }
                  }
		      else
                  {
                     s = doub0;
                     ++ncub[23];
                  }
                  scube = s*muo3;
		      if (scube == doub0)
		      {
                     v3[0] = m1 + m2 - po3;
                     n3 = 1;
                     ++ncub[24];
		      }
                  else
                  {
                     t =  -v/(scube+scube);
                     cosk = acos3(t);
                     v3[0] = (s+s)*cosk - po3;
                     n3 = 1 ;
                     sinsqk = doub1 - cosk*cosk;
		         if (sinsqk >= doub0)
		         {
                        rt3sink = rt3*sqrt(sinsqk);
                        v3[1] = s*(-cosk + rt3sink) - po3;
                        v3[2] = s*(-cosk - rt3sink) - po3;
                        n3 = 3;
                        ++ncub[25];
                     }
                     else ++ncub[26];
                  }
               }
               else
/* 
     cubic has multiple root -  
*/
               {
                  ++ncub[27];
                  v3[0] = curoot(v) - po3;
                  v3[1] = v3[0];
                  v3[2] = v3[0];
                  n3 = 3;
               }
            }
         }
      }
   }
done:
   if (debug < 1)
   {
      printf("cubic %d %g %g %g\n",n3,p,q,r);
      for (j = 0; j < n3; ++j)
         printf("   %d %13g %13g\n",j,v3[j],r+v3[j]*(q+v3[j]*(p+v3[j])));
      printf("v %g,  uo3 %g,  m1 %g,   m2 %g,  po3 %g, cosk %g\n",
         v,uo3,m1,m2,po3,cosk);
      for (j = 0; j < 28; ++j)
      {
         printf("  %d",ncub[j]);
         if ((j%10) == 9) printf("\n");
      }
      printf("\n");
   }
   if (iterate == TRUE) cubnewton(p,q,r,n3,v3);
   return(n3) ;
} /* cubic */
/****************************************************/

cubnewton(p,q,r,n3,v3)
int n3;
double p,q,r,v3[4];
/*
    improve roots of cubic by Newton-Raphson iteration

    5 Jan 2004  Don Herbison-Evans

    called by cubic.
*/
{
   int j,k;
   double corr,deriv,err,root;

   if (debug < 2) printf("cubnewtona %d %g\n",
        n3,v3[0]);
   for (j = 0; j < n3; ++j)
   {
      for (k = 0; k < 4; ++k)
      {
         root = v3[j];
         err = ((root + p)*root + q)*root + r;
         deriv = (doub3*root + doub2*p)*root + q;
         if (deriv != doub0) corr = err/deriv; else corr = doub0;
         v3[j] -= corr;
         if (debug < 1) printf("cubnewtonb %d %d %g %g %g %g %g\n",
              j,k,root,err,deriv,corr,v3[j]);
      }
   }
} /* cubnewton */
/****************************************************/

int quartic(a,b,c,d,rts)
double a,b,c,d,rts[4];
/*
   Solve quartic equation using either
   quadratic, Ferrari's or Neumark's algorithm.

   called by 
   calls  descartes, ferrari, neumark, yacfraid.

     15 Dec 2003  added yacfraid
     10 Dec 2003  added descartes with neg coeffs
     21 Jan 1989  Don Herbison-Evans
*/
{
   int quadratic(),ferrari(),neumark(),yacfraid();
   int j,k,nq,nr;
   double odd, even;
   double roots[4];
   double fabs();

   if (fabs(a) > doubmax)
      nr = yacfraid(a,b,c,d,rts);
   else
   if ((a == doub0) && (c == doub0))
   {
      nq = quadratic(b,d,roots);
      nr = 0;
      for (j = 0; j < nq; ++j)
      {
         if (roots[0] >= doub0)
         {
            rts[0] = sqrt(roots[0]);
            rts[1] = -rts[0];
            nr = 2;
         }
         if (roots[1] >= doub0)
         {
            rts[nr] = sqrt(roots[1]);
            rts[nr+1] = -rts[nr];
            nr += 2;
         }
      }
   }
   else
   {
      k = 0;
      if (a < doub0) k += 2; 
      if (b < doub0) k += 1; 
      if (c < doub0) k += 8; 
      if (d < doub0) k += 4;
      switch (k)
      {
              case 0 : nr = neumark(a,b,c,d,rts); break;
              case 1 : nr = neumark(a,b,c,d,rts); break;
              case 2 : nr = neumark(a,b,c,d,rts); break;
              case 3 : nr = ferrari(a,b,c,d,rts); break;
              case 4 : nr = neumark(a,b,c,d,rts); break;
              case 5 : nr = descartes(a,b,c,d,rts); break;
              case 6 : nr = neumark(a,b,c,d,rts); break;
              case 7 : nr = neumark(a,b,c,d,rts); break;
              case 8 : nr = neumark(a,b,c,d,rts); break;
              case 9 : nr = ferrari(a,b,c,d,rts); break;
              case 10 : nr = neumark(a,b,c,d,rts); break;
              case 11 : nr = neumark(a,b,c,d,rts); break;
              case 12 : nr = neumark(a,b,c,d,rts); break;
              case 13 : nr = neumark(a,b,c,d,rts); break;
              case 14 : nr = neumark(a,b,c,d,rts); break;
              case 15 : nr = descartes(-a,b,-c,d,rts); break;
      }
      if (k == 15)
        for (j = 0; j < nr; ++j)
           rts[j] = -rts[j];
   }
   return(nr);
} /* quartic */
/*****************************************/

int descartes(a,b,c,d,rts)
double a,b,c,d,rts[4];
/*
   Solve quartic equation using
   Descartes-Euler-Cardano algorithm

   called by quartic, compare, quartictest.

   Strong, T. "Elemementary and Higher Algebra"
      Pratt and Oakley, p. 469 (1859)

   16 Jul 1981  Don Herbison-Evans
*/
{
   double errors();
   int quadratic(),cubic();

   int j;
   double v1[4],v2[4],v3[4];
   double k,y;
   double dis;
   double p,q,r;
   double e0,e1,e2;
   double g,h;
   double asq;
   double ainv4;
   double e1invk;

   asq = a*a;
   e2 = b - asq*d3o8;
   e1 = c + a*(asq*inv8 - b*inv2);
   e0 = d + asq*(b*inv16 - asq*d3o256) - a*c*inv4;

   p = doub2*e2;
   q = e2*e2 - doub4*e0;
   r = -e1*e1;

   n3 = cubic(p,q,r,v3);
   for (j3 = 0; j3 < n3; ++j3)
   {
      y = v3[j3];
      if (y <= doub0)
      { 
         n4[j3] = 0;
         ++ndes[0];
      } /* y<0 */
      else
      {
         k = sqrt(y);
         ainv4 = a*inv4;
         e1invk = e1/k;
         g = (y + e2 + e1invk)*inv2;
         h = (y + e2 - e1invk)*inv2 ;
         n1 = quadratic(-k, g, v1);
         n2 = quadratic( k, h, v2);
         qrts[0][j3] = v1[0] - ainv4;
         qrts[1][j3] = v1[1] - ainv4;
         qrts[n1][j3] = v2[0] - ainv4;
         qrts[n1+1][j3] = v2[1] - ainv4;
         n4[j3]= n1 + n2;  
         ++ndes[1];
      } /* y>=0 */
donej3:
      for (j = 0; j < n4[j3]; ++j)
         rts[j] = qrts[j][j3];
      worst3[j3] = errors(a,b,c,d,rts,rterd,n4[j3]);
   } /* j3 loop */
done:
   j3 = 0;
   if (n3 != 1)
   {
      if ((n4[0] == n4[1]) && (n4[1] == n4[2]))
         ++ndes[NCASES-2];
      else 
         ++ndes[NCASES-1];
      if ((n4[1] > n4[j3]) || 
         ((worst3[1] < worst3[j3] ) && (n4[1] == n4[j3]))) j3 = 1;
      if ((n4[2] > n4[j3]) ||
         ((worst3[2] < worst3[j3] ) && (n4[2] == n4[j3]))) j3 = 2;
   }
   for (j = 0; j < n4[j3]; ++j)
         rts[j] = qrts[j][j3];
   if (debug < 1)
       printf("descartes chose cubic %d %g %g\n\n",j3,v3[j3],worst3[j3]);
   ++ndes[30+n4[j3]];
   ++ndes[35+j3];
   return(n4[j3]);
} /* descartes */
/****************************************************/

int ferrari(a,b,c,d,rts)
   double a,b,c,d,rts[4];
/* 
     solve the quartic equation - 

     x**4 + a*x**3 + b*x**2 + c*x + d = 0 

     called by quartic, compare, quartictest.
     calls     cubic, quadratic.

     input - 
     a,b,c,e - coeffs of equation. 

     output - 
     n4 - number of real roots. 
     rts - array of root values. 

     method :  Ferrari - Lagrange
     Theory of Equations, H.W. Turnbull p. 140 (1947)

     16 Jul 1981 Don Herbison-Evans

     calls  cubic, quadratic 
*/
{
   double errors();
   int cubic(),quadratic();

   int j,k;
   double asqinv4;
   double ainv2;
   double d4;
   double yinv2;
   double v1[4],v2[4],v3[4];
   double p,q,r;
   double y;
   double e,f,esq,fsq,ef;
   double g,gg,h,hh;

   ainv2 = a*inv2;
   asqinv4 = ainv2*ainv2;
   d4 = d*doub4 ;

   p = b;
   q = a*c-d4;
   r = (asqinv4 - b)*d4 + c*c;
   n3 = cubic(p,q,r,v3);
   for (j3 = 0; j3 < n3; ++j3)
   {
      y = v3[j3];
      yinv2 = y*inv2;
      esq = asqinv4 - b - y;
      fsq = yinv2*yinv2 - d;
      if ((esq < doub0) && (fsq < doub0))
      {
         n4[j3] = 0;
         ++nfer[0];
      }
      else
      {
         ef = -(inv4*a*y + inv2*c);
         if ( ((a > doub0)&&(y > doub0)&&(c > doub0))
           || ((a > doub0)&&(y < doub0)&&(c < doub0))
           || ((a < doub0)&&(y > doub0)&&(c < doub0))
           || ((a < doub0)&&(y < doub0)&&(c > doub0))
           ||  (a == doub0)||(y == doub0)||(c == doub0))
/* use ef - */
         {
               if ((b < doub0)&&(y < doub0))
               {
                  e = sqrt(esq);
                  f = ef/e;
                  ++nfer[1];
               }
               else if (d < doub0)
               {
                  f = sqrt(fsq);
                  e = ef/f;
                  ++nfer[2];
               }
               else
               {
                  if (esq > doub0)
                  {
                     e = sqrt(esq);
                     ++nfer[3];
                  }
                  else
                  {
                     e = doub0;
                     ++nfer[4];
                  }
                  if (fsq > doub0)
                  {
                     f = sqrt(fsq);
                     ++nfer[5];
                  }
                  else
                  {
                     f = doub0;
                     ++nfer[6];
                  }
                  if (ef < doub0)
                  {
                     f = -f;
                     ++nfer[7];
                  }
               }
         }
         else
/* use esq and fsq - */
         {
               if (esq > doub0)
               {
                  e = sqrt(esq);
                  ++nfer[8];
               }
               else
               {
                  e = doub0;
                  ++nfer[9];
               }
               if (fsq > doub0)
               {
                  f = sqrt(fsq);
                  ++nfer[10];
               }
               else
               {
                  f = doub0;
                  ++nfer[11];
               }
               if (ef < doub0)
               {
                  f = -f;
                  ++nfer[12];
               }
            }
/* note that e >= doub0 */
            g = ainv2 - e;
            gg = ainv2 + e;
            if ( ((b > doub0)&&(y > doub0))
               || ((b < doub0)&&(y < doub0)) )
            {
               if ((a > doub0) && (e > doub0)
	         || (a < doub0) && (e < doub0) )
               {
                  g = (b + y)/gg;
                  ++nfer[13];
               }
               else
	         if ((a > doub0) && (e < doub0)
	         || (a < doub0) && (e > doub0) )
               {
                  gg = (b + y)/g;
                  ++nfer[14];
               }
               else
                  ++nfer[15];
            }
            hh = -yinv2 + f;
            h = -yinv2 - f;
            if ( ((f > doub0)&&(y < doub0))
              || ((f < doub0)&&(y > doub0)) )
            {
               h = d/hh;
               ++nfer[16];
            }
            else
            if ( ((f < doub0)&&(y < doub0))
               || ((f > doub0)&&(y > doub0)) )
            {
               hh = d/h;
               ++nfer[17];
            }
            else
               ++nfer[18];

            n1 = quadratic(gg,hh,v1);
            n2 = quadratic(g,h,v2);
            n4[j3] = n1+n2;
            qrts[0][j3] = v1[0];
            qrts[1][j3] = v1[1];
            qrts[n1+0][j3] = v2[0];
            qrts[n1+1][j3] = v2[1];
      } 
donej3:
      for (j = 0; j < n4[j3]; ++j)
         rts[j] = qrts[j][j3];
      worst3[j3] = errors(a,b,c,d,rts,rterf,n4[j3]);
   } /* j3 loop */
done:
   j3 = 0;
   if (n3 != 1)
   {
      if ((n4[0] == n4[1]) && (n4[1] == n4[2]))
         ++nfer[NCASES-2];
      else 
         ++nfer[NCASES-1];
      if ((n4[1] > n4[j3]) || 
         ((worst3[1] < worst3[j3] ) && (n4[1] == n4[j3]))) j3 = 1;
      if ((n4[2] > n4[j3]) ||
         ((worst3[2] < worst3[j3] ) && (n4[2] == n4[j3]))) j3 = 2;
   }
   for (j = 0; j < n4[j3]; ++j)
         rts[j] = qrts[j][j3];
   if (debug < 1)
       printf("ferrari chose cubic %d %g %g\n\n",j3,v3[j3],worst3[j3]);
   ++nfer[30+n4[j3]];
   ++nfer[35+j3];
   return(n4[j3]);
} /* ferrari */
/*****************************************/

int neumark(a,b,c,d,rts)
   double a,b,c,d,rts[4];
/* 
     solve the quartic equation - 

     x**4 + a*x**3 + b*x**2 + c*x + d = 0 

     called by quartic, compare, quartictest.
     calls     cubic, quadratic.

     input parameters - 
     a,b,c,e - coeffs of equation. 

     output parameters - 
     n4 - number of real roots. 
     rts - array of root values. 

     method -  S. Neumark 
        "Solution of Cubic and Quartic Equations" - Pergamon 1965 

      1 Dec 1985   translated to C with help of Shawn Neely
     16 Jul 1981   Don Herbison-Evans

*/
{
   int j,k;
   double y,g,gg,h,hh,gdis,gdisrt,hdis,hdisrt,g1,g2,h1,h2;
   double bmy,gerr,herr,y4,bmysq;
   double v1[4],v2[4],v3[4];
   double asq;
   double d4;
   double p,q,r;
   double hmax,gmax;
   double errors();
   int cubic();
   int quadratic();

   if (d == doub0)
   {
      n3 = 0;
      n4[0] = cubic(a,b,c,rts);
      for (j = 0; j < n4[0]; ++j)
         qrts[j][0] = rts[j];
      qrts[n4[0]++][0] = doub0;
      goto done;
   }
   asq = a*a;
   d4 = d*doub4;
   p =  -b*doub2;
   q = b*b + a*c - d4;
   r = (c - a*b)*c + asq*d;
   if (debug < 3)
      printf("neumarka %g %g %g %g,  %g %g %g\n",
      a,b,c,d,p,q,r);
   n3 = cubic(p,q,r,v3);
   for (j3 = 0; j3 < n3; ++j3)
   {
      y = v3[j3];
      bmy = b - y;
      y4 = y*doub4;
      bmysq = bmy*bmy;
      gdis = asq - y4;
      hdis = bmysq - d4;
      if (debug < 3)
         printf("neumarkb %g %g\n",
            gdis,hdis);
      if ((gdis < doub0) || (hdis < doub0))
      {
         n4[j3] = 0;
         ++nneu[0];
      }
      else   
      {
         g1 = a*inv2;
         h1 = bmy*inv2;
         gerr = asq + y4;
         herr = hdis;
         if (d > doub0)
         {
            herr = bmysq + d4;
            ++nneu[1];
         }
         if ((y < doub0) || (herr*gdis > gerr*hdis))
         {
            gdisrt = sqrt(gdis);
            g2 = gdisrt*inv2;
            if (gdisrt != doub0)
            {
               h2 = (a*h1 - c)/gdisrt;
               ++nneu[2];
            }
            else
            {
               h2 = doub0;
               ++nneu[3];
            }   
         }
         else
         {
            hdisrt = sqrt(hdis);
            h2 = hdisrt*inv2;
            if (hdisrt != doub0)
            {
               g2 = (a*h1 - c)/hdisrt;
               ++nneu[4];
            }
            else
            {
               g2 = doub0;
               ++nneu[5];
            }
         }
/* 
     note that in the following, the tests ensure non-zero 
     denominators -  
*/
         h = h1 - h2;
         hh = h1 + h2;
         hmax = hh;
         if (hmax < doub0)
         {
            hmax =  -hmax;
            ++nneu[6];
         }
         if (hmax < h)
         {
            hmax = h;
            ++nneu[7];
         }   
         if (hmax <  -h)
         {
            hmax =  -h;
            ++nneu[8];
         }
         if ((h1 > doub0)&&(h2 > doub0))
         {
            h = d/hh;
            ++nneu[9];
         }
         if ((h1 < doub0)&&(h2 < doub0))
         {
            h = d/hh;
            ++nneu[10];
         }
         if ((h1 > doub0)&&(h2 < doub0))
         {
            hh = d/h;
            ++nneu[11];
         }
         if ((h1 < doub0)&&(h2 > doub0))
         {
            hh = d/h;
            ++nneu[12];
         }
         if (h > hmax)   
         {
            h = hmax;
            ++nneu[13];
         }
         if (h <  -hmax)
         {
            h =  -hmax;
            ++nneu[14];
         }
         if (hh > hmax)
         {
            hh = hmax;
            ++nneu[15];
         }
         if (hh < -hmax)
         {
            hh =  -hmax;
            ++nneu[16];
         }

         g = g1 - g2;
         gg = g1 + g2;
         gmax = gg;
         if (gmax < doub0)
         {
            gmax = -gmax;
            ++nneu[17];
         }
         if (gmax < g)
         {
            gmax = g;
            ++nneu[18];
         }
         if (gmax < -g)
         {
            gmax = -g;
            ++nneu[19];
         }
         if ((g1 > doub0)&&(g2 > doub0))
         {
            g = y/gg;
            ++nneu[20];
         }
         if ((g1 < doub0)&&(g2 < doub0))
         {
            g = y/gg;
            ++nneu[21];
         }
         if ((g1 > doub0)&&(g2 < doub0))
         {
            gg = y/g;
            ++nneu[22];
         }
         if ((g1 < doub0)&&(g2 > doub0))
         {
            gg = y/g;
            ++nneu[23];
         }
         if (g > gmax)
         {
            g = gmax;
            ++nneu[24];
         }
         if (g <  -gmax)
         {
            g = -gmax;
            ++nneu[25];
         }
         if (gg > gmax)
         {   
            gg = gmax;
            ++nneu[26];
         }
         if (gg <  -gmax)
         {
            gg = -gmax;
            ++nneu[27];
         }
 
         n1 = quadratic(gg,hh,v1);
         n2 = quadratic(g,h,v2);
         n4[j3] = n1 + n2;
         qrts[0][j3] = v1[0];
         qrts[1][j3] = v1[1];
         qrts[n1+0][j3] = v2[0];
         qrts[n1+1][j3] = v2[1];
      } 
donej3:
      for (j = 0; j < n4[j3]; ++j)
         rts[j] = qrts[j][j3];
      worst3[j3] = errors(a,b,c,d,rts,rtern,n4[j3]);
   } /* j3 loop */
done:
   j3 = 0;
   if (n3 > 1)
   {
      if ((n4[0] == n4[1]) && (n4[1] == n4[2]))
         ++nneu[NCASES-2];
      else 
         ++nneu[NCASES-1];
      if ((n4[1] > n4[j3]) || 
         ((worst3[1] < worst3[j3] ) && (n4[1] == n4[j3]))) j3 = 1;
      if ((n4[2] > n4[j3]) ||
         ((worst3[2] < worst3[j3] ) && (n4[2] == n4[j3]))) j3 = 2;
   }
   for (j = 0; j < n4[j3]; ++j)
         rts[j] = qrts[j][j3];
   if (debug < 1)
       printf("neumark chose cubic %d %g %g\n\n",j3,v3[j3],worst3[j3]);
   ++nneu[30+n4[j3]];
   ++nneu[35+j3];
   return(n4[j3]);
} /* neumark */
/****************************************************/

int yacfraid(a,b,c,d,rts)
   double a,b,c,d,rts[4];
/* 
     solve the quartic equation - 

     x**4 + a*x**3 + b*x**2 + c*x + d = 0 

     called by quartic, compare, quartictest.
     calls     cubic, quadratic.

     input parameters - 
     a,b,c,e - coeffs of equation. 

     output parameters - 
     n4 - number of real roots. 
     rts - array of root values. 

     method - 
        K.S. Brown 
        Reducing Quartics to Cubics,
        http://www.seanet.com/~ksbrown/kmath296.htm (1967)
 
        Michael Daoud Yacoub & Gustavo Fraidenraich
        "A new simple solution of the general quartic equation"
        Revised 16 Feb 2004

     14 Nov 2003 Don Herbison-Evans
*/
{
   int i,j;
   double y;
   double v1[4],v2[4],v3[4];
   double asq,acu;
   double b4;
   double det0,det1,det2,det3;
   double det0rt,det1rt,det2rt,det3rt;
   double e,f,g,h,k;
   double fsq,gsq,hsq,invk;
   double P,Q,R,U;
   double errors();
   int cubic();
   int quadratic();

   if (d == doub0)
   {
      n3 = 0;
      n4[0] = cubic(a,b,c,rts);
      for (j = 0; j < n4[0]; ++j)
         qrts[j][0] = rts[j];
      qrts[n4[0]++][0] = doub0;
      goto done;
   }
   asq = a*a;
   acu = a*asq;
   b4 = b*doub4;
   n3 = 0;

   P = asq*b - b4*b + doub2*a*c + doub16*d ;
   Q = asq*c - b4*c + doub8*a*d;
   R = asq*d - c*c ;
   U = acu - b4*a + doub8*c;
   n4[0] = 0; 
   if (U == doub0)
   {
      if (P == doub0)
      {
         det0 = doub3*asq - doub8*b;
         if (det0 < doub0)
         {
            ++nyac[0];
            goto done;
         }
         det0rt = sqrt(det0);
         qrts[0][0] = (-a + det0rt)*inv4;
         qrts[1][0] = qrts[0][0];
         qrts[2][0] = (-a - det0rt)*inv4;
         qrts[3][0] = qrts[2][0];
         ++nyac[1];
         n4[0] = 4;
         goto done;
      } /* P=0 */
      else
      {
         det1 = asq*asq - doub8*asq*b + doub16*b*b - doub64*d;
         if (det1 < doub0)
         {
            ++nyac[2];
            goto done;;
         }
         n4[0] = 0;
         det1rt =  sqrt(det1);
         det2 = doub3*asq - doub8*b + doub2*det1rt;
         if (det2 >= doub0)
         {
            det2rt = sqrt(det2);
            qrts[0][0] = (-a + det2rt)*inv4;
            qrts[1][0] = (-a - det2rt)*inv4;
            n4[0] = 2;
            ++nyac[3];
         }
         det3 = doub3*asq - doub8*b - doub2*det1rt;
         if (det3 >= doub0)
         {
            det3rt = sqrt(det3);
            qrts[n4[0]++][0] = (-a + det3rt)*inv4;
            qrts[n4[0]++][0] = (-a - det3rt)*inv4;
            ++nyac[5];
         }
         if (n4[0] == 0) ++nyac[6];
         goto done;
      } /* P<>0 */
   }

   n3 = cubic(P/U,Q/U,R/U,v3);
   for (j3 = 0; j3 < n3; ++j3)
   {
      y = v3[j3];
      j = 0;
      k = a + doub4*y;
      if (k == doub0)
      {
         ++nyac[9];
         goto donej3;
      }
      invk = doub1/k;
      e = (acu - doub4*c - doub2*a*b + (doub6*asq - doub16*b)*y)*invk;
      fsq = (acu + doub8*c - doub4*a*b)*invk;
      if (fsq < doub0)
      {
         ++nyac[10];
         goto donej3;
      }
      f = sqrt(fsq);
      gsq = doub2*(e + f*k);
      hsq = doub2*(e - f*k);
      if (gsq >= doub0)
      {
         ++nyac[11];
         g = sqrt(gsq);
         qrts[j++][j3] = (-a - f - g)*inv4;
         qrts[j++][j3] = (-a - f + g)*inv4;
      }
      if (hsq >= doub0)
      {
         ++nyac[12];
         h = sqrt(hsq);
         qrts[j++][j3] = (-a + f - h)*inv4;
         qrts[j++][j3] = (-a + f + h)*inv4;
      }
      if (debug < 1)
      {
         printf("j3 %d y %g k %g fsq %g gsq %g hsq %g\n",
            j3,y,k,fsq,gsq,hsq);
         printf("e %g f %g g %g h %g\n",e,f,g,h);
      }
donej3:
      n4[j3] = j;
      for (j = 0; j < n4[j3]; ++j)
         rts[j] = qrts[j][j3];
      worst3[j3] = errors(a,b,c,d,rts,rtery,n4[j3]);
   } /* j3 loop */
done:
   j3 = 0;
   if (n3 > 1)
   {
      if ((n4[0] == n4[1]) && (n4[1] == n4[2]))
         ++nyac[NCASES-2];
      else 
      {
         ++nyac[NCASES-1];
         if ((n4[0] != n4[1]) && (n4[0] != n4[2]) && (n4[1] != n4[2]))
            printf("yace %d %d %d %g %g %g %g\n",
               n4[0],n4[1],n4[2],a,b,c,d);
      }
      if ((n4[1] > n4[j3]) || 
         ((worst3[1] < worst3[j3] ) && (n4[1] == n4[j3]))) j3 = 1;
      if ((n4[2] > n4[j3]) ||
         ((worst3[2] < worst3[j3] ) && (n4[2] == n4[j3]))) j3 = 2;
   }
   for (j = 0; j < n4[j3]; ++j)
         rts[j] = qrts[j][j3];
   ++nyac[30+n4[j3]];
   ++nyac[35+j3];
   if (debug < 1)
       printf("yacfraid chose cubic %d %g  %g\n\n",j3,v3[j3],worst3[j3]);
   return(n4[j3]);
} /* yacfraid */
/*****************************************/

int chris(a,b,c,d,rts)
double a,b,c,d,rts[4];
/*
   Solve quartic equation using
   Christianson's algorithm

   called by compare, quartictest.
   calls errors, quadratic, cubic.

   Bruce Christianson, Solving Quartics Using Palindromes,
      Mathematical Gazette, Vol. 75, pp. 327-328 (1991)

   14 Jan 2004  Don Herbison-Evans
*/
{
   double errors();
   int quadratic(),cubic();

   int j;
   int n0,n1,n2;
   double v0[4],v1[4],v2[4],v3[4];
   double y,ysq,ycu;
   double p,q,r;
   double asq,acu,aqu,ao4;
   double e0,e1,e2;
   double k,ksq,kcu,kqu,kquinv,k1,k2;
   double g,h,g1,Z1,Z2;

   if (d == doub0)
   {
      n3 = 0;
      n4[0] = cubic(a,b,c,rts);
      for (j = 0; j < n4[0]; ++j)
         qrts[j][0] = rts[j];
      qrts[n4[0]++][0] = doub0;
      goto done;
   }
   asq = a*a;
   acu = asq*a;
   aqu = acu*a;
   ao4 = a*inv4;
   e2 = b - d3o8*asq;
   e1 = c - inv2*b*a + inv8*acu;
   e0 = d - inv4*c*a + inv16*b*asq - d3o256*acu*a;
   if (debug < 1)
      printf("chrisa e0 %g   e1 %g   e2 %g\n",e0,e1,e2);
   if (e1 == doub0)
   { 
      n3 = 0;
      n4[0] = 0;
      ++nchr[0];
      n3 = 3;
      v3[2] = doub0;
      v3[1] = doub0;
      v3[0] = -inv8*(doub16*e0 - doub4*e2*e2);
   }
   else
   {
      p = (inv2*b*asq - inv2*b*b - inv2*c*a + doub2*d - doub3*inv32*aqu)/e1;
      q = doub3*inv16*asq - inv2*b;
      r = inv16*b*a - inv8*c - inv64*acu;
      if (debug < 1) printf("chrisb %g %g %g\n",p,q,r);
      ++nchr[1];
      n3 = cubic(p,q,r,v3);
   }
   for (j3 = 0; j3 < n3; ++j3)
   {
      y = v3[j3];
      n4[j3] = 0;
      ysq = y*y;
      ycu = y*ysq;
      if ((y == doub0) ||
         (( y < doub0) && (e1 <= doub0) && (e0 >= doub0) && (e2 >= doub0)))
      {
         kqu = y*ycu + e2*ysq + e1*y + e0;
         if (kqu <= doub0)
         {
             ksq = doub0;
             ++nchr[2];
             goto donej3;
         }
         ksq = sqrt(kqu);
      }
      else
      {
         ksq = ysq + inv2*e2 + inv4*e1/y;
         if (ksq <= doub0)
         {
             kqu = doub0;
             ++nchr[3];
             goto donej3;
         }
         kqu = ksq*ksq;
      }
      k = sqrt(ksq);
      kcu = k*ksq;
      kquinv = doub1/kqu;
      g = doub4*y*kcu;
      h = (doub6*ysq + e2)*ksq;
      if (debug < 1)
      {
         k2 = sqrt(sqrt(fabs(y*ycu + e2*ysq + e1*y + e0)));
         if (y == doub0)
             k1 = doub0;
         else
             k1 = sqrt(fabs(ysq + inv2*e2 + inv4*e1/y));
         g1 = (doub4*ycu + doub2* y*e2 + e1)*k;
         printf("chrisc %g %g %g %g %g\n",k,k1,y,g,g1,h);
      }
      n0 = quadratic(g*kquinv,h*kquinv-doub2,v0);
      if (n0 < 1)
      {
         ++nchr[4]; 
         goto donej3;
      }
      Z1 = v0[0];
      Z2 = v0[1];
      n1 = quadratic(-Z1,doub1,v1);
      if (n1 > 0)
      {
          ++nchr[5];
          n4[j3] = n1;
          qrts[0][j3] = y + k*v1[0] - ao4;
          qrts[1][j3] = y + k*v1[1] - ao4;
      }
      n2 = quadratic(-Z2,doub1,v2);
      if (n2 > 0)
      {
          ++nchr[6];
          n4[j3] += n2;
          qrts[n1][j3] = y + k*v2[0] - ao4;
          qrts[n1+1][j3] = y + k*v2[1] - ao4;
      }
donej3:
      if (debug < 1)
         printf("chrisd %g %g\n",ksq,kqu);
      for (j = 0; j < n4[j3]; ++j)
         rts[j] = qrts[j][j3];
      worst3[j3] = errors(a,b,c,d,rts,rterc,n4[j3]);
   } /* j3 loop */
done:
   j3 = 0;
   if (n3 > 1)
   {
      if ((n4[0] == n4[1]) && (n4[1] == n4[2]))
         ++nchr[NCASES-2];
      else 
      {
         ++nchr[NCASES-1];
         if (debug < 1)
            printf("chrise %d %d %d %g %g %g %g\n",
               n4[0],n4[1],n4[2],a,b,c,d);
      }
      if ((n4[1] > n4[j3]) || 
         ((worst3[1] < worst3[j3] ) && (n4[1] == n4[j3]))) j3 = 1;
      if ((n4[2] > n4[j3]) ||
         ((worst3[2] < worst3[j3] ) && (n4[2] == n4[j3]))) j3 = 2;
   }
   for (j = 0; j < n4[j3]; ++j)
         rts[j] = qrts[j][j3];
   if (debug < 1)
       printf("chris chose cubic %d %g %g\n\n",j3,v3[j3],worst3[j3]);
   ++nchr[30+n4[j3]];
   ++nchr[35+j3];
   return(n4[j3]);
} /* chris */

