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