/* rpoly.cpp -- Jenkins-Traub real polynomial root finder. * * Written by C. Bond, with minor changes by Peter Eastman. * This file is in the public domain. * * Translation of TOMS493 from FORTRAN to C. This * implementation of Jenkins-Traub partially adapts * the original code to a C environment by restruction * many of the 'goto' controls to better fit a block * structured form. It also eliminates the global memory * allocation in favor of local, dynamic memory management. * * The calling conventions are slightly modified to return * the number of roots found as the function value. * * INPUT: * op - vector of coefficients in order of * decreasing powers. * degree - integer degree of polynomial * * OUTPUT: * zeror,zeroi - output vectors of the * real and imaginary parts of the zeros. * * RETURN: * returnval: -1 if leading coefficient is zero, otherwise * number of roots found. */ #include #include "rpoly.h" namespace SimTK { template int RPoly::findRoots(T *op, int degree, T *zeror, T *zeroi) { T t,aa,bb,cc,*temp,factor,rot; T *pt; T lo,max,min,xx,yy,cosr,sinr,xxx,x,sc,bnd; T xm,ff,df,dx,infin,smalno,base; int cnt,nz,i,j,jj,l,nm1,zerok; /* The following statements set machine constants. */ base = 2.0; eta = (T) 2.22e-16; infin = (T) 3.4e38; smalno = (T) 1.2e-38; are = eta; mre = eta; lo = smalno/eta; /* Initialization of constants for shift rotation. */ xx = sqrt((T) 0.5); yy = -xx; rot = (T) 94.0; rot *= (T) 0.017453293; cosr = cos(rot); sinr = sin(rot); n = degree; /* Algorithm fails of the leading coefficient is zero. */ if (op[0] == 0.0) return -1; /* Remove the zeros at the origin, if any. */ while (op[n] == 0.0) { j = degree - n; zeror[j] = 0.0; zeroi[j] = 0.0; n--; } if (n < 1) return -1; /* * Allocate memory here */ temp = new T [degree+1]; pt = new T [degree+1]; p = new T [degree+1]; qp = new T [degree+1]; k = new T [degree+1]; qk = new T [degree+1]; svk = new T [degree+1]; /* Make a copy of the coefficients. */ for (i=0;i<=n;i++) p[i] = op[i]; /* Start the algorithm for one zero. */ _40: if (n == 1) { zeror[degree-1] = -p[1]/p[0]; zeroi[degree-1] = 0.0; n -= 1; goto _99; } /* Calculate the final zero or pair of zeros. */ if (n == 2) { quad(p[0],p[1],p[2],&zeror[degree-2],&zeroi[degree-2], &zeror[degree-1],&zeroi[degree-1]); n -= 2; goto _99; } /* Find largest and smallest moduli of coefficients. */ max = 0.0; min = infin; for (i=0;i<=n;i++) { x = fabs(p[i]); if (x > max) max = x; if (x != 0.0 && x < min) min = x; } /* Scale if there are large or very small coefficients. * Computes a scale factor to multiply the coefficients of the * polynomial. The scaling si done to avoid overflow and to * avoid undetected underflow interfering with the convergence * criterion. The factor is a power of the base. */ sc = lo/min; if (sc > 1.0 && infin/sc < max) goto _110; if (sc <= 1.0) { if (max < 10.0) goto _110; if (sc == 0.0) sc = smalno; } l = (int)(log(sc)/log(base) + 0.5); factor = pow(base*(T) 1.0,l); if (factor != 1.0) { for (i=0;i<=n;i++) p[i] = factor*p[i]; /* Scale polynomial. */ } _110: /* Compute lower bound on moduli of roots. */ for (i=0;i<=n;i++) { pt[i] = (fabs(p[i])); } pt[n] = - pt[n]; /* Compute upper estimate of bound. */ x = exp((log(-pt[n])-log(pt[0])) / (T)n); /* If Newton step at the origin is better, use it. */ if (pt[n-1] != 0.0) { xm = -pt[n]/pt[n-1]; if (xm < x) x = xm; } /* Chop the interval (0,x) until ff <= 0 */ while (1) { xm = x*(T) 0.1; ff = pt[0]; for (i=1;i<=n;i++) ff = ff*xm + pt[i]; if (ff <= 0.0) break; x = xm; } dx = x; /* Do Newton interation until x converges to two * decimal places. */ while (fabs(dx/x) > 0.005) { ff = pt[0]; df = ff; for (i=1;i void RPoly::fxshfr(int l2,int *nz) { T svu,svv,ui,vi,s; T betas,betav,oss,ovv,ss,vv,ts,tv; T ots,otv,tvv,tss; int type, i,j,iflag,vpass,spass,vtry,stry; *nz = 0; betav = 0.25; betas = 0.25; oss = sr; ovv = v; /* Evaluate polynomial by synthetic division. */ quadsd(n,&u,&v,p,qp,&a,&b); calcsc(&type); for (j=0;j 0) return; /* Quadratic iteration has failed. Flag that it has * been tried and decrease the convergence criterion. */ vtry = 1; betav *= 0.25; /* Try linear iteration if it has not been tried and * the S sequence is converging. */ if (stry || !spass) goto _50; for (i=0;i 0) return; /* Linear iteration has failed. Flag that it has been * tried and decrease the convergence criterion. */ stry = 1; betas *=0.25; if (iflag == 0) goto _50; /* If linear iteration signals an almost double real * zero attempt quadratic iteration. */ ui = -(s+s); vi = s*s; goto _20; /* Restore variables. */ _50: u = svu; v = svv; for (i=0;i void RPoly::quadit(T *uu,T *vv,int *nz) { T ui,vi; T mp,omp,ee,relstp,t,zm; int type,i,j,tried; *nz = 0; tried = 0; u = *uu; v = *vv; j = 0; /* Main loop. */ _10: quad(1.0,u,v,&szr,&szi,&lzr,&lzi); /* Return if roots of the quadratic are real and not * close to multiple or nearly equal and of opposite * sign. */ if (fabs(fabs(szr)-fabs(lzr)) > 0.01 * fabs(lzr)) return; /* Evaluate polynomial by quadratic synthetic division. */ quadsd(n,&u,&v,p,qp,&a,&b); mp = fabs(a-szr*b) + fabs(szi*b); /* Compute a rigorous bound on the rounding error in * evaluating p. */ zm = sqrt(fabs(v)); ee = (T) 2.0*fabs(qp[0]); t = -szr*b; for (i=1;i 20) return; if (j < 2) goto _50; if (relstp > 0.01 || mp < omp || tried) goto _50; /* A cluster appears to be stalling the convergence. * Five fixed shift steps are taken with a u,v close * to the cluster. */ if (relstp < eta) relstp = eta; relstp = sqrt(relstp); u = u - u*relstp; v = v + v*relstp; quadsd(n,&u,&v,p,qp,&a,&b); for (i=0;i<5;i++) { calcsc(&type); nextk(&type); } tried = 1; j = 0; _50: omp = mp; /* Calculate next k polynomial and new u and v. */ calcsc(&type); nextk(&type); calcsc(&type); newest(type,&ui,&vi); /* If vi is zero the iteration is not converging. */ if (vi == 0.0) return; relstp = fabs((vi-v)/vi); u = ui; v = vi; goto _10; } /* Variable-shift H polynomial iteration for a real zero. * sss - starting iterate * nz - number of zeros found * iflag - flag to indicate a pair of zeros near real axis. */ template void RPoly::realit(T *sss, int *nz, int *iflag) { T pv,kv,t,s; T ms,mp,omp,ee; int i,j; *nz = 0; s = *sss; *iflag = 0; j = 0; /* Main loop */ while (1) { pv = p[0]; /* Evaluate p at s. */ qp[0] = pv; for (i=1;i<=n;i++) { pv = pv*s + p[i]; qp[i] = pv; } mp = fabs(pv); /* Compute a rigorous bound on the error in evaluating p. */ ms = fabs(s); ee = (mre/(are+mre))*fabs(qp[0]); for (i=1;i<=n;i++) { ee = ee*ms + fabs(qp[i]); } /* Iteration has converged sufficiently if the polynomial * value is less than 20 times this bound. */ if (mp <= 20.0*((are+mre)*ee-mre*mp)) { *nz = 1; szr = s; szi = 0.0; return; } j++; /* Stop iteration after 10 steps. */ if (j > 10) return; if (j < 2) goto _50; if (fabs(t) > 0.001*fabs(s-t) || mp < omp) goto _50; /* A cluster of zeros near the real axis has been * encountered. Return with iflag set to initiate a * quadratic iteration. */ *iflag = 1; *sss = s; return; /* Return if the polynomial value has increased significantly. */ _50: omp = mp; /* Compute t, the next polynomial, and the new iterate. */ kv = k[0]; qk[0] = kv; for (i=1;i fabs(k[n-1]*10.0*eta)) t = -pv/kv; s += t; } } /* This routine calculates scalar quantities used to * compute the next k polynomial and new estimates of * the quadratic coefficients. * type - integer variable set here indicating how the * calculations are normalized to avoid overflow. */ template void RPoly::calcsc(int *type) { /* Synthetic division of k by the quadratic 1,u,v */ quadsd(n-1,&u,&v,k,qk,&c,&d); if (fabs(c) > fabs(k[n-1]*100.0*eta)) goto _10; if (fabs(d) > fabs(k[n-2]*100.0*eta)) goto _10; *type = 3; /* Type=3 indicates the quadratic is almost a factor of k. */ return; _10: if (fabs(d) < fabs(c)) { *type = 1; /* Type=1 indicates that all formulas are divided by c. */ e = a/c; f = d/c; g = u*e; h = v*b; a3 = a*e + (h/c+g)*b; a1 = b - a*(d/c); a7 = a + g*d + h*f; return; } *type = 2; /* Type=2 indicates that all formulas are divided by d. */ e = a/d; f = c/d; g = u*b; h = v*b; a3 = (a+g)*e + h*(b/d); a1 = b*f-a; a7 = (f+u)*a + h; } /* Computes the next k polynomials using scalars * computed in calcsc. */ template void RPoly::nextk(int *type) { T temp; int i; if (*type == 3) { /* Use unscaled form of the recurrence if type is 3. */ k[0] = 0.0; k[1] = 0.0; for (i=2;i void RPoly::newest(int type,T *uu,T *vv) { T a4,a5,b1,b2,c1,c2,c3,c4,temp; /* Use formulas appropriate to setting of type. */ if (type == 3) { /* If type=3 the quadratic is zeroed. */ *uu = 0.0; *vv = 0.0; return; } if (type == 2) { a4 = (a+g)*f + h; a5 = (f+u)*c + v*d; } else { a4 = a + u*b +h*f; a5 = c + (u+v*f)*d; } /* Evaluate new quadratic coefficients. */ b1 = -k[n-1]/p[n]; b2 = -(k[n-2]+b1*p[n-1])/p[n]; c1 = v*b2*a1; c2 = b1*a7; c3 = b1*b1*a3; c4 = c1 - c2 - c3; temp = a5 + b1*a4 - c4; if (temp == 0.0) { *uu = 0.0; *vv = 0.0; return; } *uu = u - (u*(c3+c2)+v*(b1*a1+b2*a7))/temp; *vv = v*((T)1.0+c4/temp); return; } /* Divides p by the quadratic 1,u,v placing the quotient * in q and the remainder in a,b. */ template void RPoly::quadsd(int nn,T *u,T *v,T *p,T *q, T *a,T *b) { T c; int i; *b = p[0]; q[0] = *b; *a = p[1] - (*b)*(*u); q[1] = *a; for (i=2;i<=nn;i++) { c = p[i] - (*a)*(*u) - (*b)*(*v); q[i] = c; *b = *a; *a = c; } } /* Calculate the zeros of the quadratic a*z^2 + b1*z + c. * The quadratic formula, modified to avoid overflow, is used * to find the larger zero if the zeros are real and both * are complex. The smaller real zero is found directly from * the product of the zeros c/a. */ template void RPoly::quad(T a,T b1,T c,T *sr,T *si, T *lr,T *li) { T b,d,e; if (a == 0.0) { /* less than two roots */ if (b1 != 0.0) *sr = -c/b1; else *sr = 0.0; *lr = 0.0; *si = 0.0; *li = 0.0; return; } if (c == 0.0) { /* one real root, one zero root */ *sr = 0.0; *lr = -b1/a; *si = 0.0; *li = 0.0; return; } /* Compute discriminant avoiding overflow. */ b = b1/(T) 2.0; if (fabs(b) < fabs(c)) { if (c < 0.0) e = -a; else e = a; e = b*(b/fabs(c)) - e; d = sqrt(fabs(e))*sqrt(fabs(c)); } else { e = (T) 1.0 - (a/b)*(c/b); d = sqrt(fabs(e))*fabs(b); } if (e < 0.0) { /* complex conjugate zeros */ *sr = -b/a; *lr = *sr; *si = fabs(d/a); *li = -(*si); } else { if (b >= 0.0) /* real zeros. */ d = -d; *lr = (-b+d)/a; *sr = 0.0; if (*lr != 0.0) *sr = (c/ *lr)/a; *si = 0.0; *li = 0.0; } } template class RPoly; template class RPoly; template class RPoly; } // namespace SimTK