#include "Racine.h"
#include "MathUtil.h"

#include <stdio.h>
#include <cmath>

#define NCASES 40

// constructeur permettant d'initialiser les variables
Quartic::Quartic():
      debug(6),iterate(false),
      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 (int j = 1; doub1+doubtol > doub1; ++ j)
      {
          doubtol *= inv2;
      }
      doubtol = sqrt(doubtol);

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


/****************************************************/
/*
//   find the errors
//
//   called by quartictest, docoeff, compare,
//            chris, descartes, ferrari, neumark, yacfraid.
/****************************************************/

double Quartic::errors(double a,double b,double c,double d,double rts[4],double rterr[4],int nrts)
{
   double deriv,test,worst;

   worst = doubmax;
   if (nrts > 0)
   {
      worst =  doub0;
      for (int   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] = Dabs(test/deriv);
            else
            {
               deriv = (doub12*rts[k]+doub6*a)*rts[k]+doub2*b;
               if (deriv != doub0)
                   rterr[k] = sqrt(Dabs(test/deriv));
               else
               {
                  deriv = doub24*rts[k]+doub6*a;
                  if (deriv != doub0)
                     rterr[k] = curoot(Dabs(test/deriv));
                  else
                     rterr[k] = sqrt(sqrt(Dabs(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 */

/**********************************************/
//     find cos(acos(x)/3) 
//    
//     16 Jul 1981   Don Herbison-Evans 
//
//     called by cubic . 
/**********************************************/

double Quartic::acos3(double x)
{
   double value;
   double y = acos(x);
   
//   value = std::cos(acos(x)*inv3);  pb de nan
   // on traite dans le cas d'une erreur (qui ça arrive !!)
   // comme ça ne marche pas pour l'instant sur la lo-lg2m-045 on spécifie les machines !!!
  #ifdef SYSTEM_MAC_OS_X
   switch(fpclassify(y))
    { case FP_NAN: case FP_INFINITE:
        { // cas d'un nombre infini on fait quelques tests
          if (Dabs(x-1.) <= ConstMath::pasmalpetit)
          	{ // dans ce cas on a affaire à une erreur d'arrondi
          	  y=0.;
          	}
          else if (x > 1.)
          	{ // il y a un pb dans l'algo, on le signal mais on continu
          	  cout << "\n erreur dans la fonction Qartic::acos3, l'argument est > 1 , on limite ! "
          	       << " x-1= " << (x-1.);
          	  y=0.;
          	};
        };
        break;
    };
   #endif     
  #ifdef SYSTEM_MAC_OS_X_unix
   switch(fpclassify(y))
    { case FP_NAN: case FP_INFINITE:
        { // cas d'un nombre infini on fait quelques tests
          if (Dabs(x-1.) <= ConstMath::pasmalpetit)
          	{ // dans ce cas on a affaire à une erreur d'arrondi
          	  y=0.;
          	}
          else if (x > 1.)
          	{ // il y a un pb dans l'algo, on le signal mais on continu
          	  cout << "\n erreur dans la fonction Qartic::acos3, l'argument est > 1 , on limite ! "
          	       << " x-1= " << (x-1.);
          	  y=0.;
          	};
        };
        break;
    };
   #endif     
   // calcul de la valeur de retour
   value = std::cos(y*inv3);
   return(value);
}; /* acos3 */


/***************************************/
//     find cube root of x.
//
//     30 Jan 1989   Don Herbison-Evans 
//
//     called by cubic . 
/***************************************/

double Quartic::curoot(double x)
{
   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 */

/****************************************************/
//     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 Quartic::quadratic(double b,double c,double rts[4])
{
   int nquad;
   double dis,rtdis ;

   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 */

/**************************************************/
//   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 Quartic::cubic(double p,double q,double r,double v3[4]) 
{
   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;

   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 */



/****************************************************/
//    improve roots of cubic by Newton-Raphson iteration
//
//    5 Jan 2004  Don Herbison-Evans
//
//    called by cubic.
/****************************************************/

void Quartic::cubnewton(double p,double q,double r,int n3,double v3[4])
{
   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 */

/****************************************************/
//   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 Quartic::quartic(double a,double b,double c,double d,double rts[4])
{
   int j,k,nq,nr;
   double roots[4];

   if (Dabs(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 */


//*****************************************/
//   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
//*****************************************/
int Quartic::descartes(double a,double b,double c,double d,double rts[4])
{
   int j;
   double v1[4],v2[4],v3[4];
   double k,y;
   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 */

//****************************************************/
//     solve the quartic equation - 
//
//     x**4 + a*x**3 + b*x**2 + c*x + d = 0 
//
//     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 
//****************************************************/
int Quartic::ferrari(double a,double b,double c,double d,double rts[4])
{
   int j;
   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 */


//*****************************************/
//     solve the quartic equation - 
//
//     x**4 + a*x**3 + b*x**2 + c*x + d = 0 
//
//    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 Quartic::neumark(double a,double b,double c,double d,double rts[4])
{
   int j;
   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;

   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 */



//****************************************************/ 
//     solve the quartic equation - 
//     x**4 + a*x**3 + b*x**2 + c*x + d = 0 
//
//     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 Quartic::yacfraid(double a,double b,double c,double d,double rts[4])
{
   int j;
   double y;
   double 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;

   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 */


//*****************************************/
//   Solve quartic equation using
//   Christianson's algorithm
//   calls errors, quadratic, cubic.
//
//   Bruce Christianson, Solving Quartics Using Palindromes,
//      Mathematical Gazette, Vol. 75, pp. 327-328 (1991)
//
//   14 Jan 2004  Don Herbison-Evans
//*****************************************/

int Quartic::chris(double a,double b,double c,double d,double rts[4])
{
   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(Dabs(y*ycu + e2*ysq + e1*y + e0)));
         if (y == doub0)
             k1 = doub0;
         else
             k1 = sqrt(Dabs(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 */

