final double absoluteAccuracy = getAbsoluteAccuracy();
final double relativeAccuracy = getRelativeAccuracy();
final double functionValueAccuracy = getFunctionValueAccuracy();
Complex N = new Complex(n, 0.0);
Complex N1 = new Complex(n - 1, 0.0);
Complex pv = null;
Complex dv = null;
Complex d2v = null;
Complex G = null;
Complex G2 = null;
Complex H = null;
Complex delta = null;
Complex denominator = null;
Complex z = initial;
Complex oldz = new Complex(Double.POSITIVE_INFINITY,
Double.POSITIVE_INFINITY);
while (true) {
// Compute pv (polynomial value), dv (derivative value), and
// d2v (second derivative value) simultaneously.
pv = coefficients[n];
dv = Complex.ZERO;
d2v = Complex.ZERO;
for (int j = n-1; j >= 0; j--) {
d2v = dv.add(z.multiply(d2v));
dv = pv.add(z.multiply(dv));
pv = coefficients[j].add(z.multiply(pv));
}
d2v = d2v.multiply(new Complex(2.0, 0.0));
// check for convergence
double tolerance = FastMath.max(relativeAccuracy * z.abs(),
absoluteAccuracy);
if ((z.subtract(oldz)).abs() <= tolerance) {
return z;
}
if (pv.abs() <= functionValueAccuracy) {
return z;
}
// now pv != 0, calculate the new approximation
G = dv.divide(pv);
G2 = G.multiply(G);
H = G2.subtract(d2v.divide(pv));
delta = N1.multiply((N.multiply(H)).subtract(G2));
// choose a denominator larger in magnitude
Complex deltaSqrt = delta.sqrt();
Complex dplus = G.add(deltaSqrt);
Complex dminus = G.subtract(deltaSqrt);
denominator = dplus.abs() > dminus.abs() ? dplus : dminus;
// Perturb z if denominator is zero, for instance,
// p(x) = x^3 + 1, z = 0.
if (denominator.equals(new Complex(0.0, 0.0))) {
z = z.add(new Complex(absoluteAccuracy, absoluteAccuracy));
oldz = new Complex(Double.POSITIVE_INFINITY,
Double.POSITIVE_INFINITY);
} else {
oldz = z;
z = z.subtract(N.divide(denominator));
}