001/*
002 * $Id: GreatestCommonDivisorModular.java 5774 2017-11-05 17:04:30Z kredel $
003 */
004
005package edu.jas.ufd;
006
007
008import org.apache.log4j.Logger;
009
010import edu.jas.arith.BigInteger;
011import edu.jas.arith.Combinatoric;
012import edu.jas.arith.ModIntegerRing;
013import edu.jas.arith.ModLongRing;
014import edu.jas.arith.Modular;
015import edu.jas.arith.ModularRingFactory;
016import edu.jas.arith.PrimeList;
017import edu.jas.poly.ExpVector;
018import edu.jas.poly.GenPolynomial;
019import edu.jas.poly.GenPolynomialRing;
020import edu.jas.poly.PolyUtil;
021import edu.jas.structure.GcdRingElem;
022import edu.jas.structure.RingFactory;
023
024
025/**
026 * Greatest common divisor algorithms with modular computation and chinese
027 * remainder algorithm.
028 * @author Heinz Kredel
029 */
030
031public class GreatestCommonDivisorModular<MOD extends GcdRingElem<MOD> & Modular>
032                extends GreatestCommonDivisorAbstract<BigInteger> {
033
034
035    private static final Logger logger = Logger.getLogger(GreatestCommonDivisorModular.class);
036
037
038    private static final boolean debug = logger.isDebugEnabled(); //logger.isInfoEnabled();
039
040
041    /*
042     * Modular gcd algorithm to use.
043     */
044    protected final GreatestCommonDivisorAbstract<MOD> mufd;
045
046
047    /*
048     * Integer gcd algorithm for fall back.
049     */
050    protected final GreatestCommonDivisorAbstract<BigInteger> iufd = new GreatestCommonDivisorSubres<BigInteger>();
051
052
053    /**
054     * Constructor to set recursive algorithm. Use modular evaluation GCD
055     * algorithm.
056     */
057    public GreatestCommonDivisorModular() {
058        this(false);
059    }
060
061
062    /**
063     * Constructor to set recursive algorithm.
064     * @param simple , true if the simple PRS should be used.
065     */
066    public GreatestCommonDivisorModular(boolean simple) {
067        if (simple) {
068            mufd = new GreatestCommonDivisorSimple<MOD>();
069        } else {
070            mufd = new GreatestCommonDivisorModEval<MOD>();
071        }
072    }
073
074
075    /**
076     * Univariate GenPolynomial greatest comon divisor. Delegate to subresultant
077     * baseGcd, should not be needed.
078     * @param P univariate GenPolynomial.
079     * @param S univariate GenPolynomial.
080     * @return gcd(P,S).
081     */
082    @Override
083    public GenPolynomial<BigInteger> baseGcd(GenPolynomial<BigInteger> P, GenPolynomial<BigInteger> S) {
084        return iufd.baseGcd(P, S);
085    }
086
087
088    /**
089     * Univariate GenPolynomial recursive greatest comon divisor. Delegate to
090     * subresultant recursiveGcd, should not be needed.
091     * @param P univariate recursive GenPolynomial.
092     * @param S univariate recursive GenPolynomial.
093     * @return gcd(P,S).
094     */
095    @Override
096    public GenPolynomial<GenPolynomial<BigInteger>> recursiveUnivariateGcd(
097                    GenPolynomial<GenPolynomial<BigInteger>> P, GenPolynomial<GenPolynomial<BigInteger>> S) {
098        //return iufd.recursiveUnivariateGcd(P, S); // bad performance
099        // distributed polynomials gcd
100        GenPolynomialRing<GenPolynomial<BigInteger>> rfac = P.ring;
101        RingFactory<GenPolynomial<BigInteger>> rrfac = rfac.coFac;
102        GenPolynomialRing<BigInteger> cfac = (GenPolynomialRing<BigInteger>) rrfac;
103        GenPolynomialRing<BigInteger> dfac = cfac.extend(rfac.nvar);
104        GenPolynomial<BigInteger> Pd = PolyUtil.<BigInteger> distribute(dfac, P);
105        GenPolynomial<BigInteger> Sd = PolyUtil.<BigInteger> distribute(dfac, S);
106        GenPolynomial<BigInteger> Dd = gcd(Pd, Sd);
107        // convert to recursive
108        GenPolynomial<GenPolynomial<BigInteger>> C = PolyUtil.<BigInteger> recursive(rfac, Dd);
109        return C;
110    }
111
112
113    /**
114     * GenPolynomial greatest comon divisor, modular algorithm.
115     * @param P GenPolynomial.
116     * @param S GenPolynomial.
117     * @return gcd(P,S).
118     */
119    @Override
120    @SuppressWarnings("unchecked")
121    public GenPolynomial<BigInteger> gcd(GenPolynomial<BigInteger> P, GenPolynomial<BigInteger> S) {
122        if (S == null || S.isZERO()) {
123            return P;
124        }
125        if (P == null || P.isZERO()) {
126            return S;
127        }
128        GenPolynomialRing<BigInteger> fac = P.ring;
129        // special case for univariate polynomials
130        if (fac.nvar <= 1) {
131            GenPolynomial<BigInteger> T = baseGcd(P, S);
132            return T;
133        }
134        long e = P.degree(0);
135        long f = S.degree(0);
136        GenPolynomial<BigInteger> q;
137        GenPolynomial<BigInteger> r;
138        if (f > e) {
139            r = P;
140            q = S;
141            long g = f;
142            f = e;
143            e = g;
144        } else {
145            q = P;
146            r = S;
147        }
148        if (debug) {
149            logger.debug("degrees: e = " + e + ", f = " + f);
150        }
151        r = r.abs();
152        q = q.abs();
153        // compute contents and primitive parts
154        BigInteger a = baseContent(r);
155        BigInteger b = baseContent(q);
156        // gcd of coefficient contents
157        BigInteger c = gcd(a, b); // indirection
158        r = divide(r, a); // indirection
159        q = divide(q, b); // indirection
160        if (r.isONE()) {
161            return r.multiply(c);
162        }
163        if (q.isONE()) {
164            return q.multiply(c);
165        }
166        // compute normalization factor
167        BigInteger ac = r.leadingBaseCoefficient();
168        BigInteger bc = q.leadingBaseCoefficient();
169        BigInteger cc = gcd(ac, bc); // indirection
170        // compute norms
171        BigInteger an = r.maxNorm();
172        BigInteger bn = q.maxNorm();
173        BigInteger n = (an.compareTo(bn) < 0 ? bn : an);
174        n = n.multiply(cc).multiply(n.fromInteger(2));
175        // compute degree vectors
176        ExpVector rdegv = r.degreeVector();
177        ExpVector qdegv = q.degreeVector();
178        //compute factor coefficient bounds
179        BigInteger af = an.multiply(PolyUtil.factorBound(rdegv));
180        BigInteger bf = bn.multiply(PolyUtil.factorBound(qdegv));
181        BigInteger cf = (af.compareTo(bf) < 0 ? bf : af);
182        cf = cf.multiply(cc.multiply(cc.fromInteger(8)));
183        //initialize prime list and degree vector
184        PrimeList primes = new PrimeList();
185        int pn = 10; //primes.size();
186        ExpVector wdegv = rdegv.subst(0, rdegv.getVal(0) + 1);
187        // +1 seems to be a hack for the unlucky prime test
188        ModularRingFactory<MOD> cofac;
189        ModularRingFactory<MOD> cofacM = null;
190        GenPolynomial<MOD> qm;
191        GenPolynomial<MOD> rm;
192        GenPolynomialRing<MOD> mfac;
193        GenPolynomialRing<MOD> rfac = null;
194        int i = 0;
195        BigInteger M = null;
196        BigInteger cfe = null;
197        GenPolynomial<MOD> cp = null;
198        GenPolynomial<MOD> cm = null;
199        GenPolynomial<BigInteger> cpi = null;
200        if (debug) {
201            logger.debug("c = " + c);
202            logger.debug("cc = " + cc);
203            logger.debug("n  = " + n);
204            logger.debug("cf = " + cf);
205            logger.info("wdegv = " + wdegv);
206        }
207        for (java.math.BigInteger p : primes) {
208            //System.out.println("next run ++++++++++++++++++++++++++++++++++");
209            if (p.longValue() == 2L) { // skip 2
210                continue;
211            }
212            if (++i >= pn) {
213                logger.warn("prime list exhausted, pn = " + pn);
214                return iufd.gcd(P, S);
215                //throw new ArithmeticException("prime list exhausted");
216            }
217            // initialize coefficient factory and map normalization factor
218            if (ModLongRing.MAX_LONG.compareTo(p) > 0) {
219                cofac = (ModularRingFactory) new ModLongRing(p, true);
220            } else {
221                cofac = (ModularRingFactory) new ModIntegerRing(p, true);
222            }
223            MOD nf = cofac.fromInteger(cc.getVal());
224            if (nf.isZERO()) {
225                continue;
226            }
227            // initialize polynomial factory and map polynomials
228            mfac = new GenPolynomialRing<MOD>(cofac, fac.nvar, fac.tord, fac.getVars());
229            qm = PolyUtil.<MOD> fromIntegerCoefficients(mfac, q);
230            if (qm.isZERO() || !qm.degreeVector().equals(qdegv)) {
231                continue;
232            }
233            rm = PolyUtil.<MOD> fromIntegerCoefficients(mfac, r);
234            if (rm.isZERO() || !rm.degreeVector().equals(rdegv)) {
235                continue;
236            }
237            if (debug) {
238                logger.info("cofac = " + cofac.getIntegerModul());
239            }
240            // compute modular gcd
241            cm = mufd.gcd(rm, qm);
242            // test for constant g.c.d
243            if (cm.isConstant()) {
244                logger.debug("cm, constant = " + cm);
245                return fac.getONE().multiply(c);
246                //return cm.abs().multiply( c );
247            }
248            // test for unlucky prime
249            ExpVector mdegv = cm.degreeVector();
250            if (wdegv.equals(mdegv)) { // TL = 0
251                // prime ok, next round
252                if (M != null) {
253                    if (M.compareTo(cfe) > 0) {
254                        System.out.println("M > cfe: " + M + " > " + cfe);
255                        // continue; // why should this be required?
256                    }
257                }
258            } else { // TL = 3
259                boolean ok = false;
260                if (wdegv.multipleOf(mdegv)) { // TL = 2 // EVMT(wdegv,mdegv)
261                    M = null; // init chinese remainder
262                    ok = true; // prime ok
263                }
264                if (mdegv.multipleOf(wdegv)) { // TL = 1 // EVMT(mdegv,wdegv)
265                    continue; // skip this prime
266                }
267                if (!ok) {
268                    M = null; // discard chinese remainder and previous work
269                    continue; // prime not ok
270                }
271            }
272            //--wdegv = mdegv;
273            // prepare chinese remainder algorithm
274            cm = cm.multiply(nf);
275            if (M == null) {
276                // initialize chinese remainder algorithm
277                M = new BigInteger(p);
278                cofacM = cofac;
279                rfac = mfac;
280                cp = cm;
281                wdegv = wdegv.gcd(mdegv); //EVGCD(wdegv,mdegv);
282                cfe = cf;
283                for (int k = 0; k < wdegv.length(); k++) {
284                    cfe = cfe.multiply(new BigInteger(wdegv.getVal(k) + 1));
285                }
286            } else {
287                // apply chinese remainder algorithm
288                BigInteger Mp = M;
289                MOD mi = cofac.fromInteger(Mp.getVal());
290                mi = mi.inverse(); // mod p
291                M = M.multiply(new BigInteger(p));
292                if (ModLongRing.MAX_LONG.compareTo(M.getVal()) > 0) {
293                    cofacM = (ModularRingFactory) new ModLongRing(M.getVal());
294                } else {
295                    cofacM = (ModularRingFactory) new ModIntegerRing(M.getVal());
296                }
297                rfac = new GenPolynomialRing<MOD>(cofacM, fac);
298                if (!cofac.getClass().equals(cofacM.getClass())) {
299                    logger.info("adjusting coefficents: cofacM = " + cofacM.getClass() + ", cofacP = "
300                                    + cofac.getClass());
301                    cofac = (ModularRingFactory) new ModIntegerRing(p);
302                    mfac = new GenPolynomialRing<MOD>(cofac, fac);
303                    GenPolynomial<BigInteger> mm = PolyUtil.<MOD> integerFromModularCoefficients(fac, cm);
304                    cm = PolyUtil.<MOD> fromIntegerCoefficients(mfac, mm);
305                    mi = cofac.fromInteger(Mp.getVal());
306                    mi = mi.inverse(); // mod p
307                }
308                if (!cp.ring.coFac.getClass().equals(cofacM.getClass())) {
309                    logger.info("adjusting coefficents: cofacM = " + cofacM.getClass() + ", cofacM' = "
310                                    + cp.ring.coFac.getClass());
311                    ModularRingFactory cop = (ModularRingFactory) cp.ring.coFac;
312                    cofac = (ModularRingFactory) new ModIntegerRing(cop.getIntegerModul().getVal());
313                    mfac = new GenPolynomialRing<MOD>(cofac, fac);
314                    GenPolynomial<BigInteger> mm = PolyUtil.<MOD> integerFromModularCoefficients(fac, cp);
315                    cp = PolyUtil.<MOD> fromIntegerCoefficients(mfac, mm);
316                }
317                cp = PolyUtil.<MOD> chineseRemainder(rfac, cp, mi, cm);
318            }
319            // test for completion
320            if (n.compareTo(M) <= 0) {
321                break;
322            }
323            // must use integer.sumNorm
324            cpi = PolyUtil.<MOD> integerFromModularCoefficients(fac, cp);
325            BigInteger cmn = cpi.sumNorm();
326            cmn = cmn.multiply(cmn.fromInteger(4));
327            //if ( cmn.compareTo( M ) <= 0 ) {
328            // does not work: only if cofactors are also considered?
329            // break;
330            //}
331            if (i % 2 != 0 && !cp.isZERO()) {
332                // check if done on every second prime
333                GenPolynomial<BigInteger> x;
334                x = PolyUtil.<MOD> integerFromModularCoefficients(fac, cp);
335                x = basePrimitivePart(x);
336                if (!PolyUtil.<BigInteger> baseSparsePseudoRemainder(q, x).isZERO()) {
337                    continue;
338                }
339                if (!PolyUtil.<BigInteger> baseSparsePseudoRemainder(r, x).isZERO()) {
340                    continue;
341                }
342                logger.info("done on exact division, #primes = " + i);
343                break;
344            }
345        }
346        if (debug) {
347            logger.info("done on M = " + M + ", #primes = " + i);
348        }
349        // remove normalization
350        q = PolyUtil.<MOD> integerFromModularCoefficients(fac, cp);
351        q = basePrimitivePart(q);
352        return q.abs().multiply(c);
353    }
354
355
356    /**
357     * Univariate GenPolynomial resultant.
358     * @param P univariate GenPolynomial.
359     * @param S univariate GenPolynomial.
360     * @return res(P,S).
361     */
362    @Override
363    public GenPolynomial<BigInteger> baseResultant(GenPolynomial<BigInteger> P, GenPolynomial<BigInteger> S) {
364        // not a special case here
365        return resultant(P, S);
366    }
367
368
369    /**
370     * Univariate GenPolynomial recursive resultant.
371     * @param P univariate recursive GenPolynomial.
372     * @param S univariate recursive GenPolynomial.
373     * @return res(P,S).
374     */
375    @Override
376    public GenPolynomial<GenPolynomial<BigInteger>> recursiveUnivariateResultant(
377                    GenPolynomial<GenPolynomial<BigInteger>> P, GenPolynomial<GenPolynomial<BigInteger>> S) {
378        // only in this class
379        return recursiveResultant(P, S);
380    }
381
382
383    /**
384     * GenPolynomial resultant, modular algorithm.
385     * @param P GenPolynomial.
386     * @param S GenPolynomial.
387     * @return res(P,S).
388     */
389    @Override
390    @SuppressWarnings("unchecked")
391    public GenPolynomial<BigInteger> resultant(GenPolynomial<BigInteger> P, GenPolynomial<BigInteger> S) {
392        if (S == null || S.isZERO()) {
393            return S;
394        }
395        if (P == null || P.isZERO()) {
396            return P;
397        }
398        GenPolynomialRing<BigInteger> fac = P.ring;
399        // no special case for univariate polynomials in this class !
400        //if (fac.nvar <= 1) {
401        //    GenPolynomial<BigInteger> T = iufd.baseResultant(P, S);
402        //    return T;
403        //}
404        long e = P.degree(0);
405        long f = S.degree(0);
406        GenPolynomial<BigInteger> q;
407        GenPolynomial<BigInteger> r;
408        if (f > e) {
409            r = P;
410            q = S;
411            long g = f;
412            f = e;
413            e = g;
414        } else {
415            q = P;
416            r = S;
417        }
418        // compute norms
419        BigInteger an = r.maxNorm();
420        BigInteger bn = q.maxNorm();
421        an = an.power(f);
422        bn = bn.power(e);
423        BigInteger cn = Combinatoric.factorial(e + f);
424        BigInteger n = cn.multiply(an).multiply(bn);
425
426        // compute degree vectors
427        ExpVector rdegv = r.leadingExpVector(); //degreeVector();
428        ExpVector qdegv = q.leadingExpVector(); //degreeVector();
429
430        //initialize prime list and degree vector
431        PrimeList primes = new PrimeList();
432        int pn = 30; //primes.size();
433        ModularRingFactory<MOD> cofac;
434        ModularRingFactory<MOD> cofacM = null;
435        GenPolynomial<MOD> qm;
436        GenPolynomial<MOD> rm;
437        GenPolynomialRing<MOD> mfac;
438        GenPolynomialRing<MOD> rfac = null;
439        int i = 0;
440        BigInteger M = null;
441        GenPolynomial<MOD> cp = null;
442        GenPolynomial<MOD> cm = null;
443        //GenPolynomial<BigInteger> cpi = null;
444        if (debug) {
445            logger.debug("an  = " + an);
446            logger.debug("bn  = " + bn);
447            logger.debug("e+f = " + (e + f));
448            logger.debug("cn  = " + cn);
449            logger.info("n     = " + n);
450            //logger.info("q     = " + q);
451            //logger.info("r     = " + r);
452            //logger.info("rdegv = " + rdegv.toString(fac.getVars()));
453            //logger.info("qdegv = " + qdegv.toString(fac.getVars()));
454        }
455        for (java.math.BigInteger p : primes) {
456            //System.out.println("next run ++++++++++++++++++++++++++++++++++");
457            if (p.longValue() == 2L) { // skip 2
458                continue;
459            }
460            if (++i >= pn) {
461                logger.warn("prime list exhausted, pn = " + pn);
462                return iufd.resultant(P, S);
463                //throw new ArithmeticException("prime list exhausted");
464            }
465            // initialize coefficient factory and map normalization factor
466            if (ModLongRing.MAX_LONG.compareTo(p) > 0) {
467                cofac = (ModularRingFactory) new ModLongRing(p, true);
468            } else {
469                cofac = (ModularRingFactory) new ModIntegerRing(p, true);
470            }
471            // initialize polynomial factory and map polynomials
472            mfac = new GenPolynomialRing<MOD>(cofac, fac);
473            qm = PolyUtil.<MOD> fromIntegerCoefficients(mfac, q);
474            if (qm.isZERO() || !qm.leadingExpVector().equals(qdegv)) { //degreeVector()
475                //logger.info("qm = " + qm);
476                if (debug) {
477                    logger.info("unlucky prime = " + cofac.getIntegerModul() + ", degv = "
478                                    + qm.leadingExpVector());
479                }
480                continue;
481            }
482            rm = PolyUtil.<MOD> fromIntegerCoefficients(mfac, r);
483            if (rm.isZERO() || !rm.leadingExpVector().equals(rdegv)) { //degreeVector()
484                //logger.info("rm = " + rm);
485                if (debug) {
486                    logger.info("unlucky prime = " + cofac.getIntegerModul() + ", degv = "
487                                    + rm.leadingExpVector());
488                }
489                continue;
490            }
491            logger.info("lucky prime = " + cofac.getIntegerModul());
492
493            // compute modular resultant
494            cm = mufd.resultant(qm, rm);
495            if (debug) {
496                logger.info("res_p = " + cm);
497            }
498
499            // prepare chinese remainder algorithm
500            if (M == null) {
501                // initialize chinese remainder algorithm
502                M = new BigInteger(p);
503                cofacM = cofac;
504                //rfac = mfac;
505                cp = cm;
506            } else {
507                // apply chinese remainder algorithm
508                BigInteger Mp = M;
509                MOD mi = cofac.fromInteger(Mp.getVal());
510                mi = mi.inverse(); // mod p
511                M = M.multiply(new BigInteger(p));
512                if (ModLongRing.MAX_LONG.compareTo(M.getVal()) > 0) {
513                    cofacM = (ModularRingFactory) new ModLongRing(M.getVal());
514                } else {
515                    cofacM = (ModularRingFactory) new ModIntegerRing(M.getVal());
516                }
517                rfac = new GenPolynomialRing<MOD>(cofacM, fac);
518                if (!cofac.getClass().equals(cofacM.getClass())) {
519                    logger.info("adjusting coefficents: cofacM = " + cofacM.getClass() + ", cofacP = "
520                                    + cofac.getClass());
521                    cofac = (ModularRingFactory) new ModIntegerRing(p);
522                    mfac = new GenPolynomialRing<MOD>(cofac, fac);
523                    GenPolynomial<BigInteger> mm = PolyUtil.<MOD> integerFromModularCoefficients(fac, cm);
524                    cm = PolyUtil.<MOD> fromIntegerCoefficients(mfac, mm);
525                    mi = cofac.fromInteger(Mp.getVal());
526                    mi = mi.inverse(); // mod p
527                }
528                if (!cp.ring.coFac.getClass().equals(cofacM.getClass())) {
529                    logger.info("adjusting coefficents: cofacM = " + cofacM.getClass() + ", cofacM' = "
530                                    + cp.ring.coFac.getClass());
531                    ModularRingFactory cop = (ModularRingFactory) cp.ring.coFac;
532                    cofac = (ModularRingFactory) new ModIntegerRing(cop.getIntegerModul().getVal());
533                    mfac = new GenPolynomialRing<MOD>(cofac, fac);
534                    GenPolynomial<BigInteger> mm = PolyUtil.<MOD> integerFromModularCoefficients(fac, cp);
535                    cp = PolyUtil.<MOD> fromIntegerCoefficients(mfac, mm);
536                }
537                cp = PolyUtil.<MOD> chineseRemainder(rfac, cp, mi, cm);
538            }
539            // test for completion
540            if (n.compareTo(M) <= 0) {
541                break;
542            }
543        }
544        if (debug) {
545            logger.info("done on M = " + M + ", #primes = " + i);
546        }
547        // convert to integer polynomial
548        q = PolyUtil.<MOD> integerFromModularCoefficients(fac, cp);
549        return q;
550    }
551
552}