001/*
002 * $Id: PartialFraction.java 5810 2018-04-23 20:41:35Z kredel $
003 */
004
005package edu.jas.ufd;
006
007
008import java.io.Serializable;
009import java.util.ArrayList;
010import java.util.HashMap;
011import java.util.HashSet;
012import java.util.List;
013import java.util.Map;
014import java.util.Set;
015
016import org.apache.log4j.Logger;
017
018import edu.jas.poly.AlgebraicNumber;
019import edu.jas.poly.AlgebraicNumberRing;
020import edu.jas.poly.GenPolynomial;
021import edu.jas.poly.GenPolynomialRing;
022import edu.jas.poly.PolyUtil;
023import edu.jas.structure.GcdRingElem;
024
025
026/**
027 * Container for the partial fraction decomposition of a squarefree denominator.
028 * num/den = sum( a_i / d_i )
029 * @author Heinz Kredel
030 * @param <C> coefficient type
031 */
032
033public class PartialFraction<C extends GcdRingElem<C>> implements Serializable {
034
035
036    private static final Logger logger = Logger.getLogger(PartialFraction.class);
037
038
039    /**
040     * Original numerator polynomial coefficients from C and deg(num) &lt;
041     * deg(den).
042     */
043    public final GenPolynomial<C> num;
044
045
046    /**
047     * Original (irreducible) denominator polynomial coefficients from C.
048     */
049    public final GenPolynomial<C> den;
050
051
052    /**
053     * List of numbers from C.
054     */
055    public final List<C> cfactors;
056
057
058    /**
059     * List of linear factors of the denominator with coefficients from C.
060     */
061    public final List<GenPolynomial<C>> cdenom;
062
063
064    /**
065     * List of algebraic numbers of an algebraic field extension over C.
066     */
067    public final List<AlgebraicNumber<C>> afactors;
068
069
070    /**
071     * List of factors of the denominator with coefficients from an
072     * AlgebraicNumberRing&lt;C&gt;.
073     */
074    public final List<GenPolynomial<AlgebraicNumber<C>>> adenom;
075
076
077    /**
078     * Constructor.
079     * @param n numerator GenPolynomial over C.
080     * @param d irreducible denominator GenPolynomial over C.
081     * @param cf list of elements a_i.
082     * @param cd list of linear factors d_i of d.
083     * @param af list of algebraic elements a_i.
084     * @param ad list of linear (irreducible) factors d_i of d with algebraic
085     *            coefficients. n/d = sum( a_i / d_i )
086     */
087    public PartialFraction(GenPolynomial<C> n, GenPolynomial<C> d, List<C> cf, List<GenPolynomial<C>> cd,
088                    List<AlgebraicNumber<C>> af, List<GenPolynomial<AlgebraicNumber<C>>> ad) {
089        num = n;
090        den = d;
091        cfactors = cf;
092        cdenom = cd;
093        afactors = af;
094        adenom = ad;
095        for (GenPolynomial<C> p : cdenom) {
096            if (p.degree(0) > 1) {
097                throw new IllegalArgumentException("polynomial not linear, p = " + p);
098            }
099        }
100        for (GenPolynomial<AlgebraicNumber<C>> a : adenom) {
101            if (a.degree(0) > 1) {
102                throw new IllegalArgumentException("polynomial not linear, a = " + a);
103            }
104        }
105    }
106
107
108    /**
109     * Get the String representation.
110     * @see java.lang.Object#toString()
111     */
112    @Override
113    public String toString() {
114        StringBuffer sb = new StringBuffer();
115        sb.append("(" + num.toString() + ")");
116        sb.append(" / ");
117        sb.append("(" + den.toString() + ")");
118        sb.append(" =\n");
119        boolean first = true;
120        for (int i = 0; i < cfactors.size(); i++) {
121            C cp = cfactors.get(i);
122            if (first) {
123                first = false;
124            } else {
125                sb.append(" + ");
126            }
127            sb.append("(" + cp.toString() + ")");
128            GenPolynomial<C> p = cdenom.get(i);
129            sb.append(" / (" + p.toString() + ")");
130        }
131        if (!first && afactors.size() > 0) {
132            sb.append(" + ");
133        }
134        first = true;
135        for (int i = 0; i < afactors.size(); i++) {
136            if (first) {
137                first = false;
138            } else {
139                sb.append(" + ");
140            }
141            AlgebraicNumber<C> ap = afactors.get(i);
142            AlgebraicNumberRing<C> ar = ap.factory();
143            GenPolynomial<AlgebraicNumber<C>> p = adenom.get(i);
144            if (p.degree(0) < ar.modul.degree(0) && ar.modul.degree(0) > 2) {
145                sb.append("sum_(" + ar.getGenerator() + " in ");
146                sb.append("rootOf(" + ar.modul + ") ) ");
147            } else {
148                //sb.append("sum_("+ar+") ");
149            }
150            sb.append("(" + ap.toString() + ")");
151            sb.append(" / (" + p.toString() + ")");
152            //sb.append(" ## over " + ap.factory() + "\n");
153        }
154        return sb.toString();
155    }
156
157
158    /**
159     * Get a scripting compatible string representation.
160     * @return script compatible representation for this container.
161     * @see edu.jas.structure.ElemFactory#toScript()
162     */
163    public String toScript() {
164        // Python case
165        StringBuffer sb = new StringBuffer();
166        sb.append(num.toScript());
167        sb.append(" / ");
168        sb.append(den.toScript());
169        sb.append(" = ");
170        boolean first = true;
171        int i = 0;
172        for (C cp : cfactors) {
173            if (first) {
174                first = false;
175            } else {
176                sb.append(" + ");
177            }
178            sb.append(cp.toScript());
179            GenPolynomial<C> p = cdenom.get(i);
180            sb.append(" / " + p.toScript());
181        }
182        if (!first) {
183            sb.append(" + ");
184        }
185        first = true;
186        i = 0;
187        for (AlgebraicNumber<C> ap : afactors) {
188            if (first) {
189                first = false;
190            } else {
191                sb.append(" + ");
192            }
193            AlgebraicNumberRing<C> ar = ap.factory();
194            GenPolynomial<AlgebraicNumber<C>> p = adenom.get(i);
195            if (p.degree(0) < ar.modul.degree(0) && ar.modul.degree(0) > 2) {
196                sb.append("sum_(" + ar.getGenerator().toScript() + " in ");
197                sb.append("rootOf(" + ar.modul.toScript() + ") ) ");
198            } else {
199                //sb.append("sum_("+ar+") ");
200            }
201            sb.append(ap.toScript());
202            sb.append(" / " + p.toScript());
203            //sb.append(" ## over " + ap.toScriptFactory() + "\n");
204        }
205        return sb.toString();
206    }
207
208
209    /**
210     * Hash code for this Factors.
211     * @see java.lang.Object#hashCode()
212     */
213    @Override
214    public int hashCode() {
215        int h = num.hashCode();
216        h = h * 37 + den.hashCode();
217        h = h * 37 + cfactors.hashCode();
218        h = h * 37 + cdenom.hashCode();
219        h = h * 37 + afactors.hashCode();
220        h = h * 37 + adenom.hashCode();
221        return h;
222    }
223
224
225    /**
226     * Comparison with any other object.
227     * @see java.lang.Object#equals(java.lang.Object)
228     */
229    @Override
230    @SuppressWarnings("unchecked")
231    public boolean equals(Object B) {
232        if (B == null) {
233            return false;
234        }
235        if (!(B instanceof PartialFraction)) {
236            return false;
237        }
238        PartialFraction<C> a = (PartialFraction<C>) B;
239        boolean t = num.equals(a.num) && den.equals(a.den);
240        if (!t) {
241            return t;
242        }
243        t = cfactors.equals(a.cfactors);
244        if (!t) {
245            return t;
246        }
247        t = cdenom.equals(a.cdenom);
248        if (!t) {
249            return t;
250        }
251        t = afactors.equals(a.afactors);
252        if (!t) {
253            return t;
254        }
255        t = adenom.equals(a.adenom);
256        return t;
257    }
258
259
260    /**
261     * Test if correct partial fraction. num/den = sum( a_i / d_i )
262     */
263    @SuppressWarnings("unchecked")
264    public boolean isPartialFraction() {
265        QuotientRing<C> qfac = new QuotientRing<C>(num.ring);
266        // num / den
267        Quotient<C> q = new Quotient<C>(qfac, num, den);
268        //System.out.println("q = " + q);
269        Quotient<C> qs = qfac.getZERO();
270        int i = 0;
271        for (C c : cfactors) {
272            GenPolynomial<C> cp = cdenom.get(i++);
273            // plus c / cp
274            GenPolynomial<C> cd = num.ring.getONE().multiply(c);
275            Quotient<C> qq = new Quotient<C>(qfac, cd, cp);
276            qs = qs.sum(qq);
277        }
278        //System.out.println("qs = " + qs);
279        if (afactors.isEmpty()) {
280            return q.compareTo(qs) == 0;
281        }
282
283        // sort by extension field
284        Set<AlgebraicNumberRing<C>> fields = new HashSet<AlgebraicNumberRing<C>>();
285        for (AlgebraicNumber<C> ap : afactors) {
286            if (ap.ring.depth() > 1) {
287                logger.warn("extension field depth to high"); // todo
288            }
289            fields.add(ap.ring);
290        }
291        //System.out.println("fields = " + fields);
292        Map<AlgebraicNumberRing<C>, List<AlgebraicNumber<C>>> facs = new HashMap<AlgebraicNumberRing<C>, List<AlgebraicNumber<C>>>();
293        for (AlgebraicNumber<C> ap : afactors) {
294            List<AlgebraicNumber<C>> cf = facs.get(ap.ring);
295            if (cf == null) {
296                cf = new ArrayList<AlgebraicNumber<C>>();
297            }
298            cf.add(ap);
299            facs.put(ap.ring, cf);
300        }
301        //System.out.println("facs = " + facs);
302        Map<AlgebraicNumberRing<C>, List<GenPolynomial<AlgebraicNumber<C>>>> pfacs = new HashMap<AlgebraicNumberRing<C>, List<GenPolynomial<AlgebraicNumber<C>>>>();
303        for (GenPolynomial<AlgebraicNumber<C>> ap : adenom) {
304            AlgebraicNumberRing<C> ar = (AlgebraicNumberRing<C>) ap.ring.coFac;
305            List<GenPolynomial<AlgebraicNumber<C>>> cf = pfacs.get(ar);
306            if (cf == null) {
307                cf = new ArrayList<GenPolynomial<AlgebraicNumber<C>>>();
308            }
309            cf.add(ap);
310            pfacs.put(ar, cf);
311        }
312        //System.out.println("pfacs = " + pfacs);
313
314        // check algebraic parts 
315        boolean sumMissing = false;
316        for (AlgebraicNumberRing<C> ar : fields) {
317            if (ar.modul.degree(0) > 2) { //&& p.degree(0) < ar.modul.degree(0) ?
318                sumMissing = true;
319            }
320            List<AlgebraicNumber<C>> cf = facs.get(ar);
321            List<GenPolynomial<AlgebraicNumber<C>>> cfp = pfacs.get(ar);
322            GenPolynomialRing<AlgebraicNumber<C>> apfac = cfp.get(0).ring;
323            QuotientRing<AlgebraicNumber<C>> aqfac = new QuotientRing<AlgebraicNumber<C>>(apfac);
324            Quotient<AlgebraicNumber<C>> aq = aqfac.getZERO();
325            i = 0;
326            for (AlgebraicNumber<C> c : cf) {
327                GenPolynomial<AlgebraicNumber<C>> cp = cfp.get(i++);
328                // plus c / cp
329                GenPolynomial<AlgebraicNumber<C>> cd = apfac.getONE().multiply(c);
330                Quotient<AlgebraicNumber<C>> qq = new Quotient<AlgebraicNumber<C>>(aqfac, cd, cp);
331                //System.out.println("qq = " + qq);
332                aq = aq.sum(qq);
333            }
334            //System.out.println("aq = " + aq);
335            GenPolynomialRing<C> cfac = ar.ring;
336            GenPolynomialRing<GenPolynomial<C>> prfac = new GenPolynomialRing<GenPolynomial<C>>(cfac, apfac);
337            GenPolynomial<GenPolynomial<C>> pqnum = PolyUtil.<C> fromAlgebraicCoefficients(prfac, aq.num);
338            GenPolynomial<GenPolynomial<C>> pqden = PolyUtil.<C> fromAlgebraicCoefficients(prfac, aq.den);
339            //System.out.println("pq = (" + pqnum + ") / (" + pqden + ")");
340
341            C one = cfac.coFac.getONE(); // varaible should no more occur in coefficient
342            GenPolynomialRing<C> pfac = new GenPolynomialRing<C>(cfac.coFac, prfac);
343            GenPolynomial<C> pnum = PolyUtil.<C> evaluateFirstRec(cfac, pfac, pqnum, one);
344            GenPolynomial<C> pden = PolyUtil.<C> evaluateFirstRec(cfac, pfac, pqden, one);
345            //System.out.println("p = (" + pnum + ") / (" + pden + ")");
346
347            // iterate if multiple field extensions
348            while (cfac.coFac instanceof AlgebraicNumberRing) {
349                //System.out.println("cfac.coFac = " + cfac.coFac.toScript());
350                AlgebraicNumberRing<C> ar2 = (AlgebraicNumberRing<C>) cfac.coFac;
351                cfac = ar2.ring;
352                prfac = new GenPolynomialRing<GenPolynomial<C>>(cfac, apfac);
353                GenPolynomial<AlgebraicNumber<C>> prnum = (GenPolynomial<AlgebraicNumber<C>>) pnum;
354                GenPolynomial<AlgebraicNumber<C>> prden = (GenPolynomial<AlgebraicNumber<C>>) pden;
355                pqnum = PolyUtil.<C> fromAlgebraicCoefficients(prfac, prnum);
356                pqden = PolyUtil.<C> fromAlgebraicCoefficients(prfac, prden);
357                one = cfac.coFac.getONE(); // varaible should no more occur in coefficient
358                pfac = new GenPolynomialRing<C>(cfac.coFac, prfac);
359                pnum = PolyUtil.<C> evaluateFirstRec(cfac, pfac, pqnum, one);
360                pden = PolyUtil.<C> evaluateFirstRec(cfac, pfac, pqden, one);
361            }
362
363            Quotient<C> qq = new Quotient<C>(qfac, pnum, pden);
364            //System.out.println("qq = " + qq);
365            qs = qs.sum(qq);
366        }
367        boolean cmp = q.compareTo(qs) == 0;
368        if (!cmp) {
369            System.out.println("q != qs: " + q + " != " + qs);
370        }
371        return cmp || sumMissing;
372    }
373
374}