eeab41af4e
- versione x64 compilata con Clang-cl/LLVM - modifiche varie per eliminare warning più gravi di questo compilatore.
1400 lines
41 KiB
C++
1400 lines
41 KiB
C++
//----------------------------------------------------------------------------
|
|
// EgalTech 2013-2014
|
|
//----------------------------------------------------------------------------
|
|
// File : JenkinsTraub.cpp Data : 08.01.14 Versione : 1.5a1
|
|
// Contenuto : Implementazione calcolo degli zeri di polinomi a coefficienti
|
|
// reali o complessi con il metodo di Jenkins e Traub.
|
|
// Rpoly deriva da TOMS493. Cpoly deriva da TOMS419.
|
|
//
|
|
//
|
|
// Modifiche : 08.01.14 DS Creazione modulo.
|
|
//
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
//--------------------------- Include ----------------------------------------
|
|
#include "stdafx.h"
|
|
#include "JenkinsTraub.h"
|
|
|
|
//--------------------------- Constants --------------------------------------
|
|
#define RADIX_DOUBLE 2
|
|
#define EPS_DOUBLE 2.22e-16
|
|
#define SMALL_DOUBLE 2.3e-308
|
|
#define BIG_DOUBLE 1.7e+308
|
|
|
|
|
|
//--------------------------- Class Rpoly --------------------------------------
|
|
//------------------------------------------------------------------------------
|
|
// IN: op - double precision vector of coefficients in order of decreasing powers.
|
|
// degree - integer degree of polynomial
|
|
// OUT: zeror,zeroi - output double precision vectors of the real and imaginary parts of the zeros.
|
|
// RET: -1 if leading coefficient is zero, otherwise number of roots found.
|
|
//------------------------------------------------------------------------------
|
|
int
|
|
Rpoly::Calculate( const double* op, int degree, double* zeror, double* zeroi)
|
|
{
|
|
bool bZerOk ;
|
|
bool bScale ;
|
|
bool bContinue ;
|
|
int cnt, nz, i, j, jj, l, nm1 ;
|
|
double t, aa, bb, cc, factor, rot ;
|
|
double lo, max, min, xx, yy, cosr, sinr, xxx, x, sc, bnd ;
|
|
double xm, ff, df, dx, infin, smalno, base ;
|
|
double temp[POLY_MAXDEG+1] ;
|
|
double pt[POLY_MAXDEG+1] ;
|
|
|
|
|
|
// The following statements set machine constants.
|
|
base = RADIX_DOUBLE ;
|
|
eta = EPS_DOUBLE ;
|
|
infin = BIG_DOUBLE ;
|
|
smalno = SMALL_DOUBLE ;
|
|
are = eta ;
|
|
mre = eta ;
|
|
lo = smalno / eta ;
|
|
|
|
// Initialization of constants for shift rotation.
|
|
xx = sqrt( 0.5) ;
|
|
yy = - xx ;
|
|
rot = 94.0 ;
|
|
rot *= 0.017453293 ;
|
|
cosr = cos( rot) ;
|
|
sinr = sin( rot) ;
|
|
n = degree ;
|
|
|
|
// Inizializzo numero iterazioni
|
|
itercnt = 0 ;
|
|
|
|
// Algorithm fails if 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 degree ;
|
|
|
|
// Make a copy of the coefficients.
|
|
for ( i = 0 ; i <= n ; i++)
|
|
p[i] = op[i] ;
|
|
|
|
// Start the algorithm for one zero.
|
|
bContinue = true ;
|
|
while ( bContinue) {
|
|
|
|
// Calculate the final zero
|
|
if ( n == 1) {
|
|
zeror[degree-1] = - p[1] / p[0] ;
|
|
zeroi[degree-1] = 0.0 ;
|
|
n -= 1 ;
|
|
return ( degree - n) ;
|
|
}
|
|
|
|
// Calculate a 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 ;
|
|
return ( degree - n) ;
|
|
}
|
|
|
|
// Find largest and smallest moduli of coefficients.
|
|
max = 0.0 ;
|
|
min = infin ;
|
|
for ( i = 0 ; i <= n ; i++) {
|
|
x = abs( 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.
|
|
bScale = true ;
|
|
sc = lo / min ;
|
|
if ( sc > 1.0 && ( infin / sc) < max)
|
|
bScale = false ;
|
|
if ( sc <= 1.0) {
|
|
if ( max < 10.0)
|
|
bScale = false ;
|
|
if ( sc == 0.0)
|
|
sc = smalno ;
|
|
}
|
|
if ( bScale) {
|
|
// Scale polynomial.
|
|
l = (int)( log( sc) / log( base) + 0.5) ;
|
|
factor = pow( base * 1.0, l) ;
|
|
if ( factor != 1.0) {
|
|
for ( i = 0 ; i <= n ; i ++)
|
|
p[i] = factor * p[i] ;
|
|
}
|
|
}
|
|
|
|
// Compute lower bound on moduli of roots.
|
|
for ( i = 0 ; i <= n ; i++) {
|
|
pt[i] = abs( p[i]) ;
|
|
}
|
|
pt[n] = - pt[n] ;
|
|
// Compute upper estimate of bound.
|
|
x = exp( ( log( - pt[n]) - log( pt[0])) / (double) 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 ( true) {
|
|
xm = x * 0.1 ;
|
|
ff = pt[0] ;
|
|
for ( i = 1 ; i <= n ; i ++)
|
|
ff = ff * xm + pt[i] ;
|
|
if ( ff <= 0.0)
|
|
break ;
|
|
x = xm ;
|
|
}
|
|
|
|
// Do Newton iteration until x converges to two decimal places.
|
|
dx = x ;
|
|
while ( abs( dx / x) > 0.005) {
|
|
ff = pt[0] ;
|
|
df = ff ;
|
|
for ( i = 1 ; i < n ; i++) {
|
|
ff = ff * x + pt[i] ;
|
|
df = df * x + ff ;
|
|
}
|
|
ff = ff * x + pt[n] ;
|
|
dx = ff / df ;
|
|
x -= dx ;
|
|
itercnt ++ ;
|
|
}
|
|
bnd = x ;
|
|
|
|
// Compute the derivative as the initial k polynomial and do 5 steps with no shift.
|
|
nm1 = n - 1 ;
|
|
for ( i = 1 ; i < n ; i++)
|
|
k[i] = (double)( n - i) * p[i] / (double) n ;
|
|
k[0] = p[0] ;
|
|
aa = p[n] ;
|
|
bb = p[n-1] ;
|
|
bZerOk = ( k[n-1] == 0) ;
|
|
for ( jj = 0 ; jj < 5 ; jj ++) {
|
|
itercnt ++ ;
|
|
cc = k[n-1] ;
|
|
// Use a scaled form of recurrence if value of k at 0 is nonzero.
|
|
if ( ! bZerOk) {
|
|
t = - aa / cc ;
|
|
for ( i = 0 ; i < nm1 ; i ++) {
|
|
j = n - i - 1 ;
|
|
k[j] = t * k[j-1] + p[j] ;
|
|
}
|
|
k[0] = p[0] ;
|
|
bZerOk = ( abs( k[n-1]) <= abs( bb) * eta * 10.0) ;
|
|
}
|
|
else {
|
|
// Use unscaled form of recurrence.
|
|
for ( i = 0 ; i < nm1 ; i ++) {
|
|
j = n - i - 1 ;
|
|
k[j] = k[j-1] ;
|
|
}
|
|
k[0] = 0.0 ;
|
|
bZerOk = ( k[n-1] == 0.0) ;
|
|
}
|
|
}
|
|
|
|
// Save k for restarts with new shifts.
|
|
for ( i = 0 ; i < n ; i ++)
|
|
temp[i] = k[i] ;
|
|
|
|
// Loop to select the quadratic corresponding to each new shift.
|
|
bContinue = false ;
|
|
for ( cnt = 0 ; cnt < 20 ; cnt ++) {
|
|
// Quadratic corresponds to a double shift to a non-real point and its complex conjugate.
|
|
// The point has modulus bnd and amplitude rotated by 94 degrees from the previous shift.
|
|
xxx = cosr * xx - sinr * yy ;
|
|
yy = sinr * xx + cosr * yy ;
|
|
xx = xxx ;
|
|
sr = bnd * xx ;
|
|
si = bnd * yy ;
|
|
u = -2.0 * sr ;
|
|
v = bnd ;
|
|
fxshfr( 20 * ( cnt + 1), &nz);
|
|
// The second stage jumps directly to one of the third stage iterations and returns here if successful.
|
|
// Deflate the polynomial, store the zero or zeros and return to the main algorithm.
|
|
if ( nz != 0) {
|
|
j = degree - n ;
|
|
zeror[j] = szr ;
|
|
zeroi[j] = szi ;
|
|
n -= nz ;
|
|
for ( i = 0 ; i <= n ; i ++)
|
|
p[i] = qp[i] ;
|
|
if ( nz != 1) {
|
|
zeror[j+1] = lzr ;
|
|
zeroi[j+1] = lzi ;
|
|
}
|
|
// interrompo il loop e riparto a cercare un nuovo zero
|
|
bContinue = true ;
|
|
break ;
|
|
}
|
|
// If the iteration is unsuccessful another quadratic is chosen after restoring k.
|
|
else {
|
|
for ( i = 0 ; i < n ; i ++)
|
|
k[i] = temp[i] ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return with failure if no convergence after 20 shifts.
|
|
return ( degree - n) ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Computes up to L2 fixed shift k-polynomials,
|
|
// testing for convergence in the linear or quadratic
|
|
// case. Initiates one of the variable shift
|
|
// iterations and returns with the number of zeros found.
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
Rpoly::fxshfr( int l2, int* nz)
|
|
{
|
|
bool bVpass ;
|
|
bool bSpass ;
|
|
bool bVtry ;
|
|
bool bStry ;
|
|
bool bFflag ;
|
|
bool bIflag ;
|
|
int type, i, j ;
|
|
double svu, svv, ui, vi, s ;
|
|
double betas, betav, oss, ovv, ss, vv, ts, tv ;
|
|
double ots, otv, tvv, tss ;
|
|
double svk[POLY_MAXDEG+1] ;
|
|
|
|
|
|
// Inizializzazioni
|
|
bIflag = true ;
|
|
*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 < l2 ; j ++) {
|
|
|
|
bFflag = true ;
|
|
|
|
// Calculate next k polynomial and estimate v.
|
|
nextk( &type) ;
|
|
calcsc( &type) ;
|
|
newest( type, &ui, &vi) ;
|
|
vv = vi ;
|
|
|
|
// Estimate s.
|
|
ss = 0.0 ;
|
|
if ( k[n-1] != 0.0)
|
|
ss = - p[n] / k[n-1] ;
|
|
tv = 1.0 ;
|
|
ts = 1.0 ;
|
|
if ( j == 0 || type == 3) {
|
|
ovv = vv ;
|
|
oss = ss ;
|
|
otv = tv ;
|
|
ots = ts ;
|
|
continue ;
|
|
}
|
|
|
|
// Compute relative measures of convergence of s and v sequences.
|
|
if ( vv != 0.0)
|
|
tv = abs( ( vv - ovv) / vv) ;
|
|
if ( ss != 0.0)
|
|
ts = abs( ( ss - oss) / ss) ;
|
|
/* If decreasing, multiply two most recent convergence measures. */
|
|
tvv = 1.0 ;
|
|
if ( tv < otv)
|
|
tvv = tv * otv ;
|
|
tss = 1.0 ;
|
|
if ( ts < ots)
|
|
tss = ts * ots ;
|
|
// Compare with convergence criteria.
|
|
bVpass = ( tvv < betav) ;
|
|
bSpass = ( tss < betas) ;
|
|
if ( ! ( bSpass || bVpass)) {
|
|
ovv = vv ;
|
|
oss = ss ;
|
|
otv = tv ;
|
|
ots = ts ;
|
|
continue ;
|
|
}
|
|
|
|
// At least one sequence has passed the convergence test. Store variables before iterating.
|
|
svu = u;
|
|
svv = v;
|
|
for ( i = 0 ; i < n ; i ++)
|
|
svk[i] = k[i] ;
|
|
s = ss ;
|
|
|
|
// Choose iteration according to the fastest converging sequence.
|
|
bVtry = false ;
|
|
bStry = false ;
|
|
while ( true) {
|
|
if ( bFflag && bSpass && ( ! bVpass || tss < tvv))
|
|
;
|
|
else {
|
|
quadit( &ui, &vi, nz) ;
|
|
if ( *nz > 0)
|
|
return ;
|
|
// Quadratic iteration has failed. Flag that it has been tried and decrease the convergence criterion.
|
|
bIflag = true ;
|
|
bVtry = true ;
|
|
betav *= 0.25;
|
|
// Try linear iteration if it has not been tried and the S sequence is converging.
|
|
if ( bStry || ! bSpass)
|
|
bIflag = false ;
|
|
else {
|
|
for ( i = 0 ; i < n ; i ++)
|
|
k[i] = svk[i] ;
|
|
}
|
|
}
|
|
bFflag = false ;
|
|
if ( bIflag) {
|
|
realit( s, nz, &bIflag) ;
|
|
if ( *nz > 0)
|
|
return ;
|
|
// Linear iteration has failed. Flag that it has been tried and decrease the convergence criterion.
|
|
bStry = true ;
|
|
betas *= 0.25 ;
|
|
// If linear iteration signals an almost double real zero attempt quadratic iteration.
|
|
if ( bIflag) {
|
|
ui = -( s + s) ;
|
|
vi = s * s ;
|
|
break ;
|
|
}
|
|
}
|
|
// Restore variables
|
|
u = svu;
|
|
v = svv;
|
|
for ( i = 0 ; i < n ; i ++) {
|
|
k[i] = svk[i] ;
|
|
}
|
|
// Try quadratic iteration if it has not been tried and the V sequence is convergin.
|
|
if ( ! bVpass || bVtry)
|
|
break ;
|
|
}
|
|
|
|
// Recompute QP and scalar values to continue the second stage.
|
|
quadsd( n, &u, &v, p, qp, &a, &b) ;
|
|
calcsc( &type) ;
|
|
|
|
// Salvo valori come precedenti
|
|
ovv = vv ;
|
|
oss = ss ;
|
|
otv = tv ;
|
|
ots = ts ;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Variable-shift k-polynomial iteration for a
|
|
// quadratic factor converges only if the zeros are
|
|
// equimodular or nearly so.
|
|
// uu, vv - coefficients of starting quadratic.
|
|
// nz - number of zeros found.
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
Rpoly::quadit( double *uu, double *vv, int *nz)
|
|
{
|
|
bool bTried ;
|
|
int type, i, j ;
|
|
double ui, vi ;
|
|
double mp, omp, ee, relstp, t, zm ;
|
|
|
|
|
|
// Inizializzazioni
|
|
*nz = 0 ;
|
|
bTried = false ;
|
|
u = *uu ;
|
|
v = *vv ;
|
|
j = 0 ;
|
|
|
|
// Main loop.
|
|
while ( true) {
|
|
itercnt ++ ;
|
|
|
|
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 ( abs( abs( szr) - abs( lzr)) > 0.01 * abs( lzr))
|
|
return ;
|
|
|
|
// Evaluate polynomial by quadratic synthetic division.
|
|
quadsd( n, &u, &v, p, qp, &a, &b) ;
|
|
mp = abs( a - szr * b) + abs( szi * b) ;
|
|
// Compute a rigorous bound on the rounding error in evaluating p.
|
|
zm = sqrt( abs( v)) ;
|
|
ee = 2.0 * abs( qp[0]) ;
|
|
t = -szr * b ;
|
|
for ( i = 1 ; i < n ; i ++) {
|
|
ee = ee * zm + abs( qp[i]) ;
|
|
}
|
|
ee = ee * zm + abs( a + t) ;
|
|
ee *= (5.0 * mre + 4.0 * are) ;
|
|
ee = ee - ( 5.0 * mre + 2.0 * are) * ( abs( a + t) + abs( b) * zm) ;
|
|
ee = ee + 2.0 * are * abs( t) ;
|
|
// Iteration has converged sufficiently if the polynomial value is less than 20 times this bound.
|
|
if ( mp <= 20.0 * ee) {
|
|
*nz = 2 ;
|
|
return ;
|
|
}
|
|
j ++ ;
|
|
|
|
// Stop iteration after 20 steps.
|
|
if ( j > 20)
|
|
return ;
|
|
|
|
// A cluster appears to be stalling the convergence.
|
|
// Five fixed shift steps are taken with a u,v close to the cluster.
|
|
if ( j >= 2 &&
|
|
! ( relstp > 0.01 || mp < omp || bTried)) {
|
|
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) ;
|
|
}
|
|
bTried = true ;
|
|
j = 0 ;
|
|
}
|
|
|
|
// Salvo valore
|
|
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 = abs( ( vi - v) / vi) ;
|
|
u = ui ;
|
|
v = vi ;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// 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.
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
Rpoly::realit( double sss, int* nz, bool* pbIflag)
|
|
{
|
|
int i, j ;
|
|
double pv, kv, t, s ;
|
|
double ms, mp, omp, ee ;
|
|
|
|
|
|
// Inizializzazioni
|
|
*nz = 0 ;
|
|
s = sss ;
|
|
*pbIflag = false ;
|
|
j = 0 ;
|
|
|
|
// Main loop
|
|
while ( true) {
|
|
itercnt ++ ;
|
|
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 = abs( pv) ;
|
|
// Compute a rigorous bound on the error in evaluating p.
|
|
ms = abs( s) ;
|
|
ee = ( mre / ( are + mre)) * abs( qp[0]) ;
|
|
for ( i = 1 ; i <= n ; i ++) {
|
|
ee = ee * ms + abs( 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 ;
|
|
// A cluster of zeros near the real axis has been encountered.
|
|
if ( j >= 2 &&
|
|
! ( abs( t) > 0.001 * abs( s-t) || mp < omp)) {
|
|
// Return with iflag set to initiate a quadratic iteration.
|
|
*pbIflag = true ;
|
|
return ;
|
|
}
|
|
|
|
// Return if the polynomial value has increased significantly.
|
|
omp = mp ;
|
|
|
|
// Compute t, the next polynomial, and the new iterate.
|
|
kv = k[0] ;
|
|
qk[0] = kv ;
|
|
for ( i = 1 ; i < n ; i ++) {
|
|
kv = kv*s + k[i] ;
|
|
qk[i] = kv;
|
|
}
|
|
if ( abs( kv) <= abs( k[n-1]) * 10.0 * eta) {
|
|
// Use unscaled form.
|
|
k[0] = 0.0 ;
|
|
for ( i = 1 ; i < n ; i ++) {
|
|
k[i] = qk[i-1] ;
|
|
}
|
|
}
|
|
else {
|
|
// Use the scaled form of the recurrence if the value of k at s is nonzero.
|
|
t = - pv / kv ;
|
|
k[0] = qp[0] ;
|
|
for ( i = 1 ; i < n ; i++) {
|
|
k[i] = t * qk[i-1] + qp[i] ;
|
|
}
|
|
}
|
|
kv = k[0] ;
|
|
for ( i = 1 ; i < n ; i ++) {
|
|
kv = kv * s + k[i] ;
|
|
}
|
|
t = 0.0 ;
|
|
if ( abs( kv) > ( abs( 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.
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
Rpoly::calcsc( int *type)
|
|
{
|
|
// Synthetic division of k by the quadratic 1,u,v
|
|
quadsd( n-1, &u, &v, k, qk, &c, &d) ;
|
|
|
|
// Type=3 indicates the quadratic is almost a factor of k.
|
|
if ( abs( c) <= abs( k[n-1] * 100.0 * eta) &&
|
|
abs( d) <= abs( k[n-2] * 100.0 * eta)) {
|
|
*type = 3 ;
|
|
return ;
|
|
}
|
|
|
|
// Type=1 indicates that all formulas are divided by c.
|
|
if ( abs( d) < abs( c)) {
|
|
*type = 1 ;
|
|
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 indicates that all formulas are divided by d.
|
|
*type = 2 ;
|
|
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.
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
Rpoly::nextk( int* type)
|
|
{
|
|
double 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 < n ; i ++) {
|
|
k[i] = qk[i-2] ;
|
|
}
|
|
return ;
|
|
}
|
|
temp = a ;
|
|
if ( *type == 1)
|
|
temp = b ;
|
|
if ( abs( a1) <= abs( temp) * eta * 10.0) {
|
|
// If a1 is nearly zero then use a special form of the recurrence.
|
|
k[0] = 0.0;
|
|
k[1] = -a7*qp[0] ;
|
|
for ( i = 2 ; i < n ; i ++) {
|
|
k[i] = a3 * qk[i-2] - a7 * qp[i-1] ;
|
|
}
|
|
return ; // HVE return added
|
|
}
|
|
/* Use scaled form of the recurrence. */
|
|
a7 /= a1 ;
|
|
a3 /= a1 ;
|
|
k[0] = qp[0] ;
|
|
k[1] = qp[1] - a7 * qp[0] ;
|
|
for ( i = 2 ; i < n ; i ++) {
|
|
k[i] = a3 * qk[i-2] - a7 * qp[i-1] + qp[i] ;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Compute new estimates of the quadratic coefficients using the scalars computed in calcsc.
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
Rpoly::newest( int type, double *uu, double *vv)
|
|
{
|
|
double 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 * ( 1.0 + c4 / temp) ;
|
|
return ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Divides p by the quadratic 1,u,v placing the quotient in q and the remainder in a,b.
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
Rpoly::quadsd( int nn, double *u, double *v, double *p, double *q,
|
|
double *a, double *b)
|
|
{
|
|
int i ;
|
|
double c ;
|
|
|
|
|
|
*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.
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
Rpoly::quad( double a, double b1, double c,
|
|
double* sr, double* si, double* lr, double* li)
|
|
{
|
|
double 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 / 2.0 ;
|
|
if ( abs( b) < abs( c)) {
|
|
if ( c < 0.0)
|
|
e = - a ;
|
|
else
|
|
e = a ;
|
|
e = b * ( b / abs( c)) - e ;
|
|
d = sqrt( abs( e)) * sqrt( abs( c)) ;
|
|
}
|
|
else {
|
|
e = 1.0 - ( a / b) *( c / b) ;
|
|
d = sqrt( abs( e)) * abs( b) ;
|
|
}
|
|
if ( e < 0.0) { /* complex conjugate zeros */
|
|
*sr = - b / a ;
|
|
*lr = *sr ;
|
|
*si = abs( d / a) ;
|
|
*li = - ( *si) ;
|
|
}
|
|
else {
|
|
if ( b >= 0.0)
|
|
d = - d ; /* real zeros. */
|
|
*lr = ( - b + d) / a ;
|
|
*sr = 0.0 ;
|
|
if ( *lr != 0.0)
|
|
*sr = ( c / *lr) / a ;
|
|
*si = 0.0 ;
|
|
*li = 0.0 ;
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------- Class Cpoly --------------------------------------
|
|
//------------------------------------------------------------------------------
|
|
// IN: opr, opi - double precision vector of real and imaginary coefficients in order of decreasing powers.
|
|
// degree - integer degree of polynomial
|
|
// OUT: zeror,zeroi - output double precision vectors of the real and imaginary parts of the zeros.
|
|
// RET: -1 if leading coefficient is zero, otherwise number of roots found.
|
|
//------------------------------------------------------------------------------
|
|
int
|
|
Cpoly::Calculate( const double* opr, const double* opi, int degree, double* zeror, double* zeroi)
|
|
{
|
|
bool bContinue ;
|
|
bool bConv ;
|
|
int cnt1, cnt2, idnn2, i ;
|
|
double xx, yy, cosr, sinr, smalno, base, xxx, zr, zi, bnd ;
|
|
|
|
|
|
// The following statements set machine constants.
|
|
mcon( &eta, &infin, &smalno, &base) ;
|
|
are = eta ;
|
|
mre = 2.0 * sqrt( 2.0 ) * eta ;
|
|
|
|
// Initialization of constants for shift rotation.
|
|
xx = 0.70710678 ;
|
|
yy = -xx ;
|
|
cosr = -0.060756474 ;
|
|
sinr = -0.99756405 ;
|
|
nn = degree ;
|
|
|
|
// Inizializzo numero iterazioni
|
|
itercnt = 0 ;
|
|
|
|
// Algorithm fails if the leading coefficient is zero
|
|
if ( opr[0] == 0 && opi[0] == 0)
|
|
return - 1 ;
|
|
|
|
// Remove the zeros at the origin if any
|
|
while ( opr[nn] == 0 && opi[nn] == 0) {
|
|
idnn2 = degree - nn ;
|
|
zeror[idnn2] = 0 ;
|
|
zeroi[idnn2] = 0 ;
|
|
nn -- ;
|
|
}
|
|
|
|
// Make a copy of the coefficients
|
|
for ( i = 0 ; i <= nn ; i++) {
|
|
pr[i] = opr[i] ;
|
|
pi[i] = opi[i] ;
|
|
shr[i] = cmod( pr[i], pi[i]) ;
|
|
}
|
|
|
|
// Scale the polynomial
|
|
bnd = scale( nn, shr, eta, infin, smalno, base) ;
|
|
if ( bnd != 1)
|
|
for ( i = 0 ; i <= nn ; i++) {
|
|
pr[i] *= bnd ;
|
|
pi[i] *= bnd ;
|
|
}
|
|
|
|
// Main loop
|
|
bContinue = true ;
|
|
while ( bContinue) {
|
|
|
|
if ( nn <= 1) {
|
|
cdivid( -pr[1], -pi[1], pr[0], pi[0], &zeror[degree-1], &zeroi[degree-1]) ;
|
|
return degree ;
|
|
}
|
|
|
|
// Calculate bnd, alower bound on the modulus of the zeros
|
|
for ( i = 0 ; i <= nn ; i++)
|
|
shr[i] = cmod( pr[i], pi[i]) ;
|
|
|
|
cauchy( nn, shr, shi, &bnd) ;
|
|
|
|
// Outer loop to control 2 Major passes with different sequences of shifts
|
|
bContinue = false ;
|
|
for ( cnt1 = 1 ; cnt1 <= 2 && ! bContinue ; cnt1++) {
|
|
// First stage calculation , no shift
|
|
noshft( 5) ;
|
|
|
|
// Inner loop to select a shift
|
|
for ( cnt2 = 1 ; cnt2 <= 9 && ! bContinue ; cnt2++) {
|
|
// Shift is chosen with modulus bnd and amplitude rotated by 94 degree from the previous shif
|
|
xxx = cosr * xx - sinr * yy ;
|
|
yy = sinr * xx + cosr * yy ;
|
|
xx = xxx ;
|
|
sr = bnd * xx ;
|
|
si = bnd * yy ;
|
|
|
|
// Second stage calculation, fixed shift
|
|
fxshft( 10 * cnt2, &zr, &zi, &bConv) ;
|
|
if ( bConv) {
|
|
// The second stage jumps directly to the third stage ieration
|
|
// If successful the zero is stored and the polynomial deflated
|
|
idnn2 = degree - nn ;
|
|
zeror[idnn2] = zr ;
|
|
zeroi[idnn2] = zi ;
|
|
nn -- ;
|
|
for ( i = 0 ; i <= nn ; i++) {
|
|
pr[i] = qpr[i] ;
|
|
pi[i] = qpi[i] ;
|
|
}
|
|
bContinue = true ;
|
|
}
|
|
// If the iteration is unsuccessful another shift is chosen
|
|
}
|
|
// if 9 shifts fail, the outer loop is repeated with another sequence of shifts
|
|
}
|
|
}
|
|
|
|
// The zerofinder has failed on two major passes
|
|
// return empty handed with the number of roots found (less than the original degree)
|
|
degree -= nn ;
|
|
|
|
return degree ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// COMPUTES THE DERIVATIVE POLYNOMIAL AS THE INITIAL H
|
|
// POLYNOMIAL AND COMPUTES L1 NO-SHIFT H POLYNOMIALS.
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
Cpoly::noshft( const int l1)
|
|
{
|
|
int i, j, jj, n, nm1 ;
|
|
double xni, t1, t2 ;
|
|
|
|
|
|
n = nn ;
|
|
nm1 = n - 1 ;
|
|
for ( i = 0 ; i < n ; i++) {
|
|
xni = nn - i ;
|
|
hr[i] = xni * pr[i] / n ;
|
|
hi[i] = xni * pi[i] / n ;
|
|
}
|
|
for ( jj = 1 ; jj <= l1 ; jj++) {
|
|
itercnt ++ ;
|
|
if ( cmod( hr[n - 1], hi[n - 1]) > eta * 10 * cmod( pr[n - 1], pi[n - 1])) {
|
|
cdivid( -pr[nn], -pi[nn], hr[n - 1], hi[n - 1], &tr, &ti) ;
|
|
for ( i = 0 ; i < nm1 ; i++) {
|
|
j = nn - i - 1 ;
|
|
t1 = hr[j - 1] ;
|
|
t2 = hi[j - 1] ;
|
|
hr[j] = tr * t1 - ti * t2 + pr[j] ;
|
|
hi[j] = tr * t2 + ti * t1 + pi[j] ;
|
|
}
|
|
hr[0] = pr[0] ;
|
|
hi[0] = pi[0] ;
|
|
}
|
|
else {
|
|
// If the constant term is essentially zero, shift H coefficients
|
|
for ( i = 0 ; i < nm1 ; i++) {
|
|
j = nn - i - 1 ;
|
|
hr[j] = hr[j - 1] ;
|
|
hi[j] = hi[j - 1] ;
|
|
}
|
|
hr[0] = 0 ;
|
|
hi[0] = 0 ;
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// COMPUTES L2 FIXED-SHIFT H POLYNOMIALS AND TESTS FOR CONVERGENCE.
|
|
// INITIATES A VARIABLE-SHIFT ITERATION AND RETURNS WITH THE
|
|
// APPROXIMATE ZERO IF SUCCESSFUL.
|
|
// L2 - LIMIT OF FIXED SHIFT STEPS
|
|
// ZR,ZI - APPROXIMATE ZERO IF CONV IS .TRUE.
|
|
// CONV - LOGICAL INDICATING CONVERGENCE OF STAGE 3 ITERATION
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
Cpoly::fxshft( const int l2, double* zr, double* zi, bool* pbConv)
|
|
{
|
|
bool bBol ;
|
|
bool bPasd ;
|
|
bool bTest ;
|
|
int i, j, n ;
|
|
double otr, oti, svsr, svsi ;
|
|
|
|
|
|
n = nn ;
|
|
polyev( nn, sr, si, pr, pi, qpr, qpi, &pvr, &pvi) ;
|
|
bTest = true ;
|
|
bPasd = false ;
|
|
|
|
// Calculate first T = -P(S)/H(S)
|
|
calct( &bBol) ;
|
|
|
|
// Main loop for second stage
|
|
for ( j = 1 ; j <= l2 ; j++) {
|
|
itercnt ++ ;
|
|
|
|
otr = tr ;
|
|
oti = ti ;
|
|
|
|
// Compute the next H Polynomial and new t
|
|
nexth( bBol) ;
|
|
calct( &bBol) ;
|
|
*zr = sr + tr ;
|
|
*zi = si + ti ;
|
|
|
|
// Test for convergence unless stage 3 has failed once or this
|
|
// is the last H Polynomial
|
|
if ( ! ( bBol || ! bTest || j == 12)) {
|
|
if ( cmod( tr - otr, ti - oti) < 0.5 * cmod( *zr, *zi)) {
|
|
if ( bPasd) {
|
|
// The weak convergence test has been passwed twice, start the third stage
|
|
// Iteration, after saving the current H polynomial and shift
|
|
for ( i = 0; i < n; i++ ) {
|
|
shr[i] = hr[i] ;
|
|
shi[i] = hi[i] ;
|
|
}
|
|
svsr = sr ;
|
|
svsi = si ;
|
|
vrshft( 10, zr, zi, pbConv) ;
|
|
if ( *pbConv)
|
|
return ;
|
|
|
|
//The iteration failed to converge. Turn off testing and restore h,s,pv and T
|
|
bTest = false ;
|
|
for ( i = 0 ; i < n ; i++) {
|
|
hr[i] = shr[i] ;
|
|
hi[i] = shi[i] ;
|
|
}
|
|
sr = svsr ;
|
|
si = svsi ;
|
|
polyev( nn, sr, si, pr, pi, qpr, qpi, &pvr, &pvi) ;
|
|
calct( &bBol) ;
|
|
continue ;
|
|
}
|
|
bPasd = true ;
|
|
}
|
|
else
|
|
bPasd = false ;
|
|
}
|
|
}
|
|
|
|
// Attempt an iteration with final H polynomial from second stage
|
|
vrshft( 10, zr, zi, pbConv) ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// CARRIES OUT THE THIRD STAGE ITERATION.
|
|
// L3 - LIMIT OF STEPS IN STAGE 3.
|
|
// ZR,ZI - ON ENTRY CONTAINS THE INITIAL ITERATE, IF THE
|
|
// ITERATION CONVERGES IT CONTAINS THE FINAL ITERATE ON EXIT.
|
|
// CONV - .TRUE. IF ITERATION CONVERGES
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
Cpoly::vrshft( const int l3, double* zr, double* zi, bool* pbConv)
|
|
{
|
|
bool bBol ;
|
|
bool bFlag ;
|
|
bool bNext ;
|
|
int i, j ;
|
|
double mp, ms, omp, relstp, r1, r2, tp ;
|
|
|
|
|
|
*pbConv = false ;
|
|
bFlag = false ;
|
|
sr = *zr ;
|
|
si = *zi ;
|
|
|
|
// Main loop for stage three
|
|
for ( i = 1 ; i <= l3 ; i++) {
|
|
itercnt ++ ;
|
|
// Evaluate P at S and test for convergence
|
|
polyev( nn, sr, si, pr, pi, qpr, qpi, &pvr, &pvi) ;
|
|
mp = cmod( pvr, pvi) ;
|
|
ms = cmod( sr, si) ;
|
|
if ( mp <= 20 * errev( nn, qpr, qpi, ms, mp, are, mre)) {
|
|
// Polynomial value is smaller in value than a bound on the error
|
|
// in evaluationg P, terminate the iteration
|
|
*pbConv = true ;
|
|
*zr = sr ;
|
|
*zi = si ;
|
|
return ;
|
|
}
|
|
bNext = false ;
|
|
if ( i != 1) {
|
|
if ( ! ( bFlag || mp < omp || relstp >= 0.05)) {
|
|
// Iteration has stalled. Probably a cluster of zeros. Do 5 fixed
|
|
// shift steps into the cluster to force one zero to dominate
|
|
tp = relstp ;
|
|
bFlag = true ;
|
|
if ( relstp < eta)
|
|
tp = eta ;
|
|
r1 = sqrt( tp) ;
|
|
r2 = sr * ( 1 + r1 ) - si * r1 ;
|
|
si = sr * r1 + si * ( 1 + r1) ;
|
|
sr = r2 ;
|
|
polyev( nn, sr, si, pr, pi, qpr, qpi, &pvr, &pvi) ;
|
|
for ( j = 1 ; j <= 5 ; j++) {
|
|
calct( &bBol) ;
|
|
nexth( bBol) ;
|
|
}
|
|
omp = infin ;
|
|
bNext = true ;
|
|
}
|
|
|
|
// Exit if polynomial value increase significantly
|
|
if ( ! bNext && mp * 0.1 > omp)
|
|
return ;
|
|
}
|
|
|
|
// eventuale salvataggio dato
|
|
if ( ! bNext)
|
|
omp = mp ;
|
|
|
|
// Calculate next iterate
|
|
calct( &bBol) ;
|
|
nexth( bBol) ;
|
|
calct( &bBol) ;
|
|
if ( ! bBol) {
|
|
relstp = cmod( tr, ti) / cmod( sr, si) ;
|
|
sr += tr ;
|
|
si += ti ;
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// COMPUTES T = -P(S)/H(S).
|
|
// bool - LOGICAL, SET TRUE IF H(S) IS ESSENTIALLY ZERO.
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
Cpoly::calct( bool* pbBol)
|
|
{
|
|
int n ;
|
|
double hvr, hvi ;
|
|
|
|
|
|
n = nn ;
|
|
|
|
// evaluate h(s)
|
|
polyev( n - 1, sr, si, hr, hi, qhr, qhi, &hvr, &hvi) ;
|
|
*pbBol = ( cmod( hvr, hvi ) <= are * 10 * cmod( hr[n - 1], hi[n - 1])) ;
|
|
if ( ! *pbBol) {
|
|
cdivid( -pvr, -pvi, hvr, hvi, &tr, &ti) ;
|
|
return ;
|
|
}
|
|
|
|
tr = 0 ;
|
|
ti = 0 ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// CALCULATES THE NEXT SHIFTED H POLYNOMIAL.
|
|
// bool - LOGICAL, IF .TRUE. H(S) IS ESSENTIALLY ZERO
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
Cpoly::nexth( bool bBol)
|
|
{
|
|
int j, n ;
|
|
double t1, t2 ;
|
|
|
|
|
|
n = nn ;
|
|
if ( ! bBol) {
|
|
for ( j = 1 ; j < n ; j++) {
|
|
t1 = qhr[j - 1] ;
|
|
t2 = qhi[j - 1] ;
|
|
hr[j] = tr * t1 - ti * t2 + qpr[j] ;
|
|
hi[j] = tr * t2 + ti * t1 + qpi[j] ;
|
|
}
|
|
hr[0] = qpr[0] ;
|
|
hi[0] = qpi[0] ;
|
|
return ;
|
|
}
|
|
|
|
// If h[s] is zero replace H with qh
|
|
for ( j = 1 ; j < n ; j++) {
|
|
hr[j] = qhr[j - 1] ;
|
|
hi[j] = qhi[j - 1] ;
|
|
}
|
|
hr[0] = 0 ;
|
|
hi[0] = 0 ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// EVALUATES A POLYNOMIAL P AT S BY THE HORNER RECURRENCE
|
|
// PLACING THE PARTIAL SUMS IN Q AND THE COMPUTED VALUE IN PV.
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
Cpoly::polyev( const int nn, const double sr, const double si, const double pr[], const double pi[],
|
|
double qr[], double qi[], double *pvr, double *pvi )
|
|
{
|
|
int i ;
|
|
double t ;
|
|
|
|
|
|
qr[0] = pr[0] ;
|
|
qi[0] = pi[0] ;
|
|
*pvr = qr[0] ;
|
|
*pvi = qi[0] ;
|
|
|
|
for ( i = 1 ; i <= nn ; i++) {
|
|
t = ( *pvr) * sr - ( *pvi) * si + pr[i] ;
|
|
*pvi = ( *pvr) * si + ( *pvi) * sr + pi[i] ;
|
|
*pvr = t ;
|
|
qr[i] = *pvr ;
|
|
qi[i] = *pvi ;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// BOUNDS THE ERROR IN EVALUATING THE POLYNOMIAL BY THE HORNER RECURRENCE.
|
|
// QR,QI - THE PARTIAL SUMS
|
|
// MS -MODULUS OF THE POINT
|
|
// MP -MODULUS OF POLYNOMIAL VALUE
|
|
// ARE, MRE -ERROR BOUNDS ON COMPLEX ADDITION AND MULTIPLICATION
|
|
//------------------------------------------------------------------------------
|
|
double
|
|
Cpoly::errev( const int nn, const double qr[], const double qi[], const double ms, const double mp,
|
|
const double are, const double mre )
|
|
{
|
|
int i ;
|
|
double e ;
|
|
|
|
|
|
e = cmod( qr[0], qi[0]) * mre / ( are + mre) ;
|
|
for ( i = 0 ; i <= nn ; i++)
|
|
e = e * ms + cmod( qr[i], qi[i]) ;
|
|
|
|
return ( e * ( are + mre ) - mp * mre) ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// CAUCHY COMPUTES A LOWER BOUND ON THE MODULI OF THE ZEROS OF A
|
|
// POLYNOMIAL - PT IS THE MODULUS OF THE COEFFICIENTS.
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
Cpoly::cauchy( const int nn, double pt[], double q[], double* fn_val)
|
|
{
|
|
int i, n ;
|
|
double x, xm, f, dx, df ;
|
|
|
|
|
|
pt[nn] = -pt[nn] ;
|
|
|
|
// Compute upper estimate bound
|
|
n = nn ;
|
|
x = exp( ( log( - pt[n]) - log( pt[0])) / (double) n) ;
|
|
if ( pt[n - 1] != 0) {
|
|
// Newton step at the origin is better, use it
|
|
xm = -pt[nn] / pt[n - 1] ;
|
|
if ( xm < x)
|
|
x = xm ;
|
|
}
|
|
|
|
// Chop the interval (0,x) until f < 0
|
|
while ( true) {
|
|
xm = x * 0.1 ;
|
|
f = pt[0] ;
|
|
for ( i = 1 ; i <= nn ; i++)
|
|
f = f * xm + pt[i] ;
|
|
if ( f <= 0)
|
|
break ;
|
|
x = xm ;
|
|
}
|
|
dx = x ;
|
|
|
|
// Do Newton iteration until x converges to two decimal places
|
|
while ( abs( dx / x ) > 0.005) {
|
|
q[0] = pt[0] ;
|
|
for ( i = 1 ; i <= nn ; i++)
|
|
q[i] = q[i - 1] * x + pt[i] ;
|
|
f = q[nn] ;
|
|
df = q[0] ;
|
|
for ( i = 1 ; i < n ; i++)
|
|
df = df * x + q[i] ;
|
|
dx = f / df ;
|
|
x -= dx ;
|
|
itercnt ++ ;
|
|
}
|
|
|
|
*fn_val = x ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// RETURNS A SCALE FACTOR TO MULTIPLY THE COEFFICIENTS OF THE POLYNOMIAL.
|
|
// THE SCALING IS DONE TO AVOID OVERFLOW AND TO AVOID UNDETECTED UNDERFLOW
|
|
// INTERFERING WITH THE CONVERGENCE CRITERION. THE FACTOR IS A POWER OF THE BASE.
|
|
// PT - MODULUS OF COEFFICIENTS OF P
|
|
// ETA, INFIN, SMALNO, BASE - CONSTANTS DESCRIBING THE FLOATING POINT ARITHMETIC.
|
|
//------------------------------------------------------------------------------
|
|
double
|
|
Cpoly::scale( const int nn, const double pt[], const double eta,
|
|
const double infin, const double smalno, const double base)
|
|
{
|
|
int i, l ;
|
|
double hi, lo, max, min, x, sc ;
|
|
double fn_val ;
|
|
|
|
|
|
// Find largest and smallest moduli of coefficients
|
|
hi = sqrt( infin) ;
|
|
lo = smalno / eta ;
|
|
max = 0 ;
|
|
min = infin ;
|
|
|
|
for ( i = 0 ; i <= nn ; i++) {
|
|
x = pt[i] ;
|
|
if ( x > max)
|
|
max = x ;
|
|
if ( x != 0 && x < min)
|
|
min = x ;
|
|
}
|
|
|
|
// Scale only if there are very large or very small components
|
|
fn_val = 1 ;
|
|
if ( min >= lo && max <= hi)
|
|
return fn_val ;
|
|
x = lo / min ;
|
|
if ( x <= 1)
|
|
sc = 1 / ( sqrt( max)* sqrt( min)) ;
|
|
else {
|
|
sc = x;
|
|
if ( infin / sc > max)
|
|
sc = 1 ;
|
|
}
|
|
l = (int)( log( sc) / log( base) + 0.5) ;
|
|
fn_val = pow( base, l) ;
|
|
return fn_val ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// COMPLEX DIVISION C = A/B, AVOIDING OVERFLOW.
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
Cpoly::cdivid( const double ar, const double ai, const double br, const double bi, double* cr, double* ci)
|
|
{
|
|
double r, dinv, t, infin ;
|
|
|
|
|
|
if ( br == 0 && bi == 0) {
|
|
// Division by zero, c = infinity
|
|
mcon( &t, &infin, &t, &t) ;
|
|
*cr = infin ;
|
|
*ci = infin ;
|
|
return ;
|
|
}
|
|
|
|
if ( abs( br) < abs( bi)) {
|
|
r = br / bi ;
|
|
dinv = 1.0 / ( bi + r * br) ;
|
|
*cr = ( ar * r + ai) * dinv ;
|
|
*ci = ( ai * r - ar) * dinv ;
|
|
return ;
|
|
}
|
|
|
|
r = bi / br ;
|
|
dinv = 1.0 / ( br + r * bi) ;
|
|
*cr = ( ar + ai * r) * dinv ;
|
|
*ci = ( ai - ar * r) * dinv ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// MODULUS OF A COMPLEX NUMBER AVOIDING OVERFLOW.
|
|
//------------------------------------------------------------------------------
|
|
double
|
|
Cpoly::cmod( const double r, const double i)
|
|
{
|
|
double ar = abs( r) ;
|
|
double ai = abs( i) ;
|
|
if ( ar < ai)
|
|
return ( ai * sqrt( 1.0 + ( ar * ar) / ( ai * ai))) ;
|
|
|
|
if ( ar > ai)
|
|
return ( ar * sqrt( 1.0 + ( ai * ai) / ( ar * ar))) ;
|
|
|
|
return ( ar * sqrt( 2.0)) ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// MCON PROVIDES MACHINE CONSTANTS USED IN VARIOUS PARTS OF THE PROGRAM.
|
|
// THE USER MAY EITHER SET THEM DIRECTLY OR USE THE STATEMENTS BELOW TO
|
|
// COMPUTE THEM. THE MEANING OF THE FOUR CONSTANTS ARE -
|
|
// ETA THE MAXIMUM RELATIVE REPRESENTATION ERROR WHICH CAN BE DESCRIBED
|
|
// AS THE SMALLEST POSITIVE FLOATING-POINT NUMBER SUCH THAT
|
|
// 1.0_dp + ETA > 1.0.
|
|
// INFINY THE LARGEST FLOATING-POINT NUMBER
|
|
// SMALNO THE SMALLEST POSITIVE FLOATING-POINT NUMBER
|
|
// BASE THE BASE OF THE FLOATING-POINT NUMBER SYSTEM USED
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
Cpoly::mcon( double* eta, double* infiny, double* smalno, double* base)
|
|
{
|
|
*base = RADIX_DOUBLE ;
|
|
*eta = EPS_DOUBLE ;
|
|
*infiny = BIG_DOUBLE ;
|
|
*smalno = SMALL_DOUBLE ;
|
|
}
|