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