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