001/*
002 * $Id$
003 */
004
005package edu.jas.root;
006
007
008import java.util.ArrayList;
009import java.util.List;
010import java.util.Map;
011import java.util.TreeMap;
012import java.util.SortedMap;
013
014import org.apache.logging.log4j.Logger;
015import org.apache.logging.log4j.LogManager; 
016
017import edu.jas.arith.BigDecimal;
018import edu.jas.arith.BigRational;
019import edu.jas.arith.Rational;
020import edu.jas.poly.Complex;
021import edu.jas.poly.ComplexRing;
022import edu.jas.poly.GenPolynomial;
023import edu.jas.poly.GenPolynomialRing;
024import edu.jas.poly.PolyUtil;
025import edu.jas.structure.RingElem;
026import edu.jas.structure.RingFactory;
027import edu.jas.structure.UnaryFunctor;
028import edu.jas.ufd.Squarefree;
029import edu.jas.ufd.SquarefreeFactory;
030
031
032/**
033 * Complex roots abstract class.
034 * @param <C> coefficient type.
035 * @author Heinz Kredel
036 */
037public abstract class ComplexRootsAbstract<C extends RingElem<C> & Rational> implements ComplexRoots<C> {
038
039
040    private static final Logger logger = LogManager.getLogger(ComplexRootsAbstract.class);
041
042
043    private static final boolean debug = logger.isDebugEnabled();
044
045
046    /**
047     * Engine for square free decomposition.
048     */
049    public final Squarefree<Complex<C>> engine;
050
051
052    /**
053     * Constructor.
054     * @param cf coefficient factory.
055     */
056    public ComplexRootsAbstract(RingFactory<Complex<C>> cf) {
057        if (!(cf instanceof ComplexRing)) {
058            throw new IllegalArgumentException("cf not supported coefficients " + cf);
059        }
060        engine = SquarefreeFactory.<Complex<C>> getImplementation(cf);
061    }
062
063
064    /**
065     * Root bound. With f(-M + i M) * f(-M - i M) * f(M - i M) * f(M + i M) !=
066     * 0.
067     * @param f univariate polynomial.
068     * @return M such that root(f) is contained in the rectangle spanned by M.
069     */
070    public Complex<C> rootBound(GenPolynomial<Complex<C>> f) {
071        if (f == null) {
072            return null;
073        }
074        RingFactory<Complex<C>> cfac = f.ring.coFac;
075        Complex<C> M = cfac.getONE();
076        if (f.isZERO() || f.isConstant()) {
077            return M;
078        }
079        Complex<C> a = f.leadingBaseCoefficient().norm();
080        for (Complex<C> c : f.getMap().values()) {
081            Complex<C> d = c.norm().divide(a);
082            if (M.compareTo(d) < 0) {
083                M = d;
084            }
085        }
086        M = M.sum(cfac.getONE());
087        //System.out.println("M = " + M);
088        return M;
089    }
090
091
092    /**
093     * Magnitude bound.
094     * @param rect rectangle.
095     * @param f univariate polynomial.
096     * @return B such that |f(c)| &lt; B for c in rect.
097     */
098    public C magnitudeBound(Rectangle<C> rect, GenPolynomial<Complex<C>> f) {
099        if (f == null) {
100            return null;
101        }
102        if (f.isZERO()) {
103            return f.ring.coFac.getONE().getRe();
104        }
105        //System.out.println("f = " + f);
106        if (f.isConstant()) {
107            Complex<C> c = f.leadingBaseCoefficient();
108            return c.norm().getRe();
109        }
110        GenPolynomial<Complex<C>> fa = f.map(new UnaryFunctor<Complex<C>, Complex<C>>() {
111
112
113            public Complex<C> eval(Complex<C> a) {
114                return a.norm();
115            }
116        });
117        //System.out.println("fa = " + fa);
118        Complex<C> Mc = rect.getNW().norm();
119        C M = Mc.getRe();
120        //System.out.println("M = " + M);
121        Complex<C> M1c = rect.getSW().norm();
122        C M1 = M1c.getRe();
123        if (M.compareTo(M1) < 0) {
124            M = M1;
125            Mc = M1c;
126        }
127        M1c = rect.getSE().norm();
128        M1 = M1c.getRe();
129        if (M.compareTo(M1) < 0) {
130            M = M1;
131            Mc = M1c;
132        }
133        M1c = rect.getNE().norm();
134        M1 = M1c.getRe();
135        if (M.compareTo(M1) < 0) {
136            //M = M1;
137            Mc = M1c;
138        }
139        //System.out.println("M = " + M);
140        Complex<C> B = PolyUtil.<Complex<C>> evaluateMain(f.ring.coFac, fa, Mc);
141        //System.out.println("B = " + B);
142        return B.getRe();
143    }
144
145
146    /**
147     * Complex root count of complex polynomial on rectangle.
148     * @param rect rectangle.
149     * @param a univariate complex polynomial.
150     * @return root count of a in rectangle.
151     */
152    public abstract long complexRootCount(Rectangle<C> rect, GenPolynomial<Complex<C>> a)
153                    throws InvalidBoundaryException;
154
155
156    /**
157     * List of complex roots of complex polynomial a on rectangle.
158     * @param rect rectangle.
159     * @param a univariate squarefree complex polynomial.
160     * @return list of complex roots.
161     */
162    public abstract List<Rectangle<C>> complexRoots(Rectangle<C> rect, GenPolynomial<Complex<C>> a)
163                    throws InvalidBoundaryException;
164
165
166    /**
167     * List of complex roots of complex polynomial.
168     * @param a univariate complex polynomial.
169     * @return list of complex roots.
170     */
171    @SuppressWarnings({"cast","unchecked"})
172    public List<Rectangle<C>> complexRoots(GenPolynomial<Complex<C>> a) {
173        List<Rectangle<C>> roots = new ArrayList<Rectangle<C>>();
174        if (a.isConstant() || a.isZERO()) {
175            return roots;
176        }
177        ComplexRing<C> cr = (ComplexRing<C>) a.ring.coFac;
178        GenPolynomial<Complex<C>> sp = engine.squarefreePart(a);
179        SortedMap<GenPolynomial<Complex<C>>, Long> sa = new TreeMap<GenPolynomial<Complex<C>>, Long>();
180        sa.put(sp, 1L);
181        //SortedMap<GenPolynomial<Complex<C>>, Long> sa = engine.squarefreeFactors(a); // BUG to addAll 
182        //System.out.println("squarefree factors = " + sa);
183        for (Map.Entry<GenPolynomial<Complex<C>>, Long> me : sa.entrySet()) { // todo fix addAll
184            GenPolynomial<Complex<C>> p = me.getKey();
185            Complex<C> Mb = rootBound(p);
186            C M = Mb.getRe();
187            C M1 = M.sum(M.factory().fromInteger(1)); // asymmetric to origin
188            //System.out.println("M = " + M);
189            if (debug) {
190                logger.info("rootBound = {}", M);
191            }
192            Complex<C>[] corner = (Complex<C>[]) new Complex[4];
193            corner[0] = new Complex<C>(cr, M1.negate(), M); // nw
194            corner[1] = new Complex<C>(cr, M1.negate(), M1.negate()); // sw
195            corner[2] = new Complex<C>(cr, M, M1.negate()); // se
196            corner[3] = new Complex<C>(cr, M, M); // ne
197            Rectangle<C> rect = new Rectangle<C>(corner);
198            try {
199                List<Rectangle<C>> rs = complexRoots(rect, p);
200                long e = me.getValue(); // sa.get(p);
201                for (int i = 0; i < e; i++) { // add with multiplicity
202                    roots.addAll(rs);
203                }
204            } catch (InvalidBoundaryException e) {
205                //logger.error("invalid boundary for p = {}", p);
206                throw new RuntimeException("this should never happen " + e);
207            }
208        }
209        return roots;
210    }
211
212
213    /**
214     * Complex root refinement of complex polynomial a on rectangle.
215     * @param rect rectangle containing exactly one complex root.
216     * @param a univariate squarefree complex polynomial.
217     * @param len rational length for refinement.
218     * @return refined complex root.
219     */
220    @SuppressWarnings({"cast","unchecked"})
221    public Rectangle<C> complexRootRefinement(Rectangle<C> rect, GenPolynomial<Complex<C>> a, BigRational len)
222                    throws InvalidBoundaryException {
223        ComplexRing<C> cr = (ComplexRing<C>) a.ring.coFac;
224        Rectangle<C> root = rect;
225        long w;
226        if (debug) {
227            w = complexRootCount(root, a);
228            if (w != 1) {
229                System.out.println("#root = " + w);
230                System.out.println("root = " + root);
231                throw new ArithmeticException("no initial isolating rectangle " + rect);
232            }
233        }
234        Complex<C> eps = cr.fromInteger(1);
235        eps = eps.divide(cr.fromInteger(1000)); // 1/1000
236        BigRational length = len.multiply(len);
237        Complex<C> delta = null;
238        boolean work = true;
239        while (work) {
240            try {
241                while (root.rationalLength().compareTo(length) > 0) {
242                    //System.out.println("root = " + root + ", len = " + new BigDecimal(root.rationalLength())); 
243                    if (delta == null) {
244                        delta = root.corners[3].subtract(root.corners[1]);
245                        delta = delta.divide(cr.fromInteger(2));
246                        //System.out.println("delta = " + toDecimal(delta)); 
247                    }
248                    Complex<C> center = root.corners[1].sum(delta);
249                    //System.out.println("refine center = " + toDecimal(center)); 
250                    if (debug) {
251                        logger.info("new center = {}", center);
252                    }
253
254                    Complex<C>[] cp = (Complex<C>[]) copyOfComplex(root.corners, 4);
255                    // cp[0] fix
256                    cp[1] = new Complex<C>(cr, cp[1].getRe(), center.getIm());
257                    cp[2] = center;
258                    cp[3] = new Complex<C>(cr, center.getRe(), cp[3].getIm());
259                    Rectangle<C> nw = new Rectangle<C>(cp);
260                    w = complexRootCount(nw, a);
261                    if (w == 1) {
262                        root = nw;
263                        delta = null;
264                        continue;
265                    }
266
267                    cp = (Complex<C>[]) copyOfComplex(root.corners, 4);
268                    cp[0] = new Complex<C>(cr, cp[0].getRe(), center.getIm());
269                    // cp[1] fix
270                    cp[2] = new Complex<C>(cr, center.getRe(), cp[2].getIm());
271                    cp[3] = center;
272                    Rectangle<C> sw = new Rectangle<C>(cp);
273                    w = complexRootCount(sw, a);
274                    //System.out.println("#swr = " + w); 
275                    if (w == 1) {
276                        root = sw;
277                        delta = null;
278                        continue;
279                    }
280
281                    cp = (Complex<C>[]) copyOfComplex(root.corners, 4);
282                    cp[0] = center;
283                    cp[1] = new Complex<C>(cr, center.getRe(), cp[1].getIm());
284                    // cp[2] fix
285                    cp[3] = new Complex<C>(cr, cp[3].getRe(), center.getIm());
286                    Rectangle<C> se = new Rectangle<C>(cp);
287                    w = complexRootCount(se, a);
288                    //System.out.println("#ser = " + w); 
289                    if (w == 1) {
290                        root = se;
291                        delta = null;
292                        continue;
293                    }
294
295                    cp = (Complex<C>[]) copyOfComplex(root.corners, 4);
296                    cp[0] = new Complex<C>(cr, center.getRe(), cp[0].getIm());
297                    cp[1] = center;
298                    cp[2] = new Complex<C>(cr, cp[2].getRe(), center.getIm());
299                    // cp[3] fix
300                    Rectangle<C> ne = new Rectangle<C>(cp);
301                    w = complexRootCount(ne, a);
302                    //System.out.println("#ner = " + w); 
303                    if (w == 1) {
304                        root = ne;
305                        delta = null;
306                        continue;
307                    }
308                    if (true) {
309                        w = complexRootCount(root, a);
310                        System.out.println("#root = " + w);
311                        System.out.println("root = " + root);
312                    }
313                    throw new ArithmeticException("no isolating rectangle " + rect);
314                }
315                work = false;
316            } catch (InvalidBoundaryException e) {
317                // repeat with new center
318                delta = delta.sum(delta.multiply(eps)); // distort
319                //System.out.println("new refine delta = " + toDecimal(delta));
320                eps = eps.sum(eps.multiply(cr.getIMAG()));
321            }
322        }
323        return root;
324    }
325
326
327    /**
328     * List of complex roots of complex polynomial.
329     * @param a univariate complex polynomial.
330     * @param len rational length for refinement.
331     * @return list of complex roots to desired precision.
332     */
333    @SuppressWarnings({"cast","unchecked"})
334    public List<Rectangle<C>> complexRoots(GenPolynomial<Complex<C>> a, BigRational len) {
335        ComplexRing<C> cr = (ComplexRing<C>) a.ring.coFac;
336        SortedMap<GenPolynomial<Complex<C>>, Long> sa = engine.squarefreeFactors(a);
337        List<Rectangle<C>> roots = new ArrayList<Rectangle<C>>();
338        for (Map.Entry<GenPolynomial<Complex<C>>, Long> me : sa.entrySet()) {
339            GenPolynomial<Complex<C>> p = me.getKey();
340            Complex<C> Mb = rootBound(p);
341            C M = Mb.getRe();
342            C M1 = M.sum(M.factory().fromInteger(1)); // asymmetric to origin
343            if (debug) {
344                logger.info("rootBound = {}", M);
345            }
346            Complex<C>[] corner = (Complex<C>[]) new Complex[4];
347            corner[0] = new Complex<C>(cr, M1.negate(), M); // nw
348            corner[1] = new Complex<C>(cr, M1.negate(), M1.negate()); // sw
349            corner[2] = new Complex<C>(cr, M, M1.negate()); // se
350            corner[3] = new Complex<C>(cr, M, M); // ne
351            Rectangle<C> rect = new Rectangle<C>(corner);
352            try {
353                List<Rectangle<C>> rs = complexRoots(rect, p);
354                List<Rectangle<C>> rf = new ArrayList<Rectangle<C>>(rs.size());
355                for (Rectangle<C> r : rs) {
356                    Rectangle<C> rr = complexRootRefinement(r, p, len);
357                    rf.add(rr);
358                }
359                long e = me.getValue(); // sa.get(p);
360                for (int i = 0; i < e; i++) { // add with multiplicity
361                    roots.addAll(rf);
362                }
363            } catch (InvalidBoundaryException e) {
364                throw new RuntimeException("this should never happen " + e);
365            }
366        }
367        return roots;
368    }
369
370
371    /**
372     * Invariant rectangle for algebraic number.
373     * @param rect root isolating rectangle for f which contains exactly one
374     *            root.
375     * @param f univariate polynomial, non-zero.
376     * @param g univariate polynomial, gcd(f,g) == 1.
377     * @return v with v a new rectangle contained in iv such that g(w) != 0 for
378     *         w in v.
379     */
380    public abstract Rectangle<C> invariantRectangle(Rectangle<C> rect, GenPolynomial<Complex<C>> f,
381                    GenPolynomial<Complex<C>> g) throws InvalidBoundaryException;
382
383
384    /**
385     * Get decimal approximation.
386     * @param a complex number.
387     * @return decimal(a).
388     */
389    public String toDecimal(Complex<C> a) {
390        C r = a.getRe();
391        String s = r.toString();
392        BigRational rs = new BigRational(s);
393        BigDecimal rd = new BigDecimal(rs);
394        C i = a.getIm();
395        s = i.toString();
396        BigRational is = new BigRational(s);
397        BigDecimal id = new BigDecimal(is);
398        //System.out.println("rd = " + rd);
399        //System.out.println("id = " + id);
400        return rd.toString() + " i " + id.toString();
401    }
402
403
404    /**
405     * Approximate complex root.
406     * @param rt root isolating rectangle.
407     * @param f univariate polynomial, non-zero.
408     * @param eps requested interval length.
409     * @return a decimal approximation d such that |d-v| &lt; eps, for f(v) = 0,
410     *         v in rt.
411     */
412    public Complex<BigDecimal> approximateRoot(Rectangle<C> rt, GenPolynomial<Complex<C>> f, BigRational eps)
413                    throws NoConvergenceException {
414        if (rt == null) {
415            throw new IllegalArgumentException("null interval not allowed");
416        }
417        Complex<BigDecimal> d = rt.getDecimalCenter();
418        //System.out.println("d  = " + d);
419        if (f == null || f.isZERO() || f.isConstant() || eps == null) {
420            return d;
421        }
422        if (rt.rationalLength().compareTo(eps) < 0) {
423            return d;
424        }
425        ComplexRing<BigDecimal> cr = d.ring;
426        Complex<C> sw = rt.getSW();
427        BigDecimal swr = new BigDecimal(sw.getRe().getRational());
428        BigDecimal swi = new BigDecimal(sw.getIm().getRational());
429        Complex<BigDecimal> ll = new Complex<BigDecimal>(cr, swr, swi);
430        Complex<C> ne = rt.getNE();
431        BigDecimal ner = new BigDecimal(ne.getRe().getRational());
432        BigDecimal nei = new BigDecimal(ne.getIm().getRational());
433        Complex<BigDecimal> ur = new Complex<BigDecimal>(cr, ner, nei);
434
435        BigDecimal e = new BigDecimal(eps.getRational());
436        Complex<BigDecimal> q = new Complex<BigDecimal>(cr, new BigDecimal("0.25"));
437        e = e.multiply(d.norm().getRe()); // relative error
438        //System.out.println("e  = " + e);
439
440        // polynomials with decimal coefficients
441        GenPolynomialRing<Complex<BigDecimal>> dfac = new GenPolynomialRing<Complex<BigDecimal>>(cr, f.ring);
442        GenPolynomial<Complex<BigDecimal>> df = PolyUtil.<C> complexDecimalFromRational(dfac, f);
443        GenPolynomial<Complex<C>> fp = PolyUtil.<Complex<C>> baseDeriviative(f);
444        GenPolynomial<Complex<BigDecimal>> dfp = PolyUtil.<C> complexDecimalFromRational(dfac, fp);
445
446        // Newton Raphson iteration: x_{n+1} = x_n - f(x_n)/f'(x_n)
447        int i = 0;
448        final int MITER = 50;
449        int dir = -1;
450        while (i++ < MITER) {
451            Complex<BigDecimal> fx = PolyUtil.<Complex<BigDecimal>> evaluateMain(cr, df, d); // f(d)
452            //BigDecimal fs = fx.norm().getRe();
453            //System.out.println("fs = " + fs);
454            if (fx.isZERO()) {
455                return d;
456            }
457            Complex<BigDecimal> fpx = PolyUtil.<Complex<BigDecimal>> evaluateMain(cr, dfp, d); // f'(d)
458            if (fpx.isZERO()) {
459                throw new NoConvergenceException("zero deriviative should not happen");
460            }
461            Complex<BigDecimal> x = fx.divide(fpx);
462            Complex<BigDecimal> dx = d.subtract(x);
463            //System.out.println("dx = " + dx);
464            if (d.subtract(dx).norm().getRe().compareTo(e) <= 0) {
465                return dx;
466            }
467            //             if ( false ) { // not useful:
468            //                 Complex<BigDecimal> fxx  = PolyUtil.<Complex<BigDecimal>> evaluateMain(cr, df, dx); // f(dx)
469            //                 //System.out.println("fxx = " + fxx);
470            //                 BigDecimal fsx = fxx.norm().getRe();
471            //                 System.out.println("fsx = " + fsx);
472            //                 while ( fsx.compareTo( fs ) >= 0 ) {
473            //                     System.out.println("trying to increase f(d) ");
474            //                     if ( i++ > MITER ) { // dx > right: dx - right > 0
475            //                         throw new NoConvergenceException("no convergence after " + i + " steps");
476            //                     }
477            //                     x = x.multiply(q); // x * 1/4
478            //                     dx = d.subtract(x);
479            //                     //System.out.println(" x = " + x);
480            //                     System.out.println("dx = " + dx);
481            //                     fxx  = PolyUtil.<Complex<BigDecimal>> evaluateMain(cr, df, dx); // f(dx)
482            //                     //System.out.println("fxx = " + fxx);
483            //                     fsx = fxx.norm().getRe();
484            //                     System.out.println("fsx = " + fsx);
485            //                 }
486            //             }
487            // check interval bounds
488            while (dx.getRe().compareTo(ll.getRe()) < 0 || dx.getIm().compareTo(ll.getIm()) < 0
489                            || dx.getRe().compareTo(ur.getRe()) > 0 || dx.getIm().compareTo(ur.getIm()) > 0) {
490                // dx < ll: dx - ll < 0
491                // dx > ur: dx - ur > 0
492                if (i++ > MITER) { // dx > right: dx - right > 0
493                    throw new NoConvergenceException("no convergence after " + i + " steps");
494                }
495                if (i > MITER / 2 && dir == 0) {
496                    Complex<C> cc = rt.getCenter();
497                    Rectangle<C> nrt = rt.exchangeSE(cc);
498                    Complex<BigDecimal> sd = nrt.getDecimalCenter();
499                    d = sd;
500                    x = cr.getZERO();
501                    logger.info("trying new SE starting point {}", d);
502                    i = 0;
503                    dir = 1;
504                }
505                if (i > MITER / 2 && dir == 1) {
506                    Complex<C> cc = rt.getCenter();
507                    Rectangle<C> nrt = rt.exchangeNW(cc);
508                    Complex<BigDecimal> sd = nrt.getDecimalCenter();
509                    d = sd;
510                    x = cr.getZERO();
511                    logger.info("trying new NW starting point {}", d);
512                    i = 0;
513                    dir = 2;
514                }
515                if (i > MITER / 2 && dir == 2) {
516                    Complex<C> cc = rt.getCenter();
517                    Rectangle<C> nrt = rt.exchangeSW(cc);
518                    Complex<BigDecimal> sd = nrt.getDecimalCenter();
519                    d = sd;
520                    x = cr.getZERO();
521                    logger.info("trying new SW starting point {}", d);
522                    i = 0;
523                    dir = 3;
524                }
525                if (i > MITER / 2 && dir == 3) {
526                    Complex<C> cc = rt.getCenter();
527                    Rectangle<C> nrt = rt.exchangeNE(cc);
528                    Complex<BigDecimal> sd = nrt.getDecimalCenter();
529                    d = sd;
530                    x = cr.getZERO();
531                    logger.info("trying new NE starting point {}", d);
532                    i = 0;
533                    dir = 4;
534                }
535                if (i > MITER / 2 && (dir == -1 || dir == 4 || dir == 5)) {
536                    Complex<C> sr = rt.randomPoint();
537                    BigDecimal srr = new BigDecimal(sr.getRe().getRational());
538                    BigDecimal sri = new BigDecimal(sr.getIm().getRational());
539                    Complex<BigDecimal> sd = new Complex<BigDecimal>(cr, srr, sri);
540                    d = sd;
541                    x = cr.getZERO();
542                    logger.info("trying new random starting point {}", d);
543                    if (dir == -1) {
544                        i = 0;
545                        dir = 0;
546                    } else if (dir == 4) {
547                        i = 0;
548                        dir = 5;
549                    } else {
550                        //i = 0; 
551                        dir = 6; // end
552                    }
553                }
554                x = x.multiply(q); // x * 1/4
555                dx = d.subtract(x);
556                //System.out.println(" x = " + x);
557                //System.out.println("dx = " + dx);
558            }
559            d = dx;
560        }
561        throw new NoConvergenceException("no convergence after " + i + " steps");
562    }
563
564
565    /**
566     * List of decimal approximations of complex roots of complex polynomial.
567     * @param a univariate complex polynomial.
568     * @param eps length for refinement.
569     * @return list of complex decimal root approximations to desired precision.
570     */
571    @SuppressWarnings({"cast","unchecked"})
572    public List<Complex<BigDecimal>> approximateRoots(GenPolynomial<Complex<C>> a, BigRational eps) {
573        ComplexRing<C> cr = (ComplexRing<C>) a.ring.coFac;
574        SortedMap<GenPolynomial<Complex<C>>, Long> sa = engine.squarefreeFactors(a);
575        List<Complex<BigDecimal>> roots = new ArrayList<Complex<BigDecimal>>();
576        for (Map.Entry<GenPolynomial<Complex<C>>, Long> me : sa.entrySet()) {
577            GenPolynomial<Complex<C>> p = me.getKey();
578            List<Complex<BigDecimal>> rf = null;
579            if (p.degree(0) <= 1) {
580                Complex<C> tc = p.trailingBaseCoefficient();
581                tc = tc.negate();
582                BigDecimal rr = new BigDecimal(tc.getRe().getRational());
583                BigDecimal ri = new BigDecimal(tc.getIm().getRational());
584                ComplexRing<BigDecimal> crf = new ComplexRing<BigDecimal>(rr);
585                Complex<BigDecimal> r = new Complex<BigDecimal>(crf, rr, ri);
586                rf = new ArrayList<Complex<BigDecimal>>(1);
587                rf.add(r);
588            } else {
589                Complex<C> Mb = rootBound(p);
590                C M = Mb.getRe();
591                C M1 = M.sum(M.factory().fromInteger(1)); // asymmetric to origin
592                if (debug) {
593                    logger.info("rootBound = {}", M);
594                }
595                Complex<C>[] corner = (Complex<C>[]) new Complex[4];
596                corner[0] = new Complex<C>(cr, M1.negate(), M); // nw
597                corner[1] = new Complex<C>(cr, M1.negate(), M1.negate()); // sw
598                corner[2] = new Complex<C>(cr, M, M1.negate()); // se
599                corner[3] = new Complex<C>(cr, M, M); // ne
600                Rectangle<C> rect = new Rectangle<C>(corner);
601                List<Rectangle<C>> rs = null;
602                try {
603                    rs = complexRoots(rect, p);
604                } catch (InvalidBoundaryException e) {
605                    throw new RuntimeException("this should never happen " + e);
606                }
607                rf = new ArrayList<Complex<BigDecimal>>(rs.size());
608                for (Rectangle<C> r : rs) {
609                    Complex<BigDecimal> rr = null;
610                    while (rr == null) {
611                        try {
612                            rr = approximateRoot(r, p, eps);
613                            rf.add(rr);
614                        } catch (NoConvergenceException e) {
615                            // fall back to exact algorithm
616                            BigRational len = r.rationalLength();
617                            len = len.multiply(new BigRational(1, 1000));
618                            try {
619                                r = complexRootRefinement(r, p, len);
620                                logger.info("fall back rootRefinement = {}", r);
621                                //System.out.println("len = " + len);
622                            } catch (InvalidBoundaryException ee) {
623                                throw new RuntimeException("this should never happen " + ee);
624                            }
625                        }
626                    }
627                }
628            }
629            long e = me.getValue(); // sa.get(p);
630            for (int i = 0; i < e; i++) { // add with multiplicity
631                roots.addAll(rf);
632            }
633        }
634        return roots;
635    }
636
637
638    /**
639     * Copy the specified array.
640     * @param original array.
641     * @param newLength new array length.
642     * @return copy of this.
643     */
644    public Complex[] copyOfComplex(Complex[] original, int newLength) {
645        Complex[] copy = new Complex[newLength];
646        System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
647        return copy;
648    }
649
650
651    /**
652     * Invariant rectangle for algebraic number magnitude.
653     * @param rect root isolating rectangle for f which contains exactly one
654     *            root.
655     * @param f univariate polynomial, non-zero.
656     * @param g univariate polynomial, gcd(f,g) == 1.
657     * @param eps length limit for rectangle length.
658     * @return v with v a new rectangle contained in rect such that |g(a) -
659     *         g(b)| &lt; eps for a, b in v in rect.
660     */
661    public Rectangle<C> invariantMagnitudeRectangle(Rectangle<C> rect, GenPolynomial<Complex<C>> f,
662                    GenPolynomial<Complex<C>> g, BigRational eps) throws InvalidBoundaryException {
663        Rectangle<C> v = rect;
664        if (g == null || g.isZERO()) {
665            return v;
666        }
667        if (g.isConstant()) {
668            return v;
669        }
670        if (f == null || f.isZERO() || f.isConstant()) { // ?
671            return v;
672        }
673        GenPolynomial<Complex<C>> gp = PolyUtil.<Complex<C>> baseDeriviative(g);
674        //System.out.println("g  = " + g);
675        //System.out.println("gp = " + gp);
676        BigRational B = magnitudeBound(rect, gp).getRational();
677        //System.out.println("B = " + B + " : " + B.getClass());
678
679        BigRational len = v.rationalLength();
680        BigRational half = new BigRational(1, 2);
681
682        BigRational vlen = v.rationalLength();
683        vlen = vlen.multiply(vlen);
684        //eps = eps.multiply(eps);
685        //System.out.println("v = " + v);
686        //System.out.println("vlen = " + vlen);
687        while (B.multiply(vlen).compareTo(eps) >= 0) { // TODO: test squared
688            len = len.multiply(half);
689            v = complexRootRefinement(v, f, len);
690            //System.out.println("v = " + v);
691            vlen = v.rationalLength();
692            vlen = vlen.multiply(vlen);
693            //System.out.println("vlen = " + vlen);
694        }
695        //System.out.println("vlen = " + vlen);
696        return v;
697    }
698
699
700    /**
701     * Complex algebraic number magnitude.
702     * @param rect root isolating rectangle for f which contains exactly one
703     *            root, with rect such that |g(a) - g(b)| &lt; eps for a, b in
704     *            rect.
705     * @param f univariate polynomial, non-zero.
706     * @param g univariate polynomial, gcd(f,g) == 1.
707     * @return g(rect) .
708     */
709    public Complex<C> complexRectangleMagnitude(Rectangle<C> rect, GenPolynomial<Complex<C>> f,
710                    GenPolynomial<Complex<C>> g) {
711        if (g.isZERO() || g.isConstant()) {
712            return g.leadingBaseCoefficient();
713        }
714        RingFactory<Complex<C>> cfac = f.ring.coFac;
715        //System.out.println("cfac = " + cfac + " : " + cfac.getClass());
716        Complex<C> c = rect.getCenter();
717        Complex<C> ev = PolyUtil.<Complex<C>> evaluateMain(cfac, g, c);
718        return ev;
719    }
720
721
722    /**
723     * Complex algebraic number magnitude.
724     * @param rect root isolating rectangle for f which contains exactly one
725     *            root, with rect such that |g(a) - g(b)| &lt; eps for a, b in
726     *            rect.
727     * @param f univariate polynomial, non-zero.
728     * @param g univariate polynomial, gcd(f,g) == 1.
729     * @param eps length limit for rectangle length.
730     * @return g(rect) .
731     */
732    public Complex<C> complexMagnitude(Rectangle<C> rect, GenPolynomial<Complex<C>> f,
733                    GenPolynomial<Complex<C>> g, BigRational eps) throws InvalidBoundaryException {
734        if (g.isZERO() || g.isConstant()) {
735            return g.leadingBaseCoefficient();
736        }
737        Rectangle<C> v = invariantMagnitudeRectangle(rect, f, g, eps);
738        //System.out.println("ref = " + ref);
739        return complexRectangleMagnitude(v, f, g);
740    }
741
742}