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