001/*
002 * $Id$
003 */
004
005package edu.jas.root;
006
007
008import java.util.ArrayList;
009import java.util.List;
010
011import org.apache.logging.log4j.LogManager;
012import org.apache.logging.log4j.Logger;
013
014import edu.jas.arith.BigRational;
015import edu.jas.arith.Rational;
016import edu.jas.poly.ExpVector;
017import edu.jas.poly.GenPolynomial;
018import edu.jas.poly.GenPolynomialRing;
019import edu.jas.poly.PolyUtil;
020import edu.jas.structure.RingElem;
021import edu.jas.structure.RingFactory;
022
023
024/**
025 * Real root isolation using Sturm sequences.
026 * @param <C> coefficient type.
027 * @author Heinz Kredel
028 */
029public class RealRootsSturm<C extends RingElem<C> & Rational> extends RealRootsAbstract<C> {
030
031
032    private static final Logger logger = LogManager.getLogger(RealRootsSturm.class);
033
034
035    private static final boolean debug = logger.isDebugEnabled();
036
037
038    /**
039     * Sturm sequence.
040     * @param f univariate polynomial.
041     * @return a Sturm sequence for f.
042     */
043    public List<GenPolynomial<C>> sturmSequence(GenPolynomial<C> f) {
044        List<GenPolynomial<C>> S = new ArrayList<GenPolynomial<C>>();
045        if (f == null || f.isZERO()) {
046            return S;
047        }
048        if (f.isConstant()) {
049            S.add(f.monic());
050            return S;
051        }
052        GenPolynomial<C> F = f;
053        S.add(F);
054        GenPolynomial<C> G = PolyUtil.<C> baseDeriviative(f);
055        while (!G.isZERO()) {
056            GenPolynomial<C> r = F.remainder(G);
057            F = G;
058            G = r.negate();
059            S.add(F/*.monic()*/);
060        }
061        //System.out.println("F = " + F);
062        if (F.isConstant()) {
063            return S;
064        }
065        // make squarefree
066        List<GenPolynomial<C>> Sp = new ArrayList<GenPolynomial<C>>(S.size());
067        for (GenPolynomial<C> p : S) {
068            p = p.divide(F);
069            Sp.add(p);
070        }
071        return Sp;
072    }
073
074
075    /**
076     * Isolating intervals for the real roots.
077     * @param f univariate polynomial.
078     * @return a list of isolating intervals for the real roots of f.
079     */
080    @SuppressWarnings("cast")
081    @Override
082    public List<Interval<C>> realRoots(GenPolynomial<C> f) {
083        List<Interval<C>> R = new ArrayList<Interval<C>>();
084        if (f == null) {
085            return R;
086        }
087        GenPolynomialRing<C> pfac = f.ring;
088        if (f.isZERO()) {
089            C z = pfac.coFac.getZERO();
090            R.add(new Interval<C>(z));
091            return R;
092        }
093        // check trailing degree
094        ExpVector et = f.trailingExpVector();
095        if (!et.isZERO()) {
096            GenPolynomial<C> tr = pfac.valueOf(et);
097            if (logger.isInfoEnabled()) {
098                logger.info("trailing term = " + tr);
099            }
100            f = PolyUtil.<C> basePseudoDivide(f, tr);
101            R.add(new Interval<C>(pfac.coFac.getZERO()));
102        }
103        if (f.isConstant()) {
104            return R;
105        }
106        if (f.degree(0) == 1L) {
107            C z = f.monic().trailingBaseCoefficient().negate();
108            R.add(new Interval<C>(z));
109            return R;
110        }
111        //if (f.degree(0) == 2L) ... 
112        GenPolynomial<C> F = f;
113        C M = realRootBound(F); // M != 0, since >= 2
114        Interval<C> iv = new Interval<C>(M.negate(), M);
115        //System.out.println("iv = " + iv);
116        List<GenPolynomial<C>> S = sturmSequence(F);
117        //System.out.println("S = " + S);
118        //System.out.println("f_S = " + S.get(0));
119        List<Interval<C>> Rp = realRoots(iv, S);
120        if (logger.isInfoEnabled() && !(((Object) f.ring.coFac) instanceof BigRational)) {
121            //logger.info("realRoots bound: " + iv);
122            logger.info("realRoots: " + Rp);
123        }
124        R.addAll(Rp);
125        return R;
126    }
127
128
129    /**
130     * Isolating intervals for the real roots.
131     * @param iv interval with f(left) * f(right) != 0.
132     * @param S sturm sequence for f and I.
133     * @return a list of isolating intervals for the real roots of f in I.
134     */
135    public List<Interval<C>> realRoots(Interval<C> iv, List<GenPolynomial<C>> S) {
136        List<Interval<C>> R = new ArrayList<Interval<C>>();
137        GenPolynomial<C> f = S.get(0); // squarefree part
138        if (f.isZERO()) {
139            C z = f.leadingBaseCoefficient();
140            if (!iv.contains(z)) {
141                throw new IllegalArgumentException(
142                                                   "root not in interval: f = " + f + ", iv = " + iv + ", z = " + z);
143            }
144            Interval<C> iv1 = new Interval<C>(z);
145            R.add(iv1);
146            return R;
147        }
148        if (f.isConstant()) {
149            return R;
150            //throw new IllegalArgumentException("f has no root: f = " + f + ", iv = " + iv);
151        }
152        if (f.degree(0) == 1L) {
153            C z = f.monic().trailingBaseCoefficient().negate();
154            if (!iv.contains(z)) {
155                return R;
156                //throw new IllegalArgumentException("root not in interval: f = " + f + ", iv = " + iv + ", z = " + z);
157            }
158            Interval<C> iv1 = new Interval<C>(z);
159            R.add(iv1);
160            return R;
161        }
162        //System.out.println("iv = " + iv);
163        // check sign variations at interval bounds
164        long v = realRootCount(iv, S);
165        //System.out.println("v = " + v);
166        if (v == 0) {
167            return R;
168        }
169        if (v == 1) {
170            iv = excludeZero(iv, S);
171            R.add(iv);
172            return R;
173        }
174        // now v &gt; 1
175        // bi-sect interval, such that f(c) != 0
176        C c = bisectionPoint(iv, f);
177        //System.out.println("c = " + c);
178        // recursion on both sub-intervals
179        Interval<C> iv1 = new Interval<C>(iv.left, c);
180        Interval<C> iv2 = new Interval<C>(c, iv.right);
181        List<Interval<C>> R1 = realRoots(iv1, S);
182        //System.out.println("R1 = " + R1);
183        if (debug) {
184            logger.info("R1 = " + R1);
185        }
186        List<Interval<C>> R2 = realRoots(iv2, S);
187        //System.out.println("R2 = " + R2);
188        if (debug) {
189            logger.info("R2 = " + R2);
190        }
191
192        // refine isolating intervals if adjacent 
193        if (R1.isEmpty()) {
194            R.addAll(R2);
195            return R;
196        }
197        if (R2.isEmpty()) {
198            R.addAll(R1);
199            return R;
200        }
201        iv1 = R1.get(R1.size() - 1); // last
202        iv2 = R2.get(0); // first
203        if (iv1.right.compareTo(iv2.left) < 0) {
204            R.addAll(R1);
205            R.addAll(R2);
206            return R;
207        }
208        // now iv1.right == iv2.left
209        //System.out.println("iv1 = " + iv1);
210        //System.out.println("iv2 = " + iv2);
211        R1.remove(iv1);
212        R2.remove(iv2);
213        while (iv1.right.equals(iv2.left)) {
214            C d1 = bisectionPoint(iv1, f);
215            C d2 = bisectionPoint(iv2, f);
216            Interval<C> iv11 = new Interval<C>(iv1.left, d1);
217            Interval<C> iv12 = new Interval<C>(d1, iv1.right);
218            Interval<C> iv21 = new Interval<C>(iv2.left, d2);
219            Interval<C> iv22 = new Interval<C>(d2, iv2.right);
220
221            boolean b11 = signChange(iv11, f);
222            boolean b12 = signChange(iv12, f); // TODO check unnecessary
223            //boolean b21 = signChange(iv21, f); // TODO check unused or unnecessary
224            boolean b22 = signChange(iv22, f);
225            if (b11) {
226                iv1 = iv11;
227                if (b22) {
228                    iv2 = iv22;
229                } else {
230                    iv2 = iv21;
231                }
232                break; // done, refine
233            }
234            if (b22) {
235                iv2 = iv22;
236                if (b12) {
237                    iv1 = iv12;
238                } else {
239                    iv1 = iv11;
240                }
241                break; // done, refine
242            }
243            iv1 = iv12;
244            iv2 = iv21;
245            //System.out.println("iv1 = " + iv1);
246            //System.out.println("iv2 = " + iv2);
247        }
248        R.addAll(R1);
249        R.add(iv1);
250        R.add(iv2);
251        R.addAll(R2);
252        return R;
253    }
254
255
256    /**
257     * Number of real roots in interval.
258     * @param iv interval with f(left) * f(right) != 0.
259     * @param S sturm sequence for f and I.
260     * @return number of real roots of f in I.
261     */
262    public long realRootCount(Interval<C> iv, List<GenPolynomial<C>> S) {
263        // check sign variations at interval bounds
264        GenPolynomial<C> f = S.get(0); // squarefree part
265        //System.out.println("iv = " + iv);
266        RingFactory<C> cfac = f.ring.coFac;
267        List<C> l = PolyUtil.<C> evaluateMain(cfac, S, iv.left);
268        List<C> r = PolyUtil.<C> evaluateMain(cfac, S, iv.right);
269        long v = RootUtil.<C> signVar(l) - RootUtil.<C> signVar(r);
270        //System.out.println("v = " + v);
271        if (v < 0L) {
272            v = -v;
273        }
274        return v;
275    }
276
277
278    /**
279     * Number of real roots in interval.
280     * @param iv interval with f(left) * f(right) != 0.
281     * @param f univariate polynomial.
282     * @return number of real roots of f in I.
283     */
284    @Override
285    public long realRootCount(Interval<C> iv, GenPolynomial<C> f) {
286        if (f == null || f.isConstant()) { // ? 
287            return 0L;
288        }
289        if (f.isZERO()) {
290            C z = f.leadingBaseCoefficient();
291            if (!iv.contains(z)) {
292                return 0L;
293            }
294            return 1L;
295        }
296        List<GenPolynomial<C>> S = sturmSequence(f);
297        return realRootCount(iv, S);
298    }
299
300
301    /**
302     * Invariant interval for algebraic number sign.
303     * @param iv root isolating interval for f, with f(left) * f(right) &lt; 0.
304     * @param f univariate polynomial, non-zero.
305     * @param g univariate polynomial, gcd(f,g) == 1.
306     * @return v with v a new interval contained in iv such that g(w) != 0 for w
307     *         in v.
308     */
309    @Override
310    public Interval<C> invariantSignInterval(Interval<C> iv, GenPolynomial<C> f, GenPolynomial<C> g) {
311        Interval<C> v = iv;
312        if (g == null || g.isZERO()) {
313            //throw new IllegalArgumentException("g == 0");
314            return v;
315        }
316        if (g.isConstant()) {
317            return v;
318        }
319        if (f == null || f.isZERO()) { // ? || f.isConstant()
320            throw new IllegalArgumentException("f == 0");
321            //return v;
322        }
323        List<GenPolynomial<C>> Sg = sturmSequence(g.monic());
324        Interval<C> ivp = invariantSignInterval(iv, f, Sg);
325        return ivp;
326    }
327
328
329    /**
330     * Invariant interval for algebraic number sign.
331     * @param iv root isolating interval for f, with f(left) * f(right) &lt; 0.
332     * @param f univariate polynomial, non-zero.
333     * @param Sg Sturm sequence for (f,g), a univariate polynomial with gcd(f,g) ==
334     *            1.
335     * @return v with v a new interval contained in iv such that g(w) != 0 for w
336     *         in v.
337     */
338    public Interval<C> invariantSignInterval(Interval<C> iv, GenPolynomial<C> f, List<GenPolynomial<C>> Sg) {
339        Interval<C> v = iv;
340        GenPolynomial<C> g = Sg.get(0);
341        if (g == null || g.isZERO()) {
342            return v;
343        }
344        if (g.isConstant()) {
345            return v;
346        }
347        if (f == null || f.isZERO()) { // ? || f.isConstant()
348            return v;
349        }
350        RingFactory<C> cfac = f.ring.coFac;
351        C two = cfac.fromInteger(2);
352
353        while (true) {
354            long n = realRootCount(v, Sg);
355            logger.debug("n = " + n);
356            if (n == 0) {
357                return v;
358            }
359            C c = v.left.sum(v.right);
360            c = c.divide(two);
361            Interval<C> im = new Interval<C>(c, v.right);
362            if (signChange(im, f)) {
363                v = im;
364            } else {
365                v = new Interval<C>(v.left, c);
366            }
367        }
368        // return v;
369    }
370
371
372    /**
373     * Exclude zero, old version.
374     * @param iv root isolating interval with f(left) * f(right) &lt; 0.
375     * @param S sturm sequence for f and I.
376     * @return a new interval v such that v &lt; 0 or v &gt; 0.
377     */
378    public Interval<C> excludeZeroOld(Interval<C> iv, List<GenPolynomial<C>> S) {
379        if (S == null || S.isEmpty()) {
380            return iv;
381        }
382        C zero = S.get(0).ring.coFac.getZERO();
383        if (!iv.contains(zero)) {
384            return iv;
385        }
386        Interval<C> vn = new Interval<C>(iv.left, zero);
387        if (realRootCount(vn,S) == 1) {
388            return vn;
389        }
390        vn = new Interval<C>(zero, iv.right);
391        return vn;
392    }
393
394
395    /**
396     * Exclude zero v2.
397     * @param iv root isolating interval with f(left) * f(right) &lt; 0.
398     * @param S sturm sequence for f and I.
399     * @return a new interval v such that v &lt; 0 or v &gt; 0 or v == 0.
400     */
401    public Interval<C> excludeZero(Interval<C> iv, List<GenPolynomial<C>> S) {
402        if (S == null || S.isEmpty()) {
403            return iv;
404        }
405        GenPolynomial<C> f = S.get(0);
406        C zero = f.ring.coFac.getZERO();
407        if (!iv.contains(zero)) { // left <= 0 <= right
408            return iv;
409        }
410        if (iv.left.isZERO() && iv.right.isZERO()) { // (0, 0)
411            return iv;
412        }
413        C m = realMinimalRootBound(f);
414        Interval<C> vi = iv;
415        Interval<C> vn;
416        if (vi.left.isZERO()) {
417            vi = new Interval<C>(m, vi.right);
418        } else if (vi.right.isZERO()) {
419            vi = new Interval<C>(vi.left, m.negate());
420        }
421        vn = new Interval<C>(vi.left, m.negate()); // l != 0
422        if (realRootCount(vn, S) == 1) {
423            return vn;
424        }
425        vn = new Interval<C>(m, vi.right); // r != 0
426        if (realRootCount(vn, S) == 1) {
427            return vn;
428        }
429        vn = new Interval<C>(zero);
430        logger.warn("interval is zero: iv = " + iv + ", trail = " + f.trailingExpVector().degree()
431                     + ", vi = " + vi + ", vn = " + vn);
432        return vn;
433    }
434
435}