001/*
002 * $Id: FactorAbstract.java 5899 2018-08-15 21:34:57Z kredel $
003 */
004
005package edu.jas.ufd;
006
007
008import java.util.ArrayList;
009import java.util.List;
010import java.util.Map;
011import java.util.SortedMap;
012import java.util.SortedSet;
013import java.util.TreeMap;
014import java.util.TreeSet;
015
016import org.apache.logging.log4j.Logger;
017import org.apache.logging.log4j.LogManager; 
018
019import edu.jas.kern.TimeStatus;
020import edu.jas.kern.StringUtil;
021import edu.jas.poly.ExpVector;
022import edu.jas.poly.GenPolynomial;
023import edu.jas.poly.GenPolynomialRing;
024import edu.jas.poly.OptimizedPolynomialList;
025import edu.jas.poly.PolyUtil;
026import edu.jas.poly.TermOrderOptimization;
027import edu.jas.structure.GcdRingElem;
028import edu.jas.structure.RingFactory;
029import edu.jas.util.KsubSet;
030
031
032/**
033 * Abstract factorization algorithms class. This class contains implementations
034 * of all methods of the <code>Factorization</code> interface, except the method
035 * for factorization of a squarefree polynomial. The methods to obtain
036 * squarefree polynomials delegate the computation to the
037 * <code>GreatestCommonDivisor</code> classes and are included for convenience.
038 * @param <C> coefficient type
039 * @author Heinz Kredel
040 * @see edu.jas.ufd.FactorFactory
041 */
042
043public abstract class FactorAbstract<C extends GcdRingElem<C>> implements Factorization<C> {
044
045
046    private static final Logger logger = LogManager.getLogger(FactorAbstract.class);
047
048
049    private static final boolean debug = logger.isDebugEnabled();
050
051
052    /**
053     * Gcd engine for base coefficients.
054     */
055    protected final GreatestCommonDivisorAbstract<C> engine;
056
057
058    /**
059     * Squarefree decompositon engine for base coefficients.
060     */
061    protected final SquarefreeAbstract<C> sengine;
062
063
064    /**
065     * No argument constructor.
066     */
067    protected FactorAbstract() {
068        throw new IllegalArgumentException("don't use this constructor");
069    }
070
071
072    /**
073     * Constructor.
074     * @param cfac coefficient ring factory.
075     */
076    public FactorAbstract(RingFactory<C> cfac) {
077        engine = GCDFactory.<C> getProxy(cfac);
078        //engine = GCDFactory.<C> getImplementation(cfac);
079        sengine = SquarefreeFactory.<C> getImplementation(cfac);
080    }
081
082
083    /**
084     * Get the String representation.
085     * @see java.lang.Object#toString()
086     */
087    @Override
088    public String toString() {
089        return getClass().getName();
090    }
091
092
093    /**
094     * GenPolynomial test if is irreducible.
095     * @param P GenPolynomial.
096     * @return true if P is irreducible, else false.
097     */
098    public boolean isIrreducible(GenPolynomial<C> P) {
099        if (!isSquarefree(P)) {
100            return false;
101        }
102        List<GenPolynomial<C>> F = factorsSquarefree(P);
103        if (F.size() == 1) {
104            return true;
105        } else if (F.size() > 2) {
106            return false;
107        } else { //F.size() == 2
108            boolean cnst = false;
109            for (GenPolynomial<C> p : F) {
110                if (p.isConstant()) {
111                    cnst = true;
112                }
113            }
114            return cnst;
115        }
116    }
117
118
119    /**
120     * GenPolynomial test if a non trivial factorization exsists.
121     * @param P GenPolynomial.
122     * @return true if P is reducible, else false.
123     */
124    public boolean isReducible(GenPolynomial<C> P) {
125        return !isIrreducible(P);
126    }
127
128
129    /**
130     * GenPolynomial test if is squarefree.
131     * @param P GenPolynomial.
132     * @return true if P is squarefree, else false.
133     */
134    public boolean isSquarefree(GenPolynomial<C> P) {
135        return sengine.isSquarefree(P);
136    }
137
138
139    /**
140     * GenPolynomial factorization of a multivariate squarefree polynomial,
141     * using Kronecker substitution and variable order optimization.
142     * @param P squarefree and primitive! (respectively monic) multivariate
143     *            GenPolynomial over the ring C.
144     * @return [p_1,...,p_k] with P = prod_{i=1,...,r} p_i.
145     */
146    public List<GenPolynomial<C>> factorsSquarefreeOptimize(GenPolynomial<C> P) {
147        GenPolynomialRing<C> pfac = P.ring;
148        if (pfac.nvar <= 1) {
149            return baseFactorsSquarefree(P);
150        }
151        List<GenPolynomial<C>> topt = new ArrayList<GenPolynomial<C>>(1);
152        topt.add(P);
153        OptimizedPolynomialList<C> opt = TermOrderOptimization.<C> optimizeTermOrder(pfac, topt);
154        P = opt.list.get(0);
155        logger.info("optimized polynomial: " + P);
156        List<Integer> iperm = TermOrderOptimization.inversePermutation(opt.perm);
157        logger.info("optimize perm: " + opt.perm + ", de-optimize perm: " + iperm);
158
159        ExpVector degv = P.degreeVector();
160        int[] donv = degv.dependencyOnVariables();
161        List<GenPolynomial<C>> facs = null;
162        if (degv.length() == donv.length) { // all variables appear
163            logger.info("do.full factorsSquarefreeKronecker: " + P);
164            facs = factorsSquarefreeKronecker(P);
165        } else { // not all variables appear, remove unused variables
166            GenPolynomial<C> pu = PolyUtil.<C> removeUnusedUpperVariables(P);
167            //GenPolynomial<C> pl = PolyUtil.<C> removeUnusedLowerVariables(pu); // not useful after optimize
168            logger.info("do.sparse factorsSquarefreeKronecker: " + pu);
169            facs = factorsSquarefreeKronecker(pu); // pl
170            List<GenPolynomial<C>> fs = new ArrayList<GenPolynomial<C>>(facs.size());
171            GenPolynomialRing<C> pf = P.ring;
172            //GenPolynomialRing<C> pfu = pu.ring;
173            for (GenPolynomial<C> p : facs) {
174                //GenPolynomial<C> pel = p.extendLower(pfu, 0, 0L);
175                GenPolynomial<C> pe = p.extend(pf, 0, 0L); // pel
176                fs.add(pe);
177            }
178            //System.out.println("fs = " + fs);
179            facs = fs;
180        }
181        List<GenPolynomial<C>> iopt = TermOrderOptimization.<C> permutation(iperm, pfac, facs);
182        logger.info("de-optimized polynomials: " + iopt);
183        facs = normalizeFactorization(iopt);
184        return facs;
185    }
186
187
188    /**
189     * GenPolynomial factorization of a squarefree polynomial, using Kronecker
190     * substitution.
191     * @param P squarefree and primitive! (respectively monic) GenPolynomial.
192     * @return [p_1,...,p_k] with P = prod_{i=1,...,r} p_i.
193     */
194    @Override
195    public List<GenPolynomial<C>> factorsSquarefree(GenPolynomial<C> P) {
196        if (P != null && P.ring.nvar > 1) {
197           logger.warn("no multivariate factorization for " + P.ring.toScript() + ": falling back to Kronecker algorithm");
198           //if (P.ring.characteristic().signum() == 0) {
199           //    throw new IllegalArgumentException(this.getClass().getName() + " P.ring.characteristic().signum() == 0");
200           //}
201        }
202        //if (logger.isInfoEnabled()) {
203        //    logger.info(StringUtil.selectStackTrace("edu\\.jas.*"));
204        //}
205        return factorsSquarefreeKronecker(P);
206        //return factorsSquarefreeOptimize(P); // test only
207    }
208
209
210    /**
211     * GenPolynomial factorization of a squarefree polynomial, using Kronecker
212     * substitution.
213     * @param P squarefree and primitive! (respectively monic) GenPolynomial.
214     * @return [p_1,...,p_k] with P = prod_{i=1,...,r} p_i.
215     */
216    public List<GenPolynomial<C>> factorsSquarefreeKronecker(GenPolynomial<C> P) {
217        if (P == null) {
218            throw new IllegalArgumentException(this.getClass().getName() + " P != null");
219        }
220        GenPolynomialRing<C> pfac = P.ring;
221        if (pfac.nvar == 1) {
222            return baseFactorsSquarefree(P);
223        }
224        List<GenPolynomial<C>> factors = new ArrayList<GenPolynomial<C>>();
225        if (P.isZERO()) {
226            return factors;
227        }
228        if (P.degreeVector().totalDeg() <= 1L) {
229            factors.add(P);
230            return factors;
231        }
232        long d = P.degree() + 1L;
233        GenPolynomial<C> kr = PolyUfdUtil.<C> substituteKronecker(P, d);
234        GenPolynomialRing<C> ufac = kr.ring;
235        ufac.setVars(ufac.newVars("zz")); // side effects 
236        logger.info("deg(subs(P,d=" + d + ")) = " + kr.degree(0) + ", original degrees: " + P.degreeVector());
237        if (debug) {
238            logger.info("subs(P,d=" + d + ") = " + kr);
239            //System.out.println("subs(P,d=" + d + ") = " + kr);
240        }
241        if (kr.degree(0) > 100) {
242            logger.warn("Kronecker substitution has to high degree");
243            TimeStatus.checkTime("degree > 100");
244        }
245
246        // factor Kronecker polynomial
247        List<GenPolynomial<C>> ulist = new ArrayList<GenPolynomial<C>>();
248        // kr might not be squarefree so complete factor univariate
249        SortedMap<GenPolynomial<C>, Long> slist = baseFactors(kr);
250        if (debug && !isFactorization(kr, slist)) {
251            logger.warn("kr    = " + kr);
252            logger.warn("slist = " + slist);
253            throw new ArithmeticException("no factorization");
254        }
255        for (Map.Entry<GenPolynomial<C>, Long> me : slist.entrySet()) {
256            GenPolynomial<C> g = me.getKey();
257            long e = me.getValue(); // slist.get(g);
258            for (int i = 0; i < e; i++) { // is this really required? yes!
259                ulist.add(g);
260            }
261        }
262        //System.out.println("ulist = " + ulist);
263        if (ulist.size() == 1 && ulist.get(0).degree() == P.degree()) {
264            factors.add(P);
265            return factors;
266        }
267        //wrong: List<GenPolynomial<C>> klist = PolyUfdUtil.<C> backSubstituteKronecker(pfac, ulist, d);
268        //System.out.println("back(klist) = " + PolyUfdUtil.<C> backSubstituteKronecker(pfac, ulist, d));
269        if (logger.isInfoEnabled()) {
270            logger.info("ulist = " + ulist);
271            //System.out.println("ulist = " + ulist);
272        }
273        // combine trial factors
274        int dl = ulist.size() - 1; //(ulist.size() + 1) / 2;
275        //System.out.println("dl = " + dl);
276        int ti = 0;
277        GenPolynomial<C> u = P;
278        long deg = (u.degree() + 1L) / 2L; // max deg
279        ExpVector evl = u.leadingExpVector();
280        ExpVector evt = u.trailingExpVector();
281        //System.out.println("deg = " + deg);
282        for (int j = 1; j <= dl; j++) {
283            KsubSet<GenPolynomial<C>> ps = new KsubSet<GenPolynomial<C>>(ulist, j);
284            for (List<GenPolynomial<C>> flist : ps) {
285                //System.out.println("flist = " + flist);
286                GenPolynomial<C> utrial = ufac.getONE();
287                for (int k = 0; k < flist.size(); k++) {
288                    utrial = utrial.multiply(flist.get(k));
289                }
290                GenPolynomial<C> trial = PolyUfdUtil.<C> backSubstituteKronecker(pfac, utrial, d);
291                ti++;
292                if (ti % 2000 == 0) {
293                    logger.warn("ti(" + ti + ") ");
294                    TimeStatus.checkTime(ti + " % 2000 == 0");
295                }
296                if (!evl.multipleOf(trial.leadingExpVector())) {
297                    continue;
298                }
299                if (!evt.multipleOf(trial.trailingExpVector())) {
300                    continue;
301                }
302                if (trial.degree() > deg || trial.isConstant()) {
303                    continue;
304                }
305                trial = trial.monic();
306                if (ti % 15000 == 0) {
307                    logger.warn("\ndl   = " + dl + ", deg(u) = " + deg);
308                    logger.warn("ulist = " + ulist);
309                    logger.warn("kr    = " + kr);
310                    logger.warn("u     = " + u);
311                    logger.warn("trial = " + trial);
312                }
313                GenPolynomial<C> rem = PolyUtil.<C> baseSparsePseudoRemainder(u, trial);
314                //System.out.println(" rem = " + rem);
315                if (rem.isZERO()) {
316                    logger.info("trial = " + trial);
317                    //System.out.println("trial = " + trial);
318                    factors.add(trial);
319                    u = PolyUtil.<C> basePseudoDivide(u, trial); //u = u.divide( trial );
320                    evl = u.leadingExpVector();
321                    evt = u.trailingExpVector();
322                    if (u.isConstant()) {
323                        j = dl + 1;
324                        break;
325                    }
326                    //if (ulist.removeAll(flist)) { // wrong
327                    ulist = removeOnce(ulist, flist);
328                    //System.out.println("new ulist = " + ulist);
329                    dl = (ulist.size() + 1) / 2;
330                    j = 0; // since j++
331                    break;
332                }
333            }
334        }
335        if (!u.isONE() && !u.equals(P)) {
336            logger.info("rest u = " + u);
337            factors.add(u);
338        }
339        if (factors.size() == 0) {
340            logger.info("irred P = " + P);
341            factors.add(P); // == u
342        }
343        return normalizeFactorization(factors);
344    }
345
346
347    /**
348     * Remove one occurrence of elements.
349     * @param a list of objects.
350     * @param b list of objects.
351     * @return remove every element of b from a, but only one occurrence.
352     *         <b>Note:</b> not available in java.util.
353     */
354    static <T> List<T> removeOnce(List<T> a, List<T> b) {
355        List<T> res = new ArrayList<T>();
356        res.addAll(a);
357        for (T e : b) {
358            @SuppressWarnings("unused")
359            boolean t = res.remove(e);
360        }
361        return res;
362    }
363
364
365    /**
366     * Univariate GenPolynomial factorization ignoring multiplicities.
367     * @param P GenPolynomial in one variable.
368     * @return [p_1, ..., p_k] with P = prod_{i=1,...,k} p_i**{e_i} for some
369     *         e_i.
370     */
371    public List<GenPolynomial<C>> baseFactorsRadical(GenPolynomial<C> P) {
372        return new ArrayList<GenPolynomial<C>>(baseFactors(P).keySet());
373    }
374
375
376    /**
377     * Univariate GenPolynomial factorization.
378     * @param P GenPolynomial in one variable.
379     * @return [p_1 -&gt; e_1, ..., p_k -&gt; e_k] with P = prod_{i=1,...,k}
380     *         p_i**e_i.
381     */
382    public SortedMap<GenPolynomial<C>, Long> baseFactors(GenPolynomial<C> P) {
383        if (P == null) {
384            throw new IllegalArgumentException(this.getClass().getName() + " P != null");
385        }
386        GenPolynomialRing<C> pfac = P.ring;
387        SortedMap<GenPolynomial<C>, Long> factors = new TreeMap<GenPolynomial<C>, Long>(pfac.getComparator());
388        if (P.isZERO()) {
389            return factors;
390        }
391        if (pfac.nvar > 1) {
392            throw new IllegalArgumentException(this.getClass().getName() + " only for univariate polynomials");
393        }
394        if (P.isConstant()) {
395            factors.put(P, 1L);
396            return factors;
397        }
398        C c;
399        if (pfac.coFac.isField()) { //pfac.characteristic().signum() > 0
400            c = P.leadingBaseCoefficient();
401        } else {
402            c = engine.baseContent(P);
403            // move sign to the content
404            if (P.signum() < 0 && c.signum() > 0) {
405                c = c.negate();
406                //P = P.negate();
407            }
408        }
409        if (!c.isONE()) {
410            GenPolynomial<C> pc = pfac.getONE().multiply(c);
411            factors.put(pc, 1L);
412            P = P.divide(c); // make primitive or monic
413        }
414        if (logger.isInfoEnabled()) {
415            logger.info("base facs for P = " + P);
416            //System.out.println(StringUtil.selectStackTrace("edu\\.jas.*"));
417        }
418        SortedMap<GenPolynomial<C>, Long> facs = sengine.baseSquarefreeFactors(P);
419        if (facs == null || facs.size() == 0) {
420            facs = new TreeMap<GenPolynomial<C>, Long>();
421            facs.put(P, 1L);
422        }
423        if (logger.isInfoEnabled()
424                        && (facs.size() > 1 || (facs.size() == 1 && facs.get(facs.firstKey()) > 1))) {
425            logger.info("squarefree facs   = " + facs);
426            //System.out.println("sfacs   = " + facs);
427            //boolean tt = isFactorization(P,facs);
428            //System.out.println("sfacs tt   = " + tt);
429        }
430        for (Map.Entry<GenPolynomial<C>, Long> me : facs.entrySet()) {
431            GenPolynomial<C> g = me.getKey();
432            Long k = me.getValue(); //facs.get(g);
433            //System.out.println("g       = " + g);
434            if (pfac.coFac.isField() && !g.leadingBaseCoefficient().isONE()) {
435                g = g.monic(); // how can this happen?
436                logger.warn("squarefree facs mon = " + g);
437            }
438            if (g.degree(0) <= 1) {
439                if (!g.isONE()) {
440                    factors.put(g, k);
441                }
442            } else {
443                List<GenPolynomial<C>> sfacs = baseFactorsSquarefree(g);
444                if (debug) {
445                    logger.info("factors of squarefree = " + sfacs);
446                    //System.out.println("sfacs   = " + sfacs);
447                }
448                for (GenPolynomial<C> h : sfacs) {
449                    Long j = factors.get(h); // evtl. constants
450                    if (j != null) {
451                        k += j;
452                    }
453                    if (!h.isONE()) {
454                        factors.put(h, k);
455                    }
456                }
457            }
458        }
459        //System.out.println("factors = " + factors);
460        return factors;
461    }
462
463
464    /**
465     * Univariate GenPolynomial factorization of a squarefree polynomial.
466     * @param P squarefree and primitive! GenPolynomial in one variable.
467     * @return [p_1, ..., p_k] with P = prod_{i=1,...,k} p_i.
468     */
469    public abstract List<GenPolynomial<C>> baseFactorsSquarefree(GenPolynomial<C> P);
470
471
472    /**
473     * GenPolynomial factorization ignoring multiplicities.
474     * @param P GenPolynomial.
475     * @return [p_1, ..., p_k] with P = prod_{i=1,...,k} p_i**{e_i} for some
476     *         e_i.
477     */
478    public List<GenPolynomial<C>> factorsRadical(GenPolynomial<C> P) {
479        return new ArrayList<GenPolynomial<C>>(factors(P).keySet());
480    }
481
482
483    /**
484     * GenPolynomial list factorization ignoring multiplicities.
485     * @param L list of GenPolynomials.
486     * @return [p_1, ..., p_k] with p = prod_{i=1,...,k} p_i**{e_i} for some e_i
487     *         for all p in L.
488     */
489    public List<GenPolynomial<C>> factorsRadical(List<GenPolynomial<C>> L) {
490        SortedSet<GenPolynomial<C>> facs = new TreeSet<GenPolynomial<C>>();
491        for (GenPolynomial<C> p : L) {
492            List<GenPolynomial<C>> fs = factorsRadical(p);
493            facs.addAll(fs);
494        }
495        return new ArrayList<GenPolynomial<C>>(facs);
496    }
497
498
499    /**
500     * GenPolynomial factorization.
501     * @param P GenPolynomial.
502     * @return [p_1 -&gt; e_1, ..., p_k -&gt; e_k] with P = prod_{i=1,...,k}
503     *         p_i**e_i.
504     */
505    public SortedMap<GenPolynomial<C>, Long> factors(GenPolynomial<C> P) {
506        if (P == null) {
507            throw new IllegalArgumentException(this.getClass().getName() + " P != null");
508        }
509        GenPolynomialRing<C> pfac = P.ring;
510        if (pfac.nvar == 1) {
511            return baseFactors(P);
512        }
513        SortedMap<GenPolynomial<C>, Long> factors = new TreeMap<GenPolynomial<C>, Long>(pfac.getComparator());
514        if (P.isZERO()) {
515            return factors;
516        }
517        if (P.isConstant()) {
518            factors.put(P, 1L);
519            return factors;
520        }
521        C c;
522        if (pfac.coFac.isField()) { 
523            c = P.leadingBaseCoefficient();
524        } else {
525            c = engine.baseContent(P);
526            // move sign to the content
527            if (P.signum() < 0 && c.signum() > 0) {
528                c = c.negate();
529                //P = P.negate();
530            }
531        }
532        if (!c.isONE()) {
533            GenPolynomial<C> pc = pfac.getONE().multiply(c);
534            factors.put(pc, 1L);
535            P = P.divide(c); // make base primitive or base monic
536        }
537        if (logger.isInfoEnabled()) {
538            logger.info("base primitive part P = " + P);
539        }
540        GenPolynomial<C>[] cpp = engine.contentPrimitivePart(P);
541        GenPolynomial<C> pc = cpp[0];
542        if (!pc.isONE()) {
543            SortedMap<GenPolynomial<C>, Long> rec = factors(pc); // recursion
544            for (Map.Entry<GenPolynomial<C>, Long> me : rec.entrySet()) {
545                GenPolynomial<C> g = me.getKey();
546                Long d = me.getValue();
547                GenPolynomial<C> pn = g.extend(pfac,0,0L);
548                factors.put(pn,d);
549            }
550            if (logger.isInfoEnabled()) {
551                logger.info("content factors = " + factors);
552            }
553        }
554        P = cpp[1];
555        if (logger.isInfoEnabled()) {
556            logger.info("primitive part P = " + P);
557        }
558        if (P.isONE()) {
559            return factors;
560        }
561        SortedMap<GenPolynomial<C>, Long> facs = sengine.squarefreeFactors(P);
562        if (facs == null || facs.size() == 0) {
563            facs = new TreeMap<GenPolynomial<C>, Long>();
564            facs.put(P, 1L);
565            throw new RuntimeException("this should not happen, facs is empty: " + facs);
566        }
567        if (logger.isInfoEnabled()) {
568            if (facs.size() > 1) {
569                logger.info("squarefree mfacs      = " + facs);
570            } else if (facs.size() == 1 && facs.get(facs.firstKey()) > 1L) {
571                logger.info("squarefree #mfacs 1-n = " + facs);
572            } else {
573                logger.info("squarefree #mfacs 1-1 = " + facs);
574            }
575        }
576        for (Map.Entry<GenPolynomial<C>, Long> me : facs.entrySet()) {
577            GenPolynomial<C> g = me.getKey();
578            if (g.isONE()) { // skip 1
579                continue;
580            }
581            Long d = me.getValue(); // facs.get(g);
582            List<GenPolynomial<C>> sfacs = factorsSquarefree(g);
583            if (logger.isInfoEnabled()) {
584                logger.info("factors of squarefree ^" + d + " = " + sfacs);
585                //System.out.println("sfacs   = " + sfacs);
586            }
587            for (GenPolynomial<C> h : sfacs) {
588                long dd = d;
589                Long j = factors.get(h); // evtl. constants
590                if (j != null) {
591                    dd += j;
592                }
593                factors.put(h, dd);
594            }
595        }
596        //System.out.println("factors = " + factors);
597        return factors;
598    }
599
600
601    /**
602     * GenPolynomial greatest squarefree divisor. Delegates computation to a
603     * GreatestCommonDivisor class.
604     * @param P GenPolynomial.
605     * @return squarefree(P).
606     */
607    public GenPolynomial<C> squarefreePart(GenPolynomial<C> P) {
608        return sengine.squarefreePart(P);
609    }
610
611
612    /**
613     * GenPolynomial primitive part. Delegates computation to a
614     * GreatestCommonDivisor class.
615     * @param P GenPolynomial.
616     * @return primitivePart(P).
617     */
618    public GenPolynomial<C> primitivePart(GenPolynomial<C> P) {
619        return engine.primitivePart(P);
620    }
621
622
623    /**
624     * GenPolynomial base primitive part. Delegates computation to a
625     * GreatestCommonDivisor class.
626     * @param P GenPolynomial.
627     * @return basePrimitivePart(P).
628     */
629    public GenPolynomial<C> basePrimitivePart(GenPolynomial<C> P) {
630        return engine.basePrimitivePart(P);
631    }
632
633
634    /**
635     * GenPolynomial squarefree factorization. Delegates computation to a
636     * GreatestCommonDivisor class.
637     * @param P GenPolynomial.
638     * @return [p_1 -&gt; e_1, ..., p_k -&gt; e_k] with P = prod_{i=1,...,k}
639     *         p_i**e_i.
640     */
641    public SortedMap<GenPolynomial<C>, Long> squarefreeFactors(GenPolynomial<C> P) {
642        return sengine.squarefreeFactors(P);
643    }
644
645
646    /**
647     * GenPolynomial is factorization.
648     * @param P GenPolynomial.
649     * @param F = [p_1,...,p_k].
650     * @return true if P = prod_{i=1,...,r} p_i, else false.
651     */
652    public boolean isFactorization(GenPolynomial<C> P, List<GenPolynomial<C>> F) {
653        return sengine.isFactorization(P, F);
654        // test irreducible
655    }
656
657
658    /**
659     * GenPolynomial is factorization.
660     * @param P GenPolynomial.
661     * @param F = [p_1 -&gt; e_1, ..., p_k -&gt; e_k].
662     * @return true if P = prod_{i=1,...,k} p_i**e_i , else false.
663     */
664    public boolean isFactorization(GenPolynomial<C> P, SortedMap<GenPolynomial<C>, Long> F) {
665        return sengine.isFactorization(P, F);
666        // test irreducible
667    }
668
669
670    /**
671     * Degree of a factorization.
672     * @param F a factors map [p_1 -&gt; e_1, ..., p_k -&gt; e_k].
673     * @return sum_{i=1,...,k} degree(p_i)*e_i.
674     */
675    public long factorsDegree(SortedMap<GenPolynomial<C>, Long> F) {
676        long d = 0;
677        for (Map.Entry<GenPolynomial<C>, Long> me : F.entrySet()) {
678            GenPolynomial<C> p = me.getKey();
679            long e = me.getValue(); //F.get(p);
680            d += p.degree() * e;
681        }
682        return d;
683    }
684
685
686    /**
687     * GenPolynomial is factorization.
688     * @param P GenPolynomial.
689     * @param F = [p_1 -&gt; e_1, ..., p_k -&gt; e_k].
690     * @return true if P = prod_{i=1,...,k} p_i**e_i , else false.
691     */
692    public boolean isRecursiveFactorization(GenPolynomial<GenPolynomial<C>> P,
693                    SortedMap<GenPolynomial<GenPolynomial<C>>, Long> F) {
694        return sengine.isRecursiveFactorization(P, F);
695        // test irreducible
696    }
697
698
699    /**
700     * Recursive GenPolynomial factorization of a squarefree polynomial.
701     * @param P squarefree recursive GenPolynomial.
702     * @return [p_1,...,p_k] with P = prod_{i=1, ..., k} p_i.
703     */
704    public List<GenPolynomial<GenPolynomial<C>>> recursiveFactorsSquarefree(GenPolynomial<GenPolynomial<C>> P) {
705        if (P == null) {
706            throw new IllegalArgumentException(this.getClass().getName() + " P == null");
707        }
708        List<GenPolynomial<GenPolynomial<C>>> factors = new ArrayList<GenPolynomial<GenPolynomial<C>>>();
709        if (P.isZERO()) {
710            return factors;
711        }
712        if (P.isONE()) {
713            factors.add(P);
714            return factors;
715        }
716        GenPolynomialRing<GenPolynomial<C>> pfac = P.ring;
717        GenPolynomialRing<C> qi = (GenPolynomialRing<C>) pfac.coFac;
718        GenPolynomialRing<C> ifac = qi.extend(pfac.getVars());
719        GenPolynomial<C> Pi = PolyUtil.<C> distribute(ifac, P);
720        //System.out.println("Pi = " + Pi);
721
722        C ldcf = Pi.leadingBaseCoefficient();
723        if (!ldcf.isONE() && ldcf.isUnit()) {
724            //System.out.println("ldcf = " + ldcf);
725            Pi = Pi.monic();
726        }
727
728        // factor in C[x_1,...,x_n,y_1,...,y_m]
729        List<GenPolynomial<C>> ifacts = factorsSquarefree(Pi);
730        if (logger.isInfoEnabled()) {
731            logger.info("ifacts = " + ifacts);
732        }
733        if (ifacts.size() <= 1) {
734            factors.add(P);
735            return factors;
736        }
737        if (!ldcf.isONE() && ldcf.isUnit()) {
738            GenPolynomial<C> r = ifacts.get(0);
739            ifacts.remove(r);
740            r = r.multiply(ldcf);
741            ifacts.add(0, r);
742        }
743        List<GenPolynomial<GenPolynomial<C>>> rfacts = PolyUtil.<C> recursive(pfac, ifacts);
744        //System.out.println("rfacts = " + rfacts);
745        if (logger.isDebugEnabled()) {
746            logger.info("recfacts = " + rfacts);
747        }
748        factors.addAll(rfacts);
749        return factors;
750    }
751
752
753    /**
754     * Recursive GenPolynomial factorization.
755     * @param P recursive GenPolynomial.
756     * @return [p_1 -&gt; e_1, ..., p_k -&gt; e_k] with P = prod_{i=1,...,k}
757     *         p_i**e_i.
758     */
759    public SortedMap<GenPolynomial<GenPolynomial<C>>, Long> recursiveFactors(GenPolynomial<GenPolynomial<C>> P) {
760        if (P == null) {
761            throw new IllegalArgumentException(this.getClass().getName() + " P != null");
762        }
763        GenPolynomialRing<GenPolynomial<C>> pfac = P.ring;
764        SortedMap<GenPolynomial<GenPolynomial<C>>, Long> factors = new TreeMap<GenPolynomial<GenPolynomial<C>>, Long>(
765                        pfac.getComparator());
766        if (P.isZERO()) {
767            return factors;
768        }
769        if (P.isONE()) {
770            factors.put(P, 1L);
771            return factors;
772        }
773        GenPolynomialRing<C> qi = (GenPolynomialRing<C>) pfac.coFac;
774        GenPolynomialRing<C> ifac = qi.extend(pfac.getVars());
775        GenPolynomial<C> Pi = PolyUtil.<C> distribute(ifac, P);
776        //System.out.println("Pi = " + Pi);
777
778        C ldcf = Pi.leadingBaseCoefficient();
779        if (!ldcf.isONE() && ldcf.isUnit()) {
780            //System.out.println("ldcf = " + ldcf);
781            Pi = Pi.monic();
782        }
783
784        // factor in C[x_1,...,x_n,y_1,...,y_m]
785        SortedMap<GenPolynomial<C>, Long> dfacts = factors(Pi);
786        if (logger.isInfoEnabled()) {
787            logger.info("dfacts = " + dfacts);
788        }
789        if (!ldcf.isONE() && ldcf.isUnit()) {
790            GenPolynomial<C> r = dfacts.firstKey();
791            Long E = dfacts.remove(r);
792            r = r.multiply(ldcf);
793            dfacts.put(r, E);
794        }
795        for (Map.Entry<GenPolynomial<C>, Long> me : dfacts.entrySet()) {
796            GenPolynomial<C> f = me.getKey();
797            Long E = me.getValue(); //dfacts.get(f);
798            GenPolynomial<GenPolynomial<C>> rp = PolyUtil.<C> recursive(pfac, f);
799            factors.put(rp, E);
800        }
801        //System.out.println("rfacts = " + rfacts);
802        if (logger.isInfoEnabled()) {
803            logger.info("recursive factors = " + factors);
804        }
805        return factors;
806    }
807
808
809    /**
810     * Normalize factorization. p'_i &gt; 0 for i &gt; 1 and p'_1 != 1 if k &gt;
811     * 1.
812     * @param F = [p_1,...,p_k].
813     * @return F' = [p'_1,...,p'_k].
814     */
815    public List<GenPolynomial<C>> normalizeFactorization(List<GenPolynomial<C>> F) {
816        if (F == null || F.size() <= 1) {
817            return F;
818        }
819        List<GenPolynomial<C>> Fp = new ArrayList<GenPolynomial<C>>(F.size());
820        GenPolynomial<C> f0 = F.get(0);
821        for (int i = 1; i < F.size(); i++) {
822            GenPolynomial<C> fi = F.get(i);
823            if (fi.signum() < 0) {
824                fi = fi.negate();
825                f0 = f0.negate();
826            }
827            Fp.add(fi);
828        }
829        if (!f0.isONE()) {
830            Fp.add(0, f0);
831        }
832        return Fp;
833    }
834}