/*
 * $Id$
 */

package edu.jas.root;


import java.util.ArrayList;
//import java.util.Arrays;
import java.util.List;

import org.apache.log4j.Logger;

import edu.jas.arith.Rational;
import edu.jas.arith.BigRational;
import edu.jas.poly.Complex;
import edu.jas.poly.ComplexRing;
import edu.jas.poly.GenPolynomial;
import edu.jas.poly.GenPolynomialRing;
import edu.jas.poly.PolyUtil;
import edu.jas.structure.RingElem;
import edu.jas.structure.GcdRingElem;
import edu.jas.structure.RingFactory;
import edu.jas.util.ArrayUtil;


/**
 * Complex roots implemented by Sturm sequences. Algorithms use exact method
 * derived from Wilf's numeric Routh-Hurwitz method.
 * @param  coefficient type.
 * @author Heinz Kredel
 */
public class ComplexRootsSturm & Rational> extends ComplexRootsAbstract {


    private static final Logger logger = Logger.getLogger(ComplexRootsSturm.class);


    private final boolean debug = logger.isDebugEnabled();


    /**
     * Constructor.
     * @param cf coefficient factory.
     */
    public ComplexRootsSturm(RingFactory> cf) {
        super(cf);
        //ufd = GCDFactory.> getImplementation(cf);
    }


    /**
     * Cauchy index of rational function f/g on interval.
     * @param a interval bound for I = [a,b].
     * @param b interval bound for I = [a,b].
     * @param f univariate polynomial.
     * @param g univariate polynomial.
     * @return winding number of f/g in I.
     */
    public long indexOfCauchy(C a, C b, GenPolynomial f, GenPolynomial g) {
        List> S = sturmSequence(g, f);
        //System.out.println("S = " + S);
        if (debug) {
            logger.info("sturmSeq = " + S);
        }
        RingFactory cfac = f.ring.coFac;
        List l = PolyUtil. evaluateMain(cfac, S, a);
        List r = PolyUtil. evaluateMain(cfac, S, b);
        long v = RootUtil. signVar(l) - RootUtil. signVar(r);
        //System.out.println("v = " + v);
        //         if (v < 0L) {
        //             v = -v;
        //         }
        return v;
    }


    /**
     * Routh index of complex function f + i g on interval.
     * @param a interval bound for I = [a,b].
     * @param b interval bound for I = [a,b].
     * @param f univariate polynomial.
     * @param g univariate polynomial != 0.
     * @return index number of f + i g.
     */
    public long[] indexOfRouth(C a, C b, GenPolynomial f, GenPolynomial g) {
        List> S = sturmSequence(f, g);
        //System.out.println("S = " + S);
        RingFactory cfac = f.ring.coFac;
        List l = PolyUtil. evaluateMain(cfac, S, a);
        List r = PolyUtil. evaluateMain(cfac, S, b);
        long v = RootUtil. signVar(l) - RootUtil. signVar(r);
        //System.out.println("v = " + v);

        long d = f.degree(0);
        if (d < g.degree(0)) {
            d = g.degree(0);
        }
        //System.out.println("d = " + d);
        long ui = (d - v) / 2;
        long li = (d + v) / 2;
        //System.out.println("upper = " + ui);
        //System.out.println("lower = " + li);
        return new long[] { ui, li };
    }


    /**
     * Sturm sequence.
     * @param f univariate polynomial.
     * @param g univariate polynomial.
     * @return a Sturm sequence for f and g.
     */
    public List> sturmSequence(GenPolynomial f, GenPolynomial g) {
        List> S = new ArrayList>();
        if (f == null || f.isZERO()) {
            return S;
        }
        if (f.isConstant()) {
            S.add(f.monic());
            return S;
        }
        GenPolynomial F = f;
        S.add(F);
        GenPolynomial G = g; //PolyUtil. baseDeriviative(f);
        while (!G.isZERO()) {
            GenPolynomial r = F.remainder(G);
            F = G;
            G = r.negate();
            S.add(F/*.monic()*/);
        }
        //System.out.println("F = " + F);
        if (F.isConstant()) {
            return S;
        }
        // make squarefree
        List> Sp = new ArrayList>(S.size());
        for (GenPolynomial p : S) {
            p = p.divide(F);
            Sp.add(p);
        }
        return Sp;
    }


