001/*
002 * $Id: ElementaryIntegration.java 5847 2018-06-24 13:46:53Z elbarbary $
003 */
004
005package edu.jas.integrate;
006
007
008import java.util.ArrayList;
009import java.util.List;
010import java.util.SortedMap;
011
012import org.apache.log4j.Logger;
013
014import edu.jas.poly.AlgebraicNumber;
015import edu.jas.poly.AlgebraicNumberRing;
016import edu.jas.poly.GenPolynomial;
017import edu.jas.poly.GenPolynomialRing;
018import edu.jas.poly.PolyUtil;
019import edu.jas.structure.GcdRingElem;
020import edu.jas.structure.RingFactory;
021import edu.jas.ufd.FactorAbstract;
022import edu.jas.ufd.FactorFactory;
023import edu.jas.ufd.GCDFactory;
024import edu.jas.ufd.GreatestCommonDivisorAbstract;
025import edu.jas.ufd.GreatestCommonDivisorSubres;
026import edu.jas.ufd.PolyUfdUtil;
027import edu.jas.ufd.Quotient;
028import edu.jas.ufd.QuotientRing;
029import edu.jas.ufd.SquarefreeAbstract;
030import edu.jas.ufd.SquarefreeFactory;
031
032
033/**
034 * Methods related to elementary integration. In particular there are methods
035 * for Hermite reduction and Rothstein-Trager integration of the logarithmic
036 * part.
037 * 
038 * @author Axel Kramer
039 * @author Heinz Kredel
040 * @param <C> coefficient type
041 */
042
043public class ElementaryIntegration<C extends GcdRingElem<C>> {
044
045
046    private static final Logger logger = Logger.getLogger(ElementaryIntegration.class);
047
048
049    private static final boolean debug = logger.isDebugEnabled();
050
051
052    /**
053     * Engine for factorization.
054     */
055    public final FactorAbstract<C> irr;
056
057
058    /**
059     * Engine for squarefree decomposition.
060     */
061    public final SquarefreeAbstract<C> sqf;
062
063
064    /**
065     * Engine for greatest common divisors.
066     */
067    public final GreatestCommonDivisorAbstract<C> ufd;
068
069
070    /**
071     * Flag for irreducible input to integrateLogPart.
072     */
073    public boolean irredLogPart = true;
074
075
076    /**
077     * Constructor.
078     */
079    public ElementaryIntegration(RingFactory<C> br) {
080        ufd = GCDFactory.<C> getProxy(br);
081        sqf = SquarefreeFactory.<C> getImplementation(br);
082        irr = /*(FactorAbsolute<C>)*/FactorFactory.<C> getImplementation(br);
083        irredLogPart = true;
084    }
085
086
087    /**
088     * Integration of a rational function.
089     * @param r rational function
090     * @return Integral container, such that integrate(r) = sum_i(g_i) + sum_j(
091     *         an_j log(hd_j) )
092     */
093    public QuotIntegral<C> integrate(Quotient<C> r) {
094        Integral<C> integral = integrate(r.num, r.den);
095        return new QuotIntegral<C>(r.ring, integral);
096    }
097
098
099    /**
100     * Integration of a rational function.
101     * @param a numerator
102     * @param d denominator
103     * @return Integral container, such that integrate(a/d) = sum_i(gn_i/gd_i) +
104     *         integrate(h0) + sum_j( an_j log(hd_j) )
105     */
106    public Integral<C> integrate(GenPolynomial<C> a, GenPolynomial<C> d) {
107        if (d == null || a == null || d.isZERO()) {
108            throw new IllegalArgumentException("zero or null not allowed");
109        }
110        if (a.isZERO()) {
111            return new Integral<C>(a, d, a);
112        }
113        if (d.isONE()) {
114            GenPolynomial<C> pi = PolyUtil.<C> baseIntegral(a);
115            return new Integral<C>(a, d, pi);
116        }
117        GenPolynomialRing<C> pfac = d.ring;
118        if (pfac.nvar > 1) {
119            throw new IllegalArgumentException("only for univariate polynomials " + pfac);
120        }
121        if (!pfac.coFac.isField()) {
122            throw new IllegalArgumentException("only for field coefficients " + pfac);
123        }
124
125        GenPolynomial<C>[] qr = PolyUtil.<C> basePseudoQuotientRemainder(a, d);
126        GenPolynomial<C> p = qr[0];
127        GenPolynomial<C> r = qr[1];
128
129        GenPolynomial<C> c = ufd.gcd(r, d);
130        if (!c.isONE()) {
131            r = PolyUtil.<C> basePseudoQuotientRemainder(r, c)[0];
132            d = PolyUtil.<C> basePseudoQuotientRemainder(d, c)[0];
133        }
134        List<GenPolynomial<C>>[] ih = integrateHermite(r, d);
135        List<GenPolynomial<C>> rat = ih[0];
136        List<GenPolynomial<C>> log = ih[1];
137
138        GenPolynomial<C> pp = log.remove(0);
139        p = p.sum(pp);
140        GenPolynomial<C> pi = PolyUtil.<C> baseIntegral(p);
141
142        if (debug) {
143            logger.debug("pi  = " + pi);
144            logger.debug("rat = " + rat);
145            logger.debug("log = " + log);
146        }
147        if (log.size() == 0) {
148            return new Integral<C>(a, d, pi, rat);
149        }
150
151        List<LogIntegral<C>> logi = new ArrayList<LogIntegral<C>>(log.size() / 2);
152        for (int i = 0; i < log.size(); i++) {
153            GenPolynomial<C> ln = log.get(i++);
154            GenPolynomial<C> ld = log.get(i);
155            LogIntegral<C> pf = integrateLogPartPrepare(ln, ld);
156            logi.add(pf);
157        }
158        if (debug) {
159            logger.debug("logi = " + logi);
160        }
161        return new Integral<C>(a, d, pi, rat, logi);
162    }
163
164
165    /**
166     * Integration of the rational part, Hermite reduction step.
167     * @param a numerator
168     * @param d denominator, gcd(a,d) == 1
169     * @return [ [ gn_i, gd_i ], [ h0, hn_j, hd_j ] ] such that integrate(a/d) =
170     *         sum_i(gn_i/gd_i) + integrate(h0) + sum_j( integrate(hn_j/hd_j) )
171     */
172    @SuppressWarnings({ "unchecked", "cast" })
173    public List<GenPolynomial<C>>[] integrateHermite(GenPolynomial<C> a, GenPolynomial<C> d) {
174        if (d == null || d.isZERO()) {
175            throw new IllegalArgumentException("d == null or d == 0");
176        }
177        if (a == null || a.isZERO()) {
178            throw new IllegalArgumentException("a == null or a == 0");
179        }
180
181        // get squarefree decomposition
182        SortedMap<GenPolynomial<C>, Long> sfactors = sqf.squarefreeFactors(d);
183
184        List<GenPolynomial<C>> D = new ArrayList<GenPolynomial<C>>(sfactors.keySet());
185        List<GenPolynomial<C>> DP = new ArrayList<GenPolynomial<C>>();
186        for (GenPolynomial<C> f : D) {
187            long e = sfactors.get(f);
188            GenPolynomial<C> dp = f.power(e); //Power.<GenPolynomial<C>> positivePower(f, e);
189            DP.add(dp);
190        }
191        //System.out.println("D:      " + D);
192        //System.out.println("DP:     " + DP);
193
194        // get partial fraction decompostion 
195        List<GenPolynomial<C>> Ai = ufd.basePartialFraction(a, DP);
196        //System.out.println("Ai:     " + Ai);
197
198        List<GenPolynomial<C>> G = new ArrayList<GenPolynomial<C>>();
199        List<GenPolynomial<C>> H = new ArrayList<GenPolynomial<C>>();
200        H.add(Ai.remove(0)); // P
201
202        GenPolynomialRing<C> fac = d.ring;
203        int i = 0;
204        for (GenPolynomial<C> v : D) {
205            //System.out.println("V:" + v.toString());
206            GenPolynomial<C> Ak = Ai.get(i++);
207            //System.out.println("Ak:  " + Ak.toString());
208            int k = sfactors.get(v).intValue(); // assert low power
209            for (int j = k - 1; j >= 1; j--) {
210                //System.out.println("Step(" + k + "," + j + ")");
211                GenPolynomial<C> DV_dx = PolyUtil.<C> baseDeriviative(v);
212                GenPolynomial<C> Aik = Ak.divide(fac.fromInteger(-j));
213                GenPolynomial<C>[] BC = ufd.baseGcdDiophant(DV_dx, v, Aik);
214                GenPolynomial<C> b = BC[0];
215                GenPolynomial<C> c = BC[1];
216                GenPolynomial<C> vj = v.power(j);
217                G.add(b); // B
218                G.add(vj); // v^j
219                Ak = fac.fromInteger(-j).multiply(c).subtract(PolyUtil.<C> baseDeriviative(b));
220                //System.out.println("B:   " + b.toString());
221                //System.out.println("C:   " + c.toString());
222            }
223            //System.out.println("V:" + v.toString());
224            //System.out.println("Ak:  " + Ak.toString());
225            if (!Ak.isZERO()) {
226                H.add(Ak); // A_k
227                H.add(v); // v
228            }
229        }
230        List<GenPolynomial<C>>[] ret = (List<GenPolynomial<C>>[]) new List[2];
231        ret[0] = G;
232        ret[1] = H;
233        return ret;
234    }
235
236
237    /**
238     * Univariate GenPolynomial integration of the logaritmic part, eventual
239     * preparation for irreducible factorization of P.
240     * @param A univariate GenPolynomial, deg(A) < deg(P).
241     * @param P univariate squarefree GenPolynomial, gcd(A,P) == 1.
242     * @return logarithmic part container.
243     */
244    public LogIntegral<C> integrateLogPartPrepare(GenPolynomial<C> A, GenPolynomial<C> P) {
245        if (!irredLogPart) {
246            return integrateLogPart(A, P);
247        }
248        if (P == null || P.isZERO()) {
249            throw new IllegalArgumentException(" P == null or P == 0");
250        }
251        if (A == null || A.isZERO()) {
252            throw new IllegalArgumentException(" A == null or A == 0");
253        }
254        //System.out.println("\nP_base_algeb_part = " + P);
255        GenPolynomialRing<C> pfac = P.ring; // K[x]
256        if (pfac.nvar > 1) {
257            throw new IllegalArgumentException("only for univariate polynomials " + pfac);
258        }
259        if (!pfac.coFac.isField()) {
260            throw new IllegalArgumentException("only for field coefficients " + pfac);
261        }
262        List<C> cfactors = new ArrayList<C>();
263        List<GenPolynomial<C>> cdenom = new ArrayList<GenPolynomial<C>>();
264        List<AlgebraicNumber<C>> afactors = new ArrayList<AlgebraicNumber<C>>();
265        List<GenPolynomial<AlgebraicNumber<C>>> adenom = new ArrayList<GenPolynomial<AlgebraicNumber<C>>>();
266
267        // P linear
268        if (P.degree(0) <= 1) {
269            cfactors.add(A.leadingBaseCoefficient());
270            cdenom.add(P);
271            return new LogIntegral<C>(A, P, cfactors, cdenom, afactors, adenom);
272        }
273        List<GenPolynomial<C>> Pfac = irr.baseFactorsSquarefree(P);
274        //System.out.println("\nPfac = " + Pfac);
275
276        List<GenPolynomial<C>> Afac = ufd.basePartialFraction(A, Pfac);
277
278        GenPolynomial<C> A0 = Afac.remove(0);
279        if (!A0.isZERO()) {
280            throw new RuntimeException(" A0 != 0: deg(A)>= deg(P)");
281        }
282
283        // algebraic and linear factors
284        int i = 0;
285        for (GenPolynomial<C> pi : Pfac) {
286            GenPolynomial<C> ai = Afac.get(i++);
287            if (pi.degree(0) <= 1) {
288                cfactors.add(ai.leadingBaseCoefficient());
289                cdenom.add(pi);
290                continue;
291            }
292            LogIntegral<C> pf = integrateLogPart(ai, pi);
293            cfactors.addAll(pf.cfactors);
294            cdenom.addAll(pf.cdenom);
295            afactors.addAll(pf.afactors);
296            adenom.addAll(pf.adenom);
297        }
298        return new LogIntegral<C>(A, P, cfactors, cdenom, afactors, adenom);
299    }
300
301
302    /**
303     * Univariate GenPolynomial integration of the logaritmic part,
304     * Rothstein-Trager algorithm.
305     * @param A univariate GenPolynomial, deg(A) < deg(P).
306     * @param P univariate squarefree or irreducible GenPolynomial. // gcd(A,P)
307     *            == 1 automatic
308     * @return logarithmic part container.
309     */
310    public LogIntegral<C> integrateLogPart(GenPolynomial<C> A, GenPolynomial<C> P) {
311        if (P == null || P.isZERO()) {
312            throw new IllegalArgumentException("P == null or P == 0");
313        }
314        //System.out.println("\nP_base_algeb_part = " + P);
315        GenPolynomialRing<C> pfac = P.ring; // K[x]
316        if (pfac.nvar > 1) {
317            throw new IllegalArgumentException("only for univariate polynomials " + pfac);
318        }
319        if (!pfac.coFac.isField()) {
320            throw new IllegalArgumentException("only for field coefficients " + pfac);
321        }
322        List<C> cfactors = new ArrayList<C>();
323        List<GenPolynomial<C>> cdenom = new ArrayList<GenPolynomial<C>>();
324        List<AlgebraicNumber<C>> afactors = new ArrayList<AlgebraicNumber<C>>();
325        List<GenPolynomial<AlgebraicNumber<C>>> adenom = new ArrayList<GenPolynomial<AlgebraicNumber<C>>>();
326
327        // P linear
328        if (P.degree(0) <= 1) {
329            cfactors.add(A.leadingBaseCoefficient());
330            cdenom.add(P);
331            return new LogIntegral<C>(A, P, cfactors, cdenom, afactors, adenom);
332        }
333
334        // deriviative
335        GenPolynomial<C> Pp = PolyUtil.<C> baseDeriviative(P);
336        //no: Pp = Pp.monic();
337        //System.out.println("\nP  = " + P);
338        //System.out.println("Pp = " + Pp);
339
340        // Q[t]
341        String[] vars = new String[] { "t" };
342        GenPolynomialRing<C> cfac = new GenPolynomialRing<C>(pfac.coFac, 1, pfac.tord, vars);
343        GenPolynomial<C> t = cfac.univariate(0);
344        //System.out.println("t = " + t);
345
346        // Q[x][t]
347        GenPolynomialRing<GenPolynomial<C>> rfac = new GenPolynomialRing<GenPolynomial<C>>(pfac, cfac); // sic
348        //System.out.println("rfac = " + rfac.toScript());
349
350        // transform polynomials to bi-variate polynomial
351        GenPolynomial<GenPolynomial<C>> Ac = PolyUfdUtil.<C> introduceLowerVariable(rfac, A);
352        //System.out.println("Ac = " + Ac);
353        GenPolynomial<GenPolynomial<C>> Pc = PolyUfdUtil.<C> introduceLowerVariable(rfac, P);
354        //System.out.println("Pc = " + Pc);
355        GenPolynomial<GenPolynomial<C>> Pcp = PolyUfdUtil.<C> introduceLowerVariable(rfac, Pp);
356        //System.out.println("Pcp = " + Pcp);
357
358        // Q[t][x]
359        GenPolynomialRing<GenPolynomial<C>> rfac1 = Pc.ring;
360        //System.out.println("rfac1 = " + rfac1.toScript());
361
362        // A - t P'
363        GenPolynomial<GenPolynomial<C>> tc = rfac1.getONE().multiply(t);
364        //System.out.println("tc = " + tc);
365        GenPolynomial<GenPolynomial<C>> At = Ac.subtract(tc.multiply(Pcp));
366        //System.out.println("At = " + At);
367
368        GreatestCommonDivisorSubres<C> engine = new GreatestCommonDivisorSubres<C>();
369        // = GCDFactory.<C>getImplementation( cfac.coFac );
370        GreatestCommonDivisorAbstract<AlgebraicNumber<C>> aengine = null;
371
372        GenPolynomial<GenPolynomial<C>> Rc = engine.recursiveUnivariateResultant(Pc, At);
373        //System.out.println("Rc = " + Rc);
374        GenPolynomial<C> res = Rc.leadingBaseCoefficient();
375        //no: res = res.monic();
376        //System.out.println("\nres = " + res);
377
378        SortedMap<GenPolynomial<C>, Long> resfac = irr.baseFactors(res);
379        //System.out.println("resfac = " + resfac + "\n");
380
381        for (GenPolynomial<C> r : resfac.keySet()) {
382            //System.out.println("\nr(t) = " + r);
383            if (r.isConstant()) {
384                continue;
385            }
386            //vars = new String[] { "z_" + Math.abs(r.hashCode() % 1000) };
387            vars = pfac.newVars("z_");
388            pfac = pfac.copy();
389            @SuppressWarnings("unused")
390            String[] unused = pfac.setVars(vars);
391            r = pfac.copy(r); // hack to exchange the variables
392            //System.out.println("r(z_) = " + r);
393            AlgebraicNumberRing<C> afac = new AlgebraicNumberRing<C>(r, true); // since irreducible
394            logger.debug("afac = " + afac.toScript());
395            AlgebraicNumber<C> a = afac.getGenerator();
396            //no: a = a.negate();
397            //System.out.println("a = " + a);
398
399            // K(alpha)[x]
400            GenPolynomialRing<AlgebraicNumber<C>> pafac = new GenPolynomialRing<AlgebraicNumber<C>>(afac,
401                            Pc.ring);
402            //System.out.println("pafac = " + pafac.toScript());
403
404            // convert to K(alpha)[x]
405            GenPolynomial<AlgebraicNumber<C>> Pa = PolyUtil.<C> convertToAlgebraicCoefficients(pafac, P);
406            //System.out.println("Pa = " + Pa);
407            GenPolynomial<AlgebraicNumber<C>> Pap = PolyUtil.<C> convertToAlgebraicCoefficients(pafac, Pp);
408            //System.out.println("Pap = " + Pap);
409            GenPolynomial<AlgebraicNumber<C>> Aa = PolyUtil.<C> convertToAlgebraicCoefficients(pafac, A);
410            //System.out.println("Aa = " + Aa);
411
412            // A - a P'
413            GenPolynomial<AlgebraicNumber<C>> Ap = Aa.subtract(Pap.multiply(a));
414            //System.out.println("Ap = " + Ap);
415
416            if (aengine == null) {
417                aengine = GCDFactory.<AlgebraicNumber<C>> getImplementation(afac);
418            }
419            GenPolynomial<AlgebraicNumber<C>> Ga = aengine.baseGcd(Pa, Ap);
420            //System.out.println("Ga = " + Ga);
421            if (Ga.isConstant()) {
422                //System.out.println("warning constant gcd ignored");
423                continue;
424            }
425            // If a is equal to zero
426            if (a.isZERO()) {
427                continue;
428            }
429            afactors.add(a);
430            adenom.add(Ga);
431            // special quadratic case
432            // todo: eventually implement special cases deg = 3, 4
433        }
434        return new LogIntegral<C>(A, P, cfactors, cdenom, afactors, adenom);
435    }
436
437
438    /**
439     * Derivation of a univariate rational function.
440     * @param r rational function
441     * @return dr/dx
442     */
443    public Quotient<C> deriviative(Quotient<C> r) {
444        GenPolynomial<C> num = r.num;
445        GenPolynomial<C> den = r.den;
446        GenPolynomial<C> nump = PolyUtil.<C> baseDeriviative(num);
447        if (den.isONE()) {
448            return new Quotient<C>(r.ring, nump, den);
449        }
450        GenPolynomial<C> denp = PolyUtil.<C> baseDeriviative(den);
451
452        GenPolynomial<C> n = den.multiply(nump).subtract(num.multiply(denp));
453        GenPolynomial<C> d = den.multiply(den);
454
455        Quotient<C> der = new Quotient<C>(r.ring, n, d);
456        return der;
457    }
458
459
460    /**
461     * Test of integration of a rational function.
462     * @param ri integral
463     * @return true, if ri is an integral, else false.
464     */
465    public boolean isIntegral(QuotIntegral<C> ri) {
466        Quotient<C> r = ri.quot;
467        QuotientRing<C> qr = r.ring;
468        Quotient<C> i = r.ring.getZERO();
469        for (Quotient<C> q : ri.rational) {
470            Quotient<C> qd = deriviative(q);
471            i = i.sum(qd);
472        }
473        if (ri.logarithm.size() == 0) {
474            return r.equals(i);
475        }
476        for (LogIntegral<C> li : ri.logarithm) {
477            Quotient<C> q = new Quotient<C>(qr, li.num, li.den);
478            i = i.sum(q);
479        }
480        boolean t = r.equals(i);
481        if (!t) {
482            return false;
483        }
484        for (LogIntegral<C> li : ri.logarithm) {
485            t = isIntegral(li);
486            if (!t) {
487                return false;
488            }
489        }
490        return true;
491    }
492
493
494    /**
495     * Test of integration of the logarithmic part of a rational function.
496     * @param rl logarithmic part of an integral
497     * @return true, if rl is an integral, else false.
498     */
499    public boolean isIntegral(LogIntegral<C> rl) {
500        QuotientRing<C> qr = new QuotientRing<C>(rl.den.ring);
501        Quotient<C> r = new Quotient<C>(qr, rl.num, rl.den);
502        Quotient<C> i = qr.getZERO();
503        int j = 0;
504        for (GenPolynomial<C> d : rl.cdenom) {
505            GenPolynomial<C> dp = PolyUtil.<C> baseDeriviative(d);
506            dp = dp.multiply(rl.cfactors.get(j++));
507            Quotient<C> f = new Quotient<C>(qr, dp, d);
508            i = i.sum(f);
509        }
510        if (rl.afactors.size() == 0) {
511            return r.equals(i);
512        }
513        r = r.subtract(i);
514        QuotientRing<AlgebraicNumber<C>> aqr = new QuotientRing<AlgebraicNumber<C>>(rl.adenom.get(0).ring);
515        Quotient<AlgebraicNumber<C>> ai = aqr.getZERO();
516
517        GenPolynomial<AlgebraicNumber<C>> aqn = PolyUtil.<C> convertToAlgebraicCoefficients(aqr.ring, r.num);
518        GenPolynomial<AlgebraicNumber<C>> aqd = PolyUtil.<C> convertToAlgebraicCoefficients(aqr.ring, r.den);
519        Quotient<AlgebraicNumber<C>> ar = new Quotient<AlgebraicNumber<C>>(aqr, aqn, aqd);
520        j = 0;
521        for (GenPolynomial<AlgebraicNumber<C>> d : rl.adenom) {
522            GenPolynomial<AlgebraicNumber<C>> dp = PolyUtil.<AlgebraicNumber<C>> baseDeriviative(d);
523            dp = dp.multiply(rl.afactors.get(j++));
524            Quotient<AlgebraicNumber<C>> f = new Quotient<AlgebraicNumber<C>>(aqr, dp, d);
525            ai = ai.sum(f);
526        }
527        boolean t = ar.equals(ai);
528        if (t) {
529            return true;
530        }
531        logger.warn("log integral not verified");
532        //System.out.println("r        = " + r);
533        //System.out.println("afactors = " + rl.afactors);
534        //System.out.println("adenom   = " + rl.adenom);
535        //System.out.println("ar       = " + ar);
536        //System.out.println("ai       = " + ai);
537        return true;
538    }
539
540}