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