    /**
     * Complex root count of complex polynomial on rectangle.
     * @param rect rectangle.
     * @param a univariate complex polynomial.
     * @return root count of a in rectangle.
     */
    @Override
    public long complexRootCount(Rectangle rect, GenPolynomial> a)
            throws InvalidBoundaryException {
        C rl = rect.lengthReal();
        C il = rect.lengthImag();
        // only linear polynomials have zero length intervals
        if ( rl.isZERO() && il.isZERO() ) {
            Complex e = PolyUtil.> evaluateMain(a.ring.coFac, a, rect.getSW());
            if ( e.isZERO() ) {
                return 1;
            }
            return 0;
        }
        if ( rl.isZERO() || il.isZERO() ) {
            //RingFactory cf = (RingFactory) rl.factory();
            //GenPolynomialRing rfac = new GenPolynomialRing(cf,a.ring);
            //cf = (RingFactory) il.factory();
            //GenPolynomialRing ifac = new GenPolynomialRing(cf,a.ring);
            //GenPolynomial rp = PolyUtil. realPartFromComplex(rfac, a);
            //GenPolynomial ip = PolyUtil. imaginaryPartFromComplex(ifac, a);
            //RealRoots rr = new RealRootsSturm();
            if ( rl.isZERO() ) {
                //logger.info("lengthReal == 0: " + rect);
                //Complex r = rect.getSW();
                //r = new Complex(r.ring,r.getRe()/*,0*/);
                //Complex e = PolyUtil.> evaluateMain(a.ring.coFac, a, r);
                //logger.info("a(re(rect)): " + e);
                //if ( !e.getRe().isZERO() ) {
                //    return 0;
                //}
                //C ev = PolyUtil. evaluateMain(rp.ring.coFac, rp, rl);
                //logger.info("re(a)(re(rect)): " + ev);
                //Interval iv = new Interval(rect.getSW().getIm(),rect.getNE().getIm());
                //logger.info("iv: " + iv);
                //long ic = rr.realRootCount(iv,ip);
                //logger.info("ic: " + ic);

                Complex sw = rect.getSW();
                Complex ne = rect.getNE();
                C delta = sw.ring.ring.parse("1"); // works since linear polynomial
                Complex cd = new Complex(sw.ring,delta/*, 0*/);
                sw = sw.subtract(cd);
                ne = ne.sum(cd);
                rect = rect.exchangeSW(sw);
                rect = rect.exchangeNE(ne);
                logger.info("new rectangle: " + rect.toScript());
            }
            if ( il.isZERO() ) {
                //logger.info("lengthImag == 0: " + rect);
                //Interval rv = new Interval(rect.getSW().getRe(),rect.getNE().getRe());
                //logger.info("rv: " + rv);
                //long rc = rr.realRootCount(rv,rp);
                //logger.info("rc: " + rc);

                Complex sw = rect.getSW();
                Complex ne = rect.getNE();
                C delta = sw.ring.ring.parse("1"); // works since linear polynomial
                Complex cd = new Complex(sw.ring,sw.ring.ring.getZERO(),delta);
                sw = sw.subtract(cd);
                ne = ne.sum(cd);
                rect = rect.exchangeSW(sw);
                rect = rect.exchangeNE(ne);
                logger.info("new rectangle: " + rect.toScript());
            }
        }
        return windingNumber(rect, a);
    }


    /**
     * Winding number of complex function A on rectangle.
     * @param rect rectangle.
     * @param A univariate complex polynomial.
     * @return winding number of A arround rect.
     */
    public long windingNumber(Rectangle rect, GenPolynomial> A) throws InvalidBoundaryException {
        Boundary bound = new Boundary(rect, A); // throws InvalidBoundaryException
        ComplexRing cr = (ComplexRing) A.ring.coFac;
        RingFactory cf = cr.ring;
        C zero = cf.getZERO();
        C one = cf.getONE();
        long ix = 0L;
        for (int i = 0; i < 4; i++) {
            long ci = indexOfCauchy(zero, one, bound.getRealPart(i), bound.getImagPart(i));
            //System.out.println("ci["+i+","+(i+1)+"] = " + ci);
            ix += ci;
        }
        if (ix % 2L != 0) {
            throw new InvalidBoundaryException("odd winding number " + ix);
        }
        return ix / 2L;
    }


