quartic_solve.C

Go to the documentation of this file.
00001 /*
00002  * This file is part of the "Archon" framework.
00003  * (http://files3d.sourceforge.net)
00004  *
00005  * Copyright © 2002 by Kristian Spangsege and Brian Kristiansen.
00006  *
00007  * Permission to use, copy, modify, and distribute this software and
00008  * its documentation under the terms of the GNU General Public License is
00009  * hereby granted. No representations are made about the suitability of
00010  * this software for any purpose. It is provided "as is" without express
00011  * or implied warranty. See the GNU General Public License
00012  * (http://www.gnu.org/copyleft/gpl.html) for more details.
00013  *
00014  * The characters in this file are ISO8859-1 encoded.
00015  *
00016  * The documentation in this file is in "Doxygen" style
00017  * (http://www.doxygen.org).
00018  */
00019 
00031 #include <math.h>
00032 #include <iostream>
00033 
00034 namespace Archon
00035 {
00036   namespace Math
00037   {
00038     static const double nought = 0;
00039     static const double doub1  = 1;
00040     static const double doub2  = 2;
00041     static const double doub3  = 3;
00042     static const double doub4  = 4;
00043     static const double doub6  = 6;
00044     static const double doub12 = 12;
00045     static const double doub24 = 24;
00046     static const double inv2   = 1.0 / 2;
00047     static const double inv3   = 1.0 / 3;
00048     static const double inv4   = 1.0 / 4;
00049     static const double rt3    = sqrt(3);
00050 
00051     static bool initialized = false;
00052 
00053     static double doubmin;  // smallest double number
00054     static double doubmax;  // approx square root of max double number
00055     static double doubtol;  // tolerance of double numbers
00056 
00057     static void initialize()
00058     {
00059       doubtol = doub1;
00060       while(doub1+doubtol > doub1) doubtol *= inv2;
00061       doubtol = sqrt(doubtol);
00062 
00063       doubmin = inv2;
00064       for(int i=1; i<=100; ++i)
00065       {
00066         doubmin = doubmin*doubmin ;
00067         if((doubmin*doubmin) <= (doubmin*doubmin*inv2)) break;
00068       }
00069       doubmax = 0.7/sqrt(doubmin);
00070 
00071       initialized = true;
00072 
00073       std::cerr << "Quartic solver initialized\n";
00074     }
00075 
00083     static int qudrtc(double b, double c, double rts[4], double dis)
00084     {
00085       int nquad;
00086       double rtdis;
00087 
00088       if(dis >= nought)
00089       {
00090         nquad = 2;
00091         rtdis = sqrt(dis);
00092         if(b > nought) rts[0] = ( -b - rtdis)*inv2;
00093         else rts[0] = ( -b + rtdis)*inv2;
00094         if(rts[0] == nought) rts[1] =  -b;
00095         else rts[1] = c/rts[0];
00096       }
00097       else
00098       {
00099         nquad = 0;
00100         rts[0] = nought;
00101         rts[1] = nought;
00102       }
00103       return nquad;
00104     }
00105 
00113     static inline double acos3(double x)
00114     {
00115       return cos(acos(x)*inv3);
00116     }
00117 
00125     static double curoot(double x)
00126     {
00127       double value;
00128       double absx;
00129       int neg;
00130 
00131       neg = 0;
00132       absx = x;
00133       if(x < nought)
00134       {
00135         absx = -x;
00136         neg = 1;
00137       }
00138       value = exp(log(absx)*inv3);
00139       if(neg == 1) value = -value;
00140       return value;
00141     }
00142 
00166     static double cubic(double p, double q, double r)
00167     {
00168       double po3, po3sq, qo3;
00169       double uo3, u2o3, uo3sq4, uo3cu4;
00170       double v, vsq, wsq;
00171       double m, mcube = 0, n;
00172       double muo3, s, scube, t, cosk, sinsqk;
00173       double root;
00174 
00175       m = nought;
00176       if((p > doubmax) || (p <  -doubmax)) root = -p;
00177       else if((q > doubmax) || (q <  -doubmax))
00178       {
00179         if(q > nought) root = -r/q;
00180         else root = -sqrt(-q);
00181       }
00182       else if((r > doubmax)|| (r <  -doubmax)) root = -curoot(r);
00183       else
00184       {
00185         po3 = p*inv3;
00186         po3sq = po3*po3;
00187         if(po3sq > doubmax) root =  -p ;
00188         else
00189         {
00190           v = r + po3*(po3sq + po3sq - q);
00191           if((v > doubmax) || (v < -doubmax)) root = -p;
00192           else
00193           {
00194             vsq = v*v;
00195             qo3 = q*inv3;
00196             uo3 = qo3 - po3sq;
00197             u2o3 = uo3 + uo3;
00198             if((u2o3 > doubmax) || (u2o3 < -doubmax))
00199             {
00200               if(p == nought)
00201               {
00202                 if(q > nought) root =  -r/q;
00203                 else root =  -sqrt(-q);
00204               }
00205               else root = -q/p;
00206             }
00207             uo3sq4 = u2o3*u2o3;
00208             if(uo3sq4 > doubmax)
00209             {
00210               if(p == nought)
00211               {
00212                 if(q > nought) root = -r/q;
00213                 else root = -sqrt(fabs(q));
00214               }
00215               else root = -q/p;
00216             }
00217             uo3cu4 = uo3sq4*uo3;
00218             wsq = uo3cu4 + vsq;
00219             if(wsq >= nought)
00220             {
00221               /* 
00222                  cubic has one real root 
00223               */
00224               if(v <= nought) mcube = ( -v + sqrt(wsq))*inv2;
00225               if(v  > nought) mcube = ( -v - sqrt(wsq))*inv2;
00226               m = curoot(mcube);
00227               if(m != nought) n = -uo3/m;
00228               else n = nought;
00229               root = m + n - po3;
00230             }
00231             else
00232             {
00233               /* 
00234                  cubic has three real roots 
00235               */
00236               if(uo3 < nought)
00237               {
00238                 muo3 = -uo3;
00239                 s = sqrt(muo3);
00240                 scube = s*muo3;
00241                 t =  -v/(scube+scube);
00242                 cosk = acos3(t);
00243                 if(po3 < nought)
00244                   root = (s+s)*cosk - po3;
00245                 else
00246                 {
00247                   sinsqk = doub1 - cosk*cosk;
00248                   if(sinsqk < nought) sinsqk = nought;
00249                   root = s*( -cosk - rt3*sqrt(sinsqk)) - po3;
00250                 }
00251               }
00252               else
00253                 /* 
00254                    cubic has multiple root -  
00255                 */
00256                 root = curoot(v) - po3 ;
00257             }
00258           }
00259         }
00260       }
00261       return root;
00262     }
00263 
00282     static int ferrari(double a, double b, double c, double d, double rts[4])
00283     {
00284       int nquar, n1, n2;
00285       double asq, ainv2;
00286       double v1[4], v2[4];
00287       double p, q, r;
00288       double y;
00289       double e, f, esq, fsq, ef;
00290       double g, gg, h, hh;
00291 
00292       /*fprintf(stderr,"\nFerrari %g %g %g %g\n",a,b,c,d);*/
00293       asq = a*a;
00294 
00295       p = b ;
00296       q = a*c-doub4*d ;
00297       r = (asq - doub4*b)*d + c*c ;
00298       y = cubic(p,q,r) ;
00299 
00300       esq = inv4*asq - b - y;
00301       if(esq < nought) return 0;
00302 
00303       fsq = inv4*y*y - d;
00304       if(fsq < nought) return 0;
00305 
00306       ef = -(inv4*a*y + inv2*c);
00307       if(a > nought && y > nought && c > nought ||
00308          a > nought && y < nought && c < nought ||
00309          a < nought && y > nought && c < nought ||
00310          a < nought && y < nought && c > nought ||
00311          a == nought || y == nought || c == nought)
00312         // use ef -
00313       {
00314         if(b < nought && y < nought && esq > nought)
00315         {
00316           e = sqrt(esq);
00317           f = ef/e;
00318         }
00319         else if(d < nought && fsq > nought)
00320         {
00321           f = sqrt(fsq);
00322           e = ef/f;
00323         }
00324         else
00325         {
00326           e = sqrt(esq);
00327           f = sqrt(fsq);
00328           if(ef < nought) f = -f;
00329         }
00330       }
00331       else
00332       {
00333         e = sqrt(esq);
00334         f = sqrt(fsq);
00335         if(ef < nought) f = -f;
00336       }
00337       // note that e >= nought
00338       ainv2 = a*inv2;
00339       g = ainv2 - e;
00340       gg = ainv2 + e;
00341       if(b > nought && y > nought ||
00342          b < nought && y < nought)
00343       {
00344         if(a > nought && e != nought) g = (b + y)/gg;
00345         else if(e != nought) gg = (b + y)/g;
00346       }
00347       if(y == nought && f == nought)
00348       {
00349         h  = nought;
00350         hh = nought;
00351       }
00352       else if(f > nought && y < nought ||
00353               f < nought && y > nought)
00354       {
00355         hh = -inv2*y + f;
00356         h  = d/hh;
00357       }
00358       else
00359       {
00360         h  = -inv2*y - f;
00361         hh = d/h;
00362       }
00363       n1 = qudrtc(gg,hh,v1, gg*gg - doub4*hh);
00364       n2 = qudrtc(g,h,v2, g*g - doub4*h);
00365       nquar = n1+n2;
00366       rts[0]    = v1[0];
00367       rts[1]    = v1[1];
00368       rts[n1+0] = v2[0];
00369       rts[n1+1] = v2[1];
00370       return nquar;
00371     }
00372 
00393     static int neumark(double a, double b, double c, double d, double rts[4])
00394     {
00395       int nquar, n1, n2;
00396       double y, g, gg, h, hh, gdis, gdisrt, hdis, hdisrt, g1, g2, h1, h2;
00397       double bmy, gerr, herr, y4, d4, bmysq;
00398       double v1[4], v2[4];
00399       double asq;
00400       double p, q, r;
00401       double hmax, gmax;
00402 
00403       /*fprintf(stderr,"\nNeumark %g %g %g %g\n",a,b,c,d);*/
00404       asq = a*a;
00405 
00406       p =  -b*doub2;
00407       q = b*b + a*c - doub4*d;
00408       r = (c - a*b)*c + asq*d;
00409       y = cubic(p,q,r);
00410 
00411       bmy = b - y;
00412       y4 = y*doub4;
00413       d4 = d*doub4;
00414       bmysq = bmy*bmy;
00415       gdis = asq - y4;
00416       hdis = bmysq - d4;
00417       if(gdis < nought || hdis < nought) return 0;
00418 
00419       g1 = a*inv2;
00420       h1 = bmy*inv2;
00421       gerr = asq + y4;
00422       herr = hdis;
00423       if(d > nought) herr = bmysq + d4;
00424       if(y < nought || herr*gdis > gerr*hdis)
00425       {
00426         gdisrt = sqrt(gdis);
00427         g2 = gdisrt*inv2;
00428         if(gdisrt != nought) h2 = (a*h1 - c)/gdisrt;
00429         else h2 = nought;
00430       }
00431       else
00432       {
00433         hdisrt = sqrt(hdis);
00434         h2 = hdisrt*inv2;
00435         if(hdisrt != nought) g2 = (a*h1 - c)/hdisrt;
00436         else g2 = nought;
00437       }
00438       /* 
00439          note that in the following, the tests ensure non-zero 
00440          denominators -  
00441       */
00442       h  = h1 - h2;
00443       hh = h1 + h2;
00444       hmax = hh;
00445       if(hmax < nought) hmax = -hmax;
00446       if(hmax <  h) hmax =  h;
00447       if(hmax < -h) hmax = -h;
00448       if(h1 > nought && h2 > nought) h  = d/hh;
00449       if(h1 < nought && h2 < nought) h  = d/hh;
00450       if(h1 > nought && h2 < nought) hh = d/h;
00451       if(h1 < nought && h2 > nought) hh = d/h;
00452       if(h  >  hmax) h  =  hmax;
00453       if(h  < -hmax) h  = -hmax;
00454       if(hh >  hmax) hh =  hmax;
00455       if(hh < -hmax) hh = -hmax;
00456 
00457       g    = g1 - g2;
00458       gg   = g1 + g2;
00459       gmax = gg;
00460       if(gmax < nought) gmax = -gmax;
00461       if(gmax <  g) gmax =  g;
00462       if(gmax < -g) gmax = -g;
00463       if(g1 > nought && g2 > nought) g  = y/gg;
00464       if(g1 < nought && g2 < nought) g  = y/gg;
00465       if(g1 > nought && g2 < nought) gg = y/g;
00466       if(g1 < nought && g2 > nought) gg = y/g;
00467       if(g  >  gmax) g  =  gmax;
00468       if(g  < -gmax) g  = -gmax;
00469       if(gg >  gmax) gg =  gmax;
00470       if(gg < -gmax) gg = -gmax;
00471  
00472       n1 = qudrtc(gg,hh,v1, gg*gg - doub4*hh);
00473       n2 = qudrtc(g,h,v2, g*g - doub4*h);
00474       nquar = n1+n2;
00475       rts[0]    = v1[0];
00476       rts[1]    = v1[1];
00477       rts[n1+0] = v2[0];
00478       rts[n1+1] = v2[1];
00479  
00480       return nquar;
00481     }
00482 
00488     static void errors(double a, double b, double c, double d,
00489                        double rts[4], double rterr[4], int nrts)
00490     {
00491       double deriv,test;
00492     
00493       if(nrts > 0)
00494       {
00495         for(int k = 0; k < nrts; ++k)
00496         {
00497           test = (((rts[k]+a)*rts[k]+b)*rts[k]+c)*rts[k]+d;
00498           if(test == nought) rterr[k] = nought;
00499           else
00500           {
00501             deriv =
00502               ((doub4*rts[k]+doub3*a)*rts[k]+doub2*b)*rts[k]+c;
00503             if(deriv != nought)
00504               rterr[k] = fabs(test/deriv);
00505             else
00506             {
00507               deriv = (doub12*rts[k]+doub6*a)*rts[k]+doub2*b;
00508               if(deriv != nought)
00509                 rterr[k] = sqrt(fabs(test/deriv));
00510               else
00511               {
00512                 deriv = doub24*rts[k]+doub6*a;
00513                 if(deriv != nought)
00514                   rterr[k] = curoot(fabs(test/deriv));
00515                 else
00516                   rterr[k] = sqrt(sqrt(fabs(test)/doub24));
00517               }
00518             }
00519           }
00520           // fprintf(stderr, "errorsa  %d %9g %9g\n", k, rts[k], rterr[k]);
00521         }
00522       }
00523       // else fprintf(stderr,"errors ans: none\n");
00524     }
00525 
00537     int quarticSolve(double a, double b, double c, double d,
00538                      double rts[4], double rterr[4])
00539     {
00540       if(!initialized) initialize();
00541 
00542       int j, k, nq, nr = 0;
00543       double odd, even;
00544       double roots[4];
00545 
00546       if(a < 0) odd   = -a; else odd   = a;
00547       if(c < 0) odd  -=  c; else odd  += c;
00548       if(b < 0) even  = -b; else even  = b;
00549       if(d < 0) even -=  d; else even += d;
00550       if(odd < even*doubtol)
00551       {
00552         nq = qudrtc(b,d,roots,b*b-doub4*d);
00553         j = 0;
00554         for(k = 0; k < nq; ++k)
00555         {
00556           if(roots[k] > 0)
00557           {
00558             rts[j] = sqrt(roots[k]);
00559             rts[j+1] = -rts[j];
00560             ++j; ++j;
00561           }
00562         }
00563         nr = j;
00564       }
00565       else
00566       {
00567         if(a < 0) k = 1; else k = 0;
00568         if(b < 0) k += k+1; else k +=k; 
00569         if(c < 0) k += k+1; else k +=k; 
00570         if(d < 0) k += k+1; else k +=k; 
00571         switch(k)
00572         {
00573         case 0:  nr = ferrari(a,b,c,d,rts); break;
00574         case 1:  nr = neumark(a,b,c,d,rts); break;
00575         case 2:  nr = neumark(a,b,c,d,rts); break;
00576         case 3:  nr = ferrari(a,b,c,d,rts); break;
00577         case 4:  nr = ferrari(a,b,c,d,rts); break;
00578         case 5:  nr = neumark(a,b,c,d,rts); break;
00579         case 6:  nr = ferrari(a,b,c,d,rts); break;
00580         case 7:  nr = ferrari(a,b,c,d,rts); break;
00581         case 8:  nr = neumark(a,b,c,d,rts); break;
00582         case 9:  nr = ferrari(a,b,c,d,rts); break;
00583         case 10: nr = ferrari(a,b,c,d,rts); break;
00584         case 11: nr = neumark(a,b,c,d,rts); break;
00585         case 12: nr = ferrari(a,b,c,d,rts); break;
00586         case 13: nr = ferrari(a,b,c,d,rts); break;
00587         case 14: nr = ferrari(a,b,c,d,rts); break;
00588         case 15: nr = ferrari(a,b,c,d,rts); break;
00589         }
00590       }
00591       if(rterr) errors(a, b, c, d, rts, rterr, nr);
00592       return nr;
00593     }
00594 
00615     /*
00616       static int simple(double a, double b, double c, double d, double rts[4])
00617       {
00618       int nquar, n1, n2;
00619       double asq, y;
00620       double v1[4], v2[4];
00621       double p, q, r;
00622       double e, f, esq, fsq;
00623       double g, gg, h, hh;
00624 
00625       //fprintf(stderr,"\nsimple %g %g %g %g\n",a,b,c,d);
00626       asq = a*a;
00627 
00628       p = -b;
00629       q = a*c-doub4*d;
00630       r = -asq*d - c*c + doub4*b*d;
00631       y = cubic(p,q,r);
00632 
00633       esq = inv4*asq - b + y;
00634       fsq = inv4*y*y - d;
00635       if(esq < nought) return 0;
00636       else if(fsq < nought) return 0;
00637       else
00638       {
00639       e  = sqrt(esq);
00640       f  = sqrt(fsq);
00641       g  = inv2*a - e;
00642       h  = inv2*y - f;
00643       gg = inv2*a + e;
00644       hh = inv2*y + f;
00645       n1 = qudrtc(gg,hh,v1, gg*gg - doub4*hh);
00646       n2 = qudrtc(g,h,v2, g*g - doub4*h);
00647       nquar = n1+n2;
00648       rts[0]    = v1[0];
00649       rts[1]    = v1[1];
00650       rts[n1+0] = v2[0];
00651       rts[n1+1] = v2[1];
00652       return nquar;
00653       }
00654       }
00655     */
00656 
00666     /*
00667       static int descartes(double a, double b, double c, double d, double rts[4])
00668       {
00669       int nrts;
00670       int r1, r2;
00671       double v1[4], v2[4];
00672       double y;
00673       double p, q, r;
00674       double A, B, C;
00675       double m, n1, n2;
00676       double d3o8, d3o256;
00677       double inv8, inv16;
00678       double asq;
00679       double Binvm;
00680 
00681       d3o8 = (double)3/(double)8;
00682       inv8 = doub1/(double)8;
00683       inv16 = doub1/(double)16;
00684       d3o256 = (double)3/(double)256;
00685 
00686       //fprintf(stderr,"\nDescartes %f %f %f %f\n",a,b,c,d);
00687       asq = a*a;
00688 
00689       A = b - asq*d3o8;
00690       B = c + a*(asq*inv8 - b*inv2);
00691       C = d + asq*(b*inv16 - asq*d3o256) - a*c*inv4;
00692 
00693       p = doub2*A;
00694       q = A*A - doub4*C;
00695       r = -B*B;
00696 
00697       //        inv64 = doub1/(double)64;
00698       //        p = doub2*b - doub3*a*a*inv4 ;
00699       //        q = b*b - a*a*b - doub4*d + doub3*a*a*a*a*inv16 + a*c;
00700       //        r = -c*c - a*a*a*a*a*a*inv64 - a*a*b*b*inv4
00701       //        -a*a*a*c*inv4 + a*b*c + a*a*a*a*b*inv8;
00702 
00703       y = cubic(p,q,r);
00704       if(y <= nought) 
00705       nrts = 0;
00706       else
00707       {
00708       m = sqrt(y);
00709       Binvm = B/m;
00710       n1 = (y + A + Binvm)*inv2;
00711       n2 = (y + A - Binvm)*inv2;
00712       r1 = qudrtc(-m, n1, v1, y-doub4*n1);
00713       r2 = qudrtc( m, n2, v2, y-doub4*n2);
00714       rts[0] = v1[0]-a*inv4;
00715       rts[1] = v1[1]-a*inv4;
00716       rts[r1] = v2[0]-a*inv4;
00717       rts[r1+1] = v2[1]-a*inv4;
00718       nrts = r1+r2;
00719       }
00720       return nrts;
00721       }
00722     */
00723   }
00724 }

Generated on Sun Jul 30 22:55:43 2006 for Archon by  doxygen 1.4.4