    /**
     * List of complex roots of complex polynomial a on rectangle.
     * @param rect rectangle.
     * @param a univariate squarefree complex polynomial.
     * @return list of complex roots.
     */
    @Override
    public List> complexRoots(Rectangle rect, GenPolynomial> a)
            throws InvalidBoundaryException {
        ComplexRing cr = (ComplexRing) a.ring.coFac;
        List> roots = new ArrayList>();
        if ( a.isConstant() || a.isZERO() ) {
            return roots;
        }
        //System.out.println("rect = " + rect); 
        long n = windingNumber(rect, a);
        if (n < 0) { // can this happen?
            throw new RuntimeException("negative winding number " + n);
            //System.out.println("negative winding number " + n);
            //return roots;
        }
        if (n == 0) {
            return roots;
        }
        if (n == 1) {
            roots.add(rect);
            return roots;
        }
        Complex eps = cr.fromInteger(1);
        eps = eps.divide(cr.fromInteger(1000)); // 1/1000
        //System.out.println("eps = " + eps);
        //System.out.println("rect = " + rect); 
        // construct new center
        Complex delta = rect.corners[3].subtract(rect.corners[1]);
        delta = delta.divide(cr.fromInteger(2));
        //System.out.println("delta = " + delta); 
        boolean work = true;
        while (work) {
            Complex center = rect.corners[1].sum(delta);
            //System.out.println("center = " + toDecimal(center)); 
            if (debug) {
                logger.info("new center = " + center);
            }
            try {
                Complex[] cp = (Complex[]) copyOfComplex(rect.corners, 4);
                    // (Complex[]) new Complex[4];  cp[0] = rect.corners[0];
                // cp[0] fix
                cp[1] = new Complex(cr, cp[1].getRe(), center.getIm());
                cp[2] = center;
                cp[3] = new Complex(cr, center.getRe(), cp[3].getIm());
                Rectangle nw = new Rectangle(cp);
                //System.out.println("nw = " + nw); 
                List> nwr = complexRoots(nw, a);
                //System.out.println("#nwr = " + nwr.size()); 
                roots.addAll(nwr);
                if ( roots.size() == a.degree(0) ) {
                    work = false;
                    break;
                }

                cp = (Complex[]) copyOfComplex(rect.corners, 4);
                cp[0] = new Complex(cr, cp[0].getRe(), center.getIm());
                // cp[1] fix
                cp[2] = new Complex(cr, center.getRe(), cp[2].getIm());
                cp[3] = center;
                Rectangle sw = new Rectangle(cp);
                //System.out.println("sw = " + sw); 
                List> swr = complexRoots(sw, a);
                //System.out.println("#swr = " + swr.size()); 
                roots.addAll(swr);
                if ( roots.size() == a.degree(0) ) {
                    work = false;
                    break;
                }

                cp = (Complex[]) copyOfComplex(rect.corners, 4);
                cp[0] = center;
                cp[1] = new Complex(cr, center.getRe(), cp[1].getIm());
                // cp[2] fix
                cp[3] = new Complex(cr, cp[3].getRe(), center.getIm());
                Rectangle se = new Rectangle(cp);
                //System.out.println("se = " + se); 
                List> ser = complexRoots(se, a);
                //System.out.println("#ser = " + ser.size()); 
                roots.addAll(ser);
                if ( roots.size() == a.degree(0) ) {
                    work = false;
                    break;
                }

                cp = (Complex[]) copyOfComplex(rect.corners, 4);
                cp[0] = new Complex(cr, center.getRe(), cp[0].getIm());
                cp[1] = center;
                cp[2] = new Complex(cr, cp[2].getRe(), center.getIm());
                // cp[3] fix
                Rectangle ne = new Rectangle(cp);
                //System.out.println("ne = " + ne); 
                List> ner = complexRoots(ne, a);
                //System.out.println("#ner = " + ner.size()); 
                roots.addAll(ner);
                work = false;
            } catch (InvalidBoundaryException e) {
                // repeat with new center
                delta = delta.sum(delta.multiply(eps)); // distort
                //System.out.println("new delta = " + toDecimal(delta)); 
                eps = eps.sum(eps.multiply(cr.getIMAG()));
            }
        }
        return roots;
    }


    /**
     * Invariant rectangle for algebraic number.
     * @param rect root isolating rectangle for f which contains exactly one root.
     * @param f univariate polynomial, non-zero.
     * @param g univariate polynomial, gcd(f,g) == 1.
     * @return v a new rectangle contained in rect such that g(w) != 0 for w in v.
     */
    @Override
    public Rectangle invariantRectangle(Rectangle rect, 
                                           GenPolynomial> f, 
                                           GenPolynomial> g) 
                        throws InvalidBoundaryException {
        Rectangle v = rect;
        if (g == null || g.isZERO()) {
            return v;
        }
        if (g.isConstant()) {
            return v;
        }
        if (f == null || f.isZERO() || f.isConstant()) { // ?
            return v;
        }
        BigRational len = v.rationalLength();
        BigRational half = new BigRational(1,2);
        while (true) {
            long n = windingNumber(v, g);
            //System.out.println("n = " + n);
            if (n < 0) { // can this happen?
                throw new RuntimeException("negative winding number " + n);
            }
            if (n == 0) {
                return v;
            }
            len = len.multiply(half);
            Rectangle v1 = v;
            v = complexRootRefinement(v,f,len);
            if ( v.equals(v1) ) {
                //System.out.println("len = " + len);
                if ( !f.gcd(g).isONE() ) {
                    System.out.println("f.gcd(g) = " + f.gcd(g));
                    throw new RuntimeException("no convergence " + v);
                }
                //break; // no convergence
            }
        }
        //return v;
    }

}

 

Ads help maintain this website.