001/*
002 * $Id$
003 */
004
005package edu.jas.ufd;
006
007
008import org.apache.logging.log4j.LogManager;
009import org.apache.logging.log4j.Logger;
010
011import edu.jas.arith.Modular;
012import edu.jas.arith.ModularRingFactory;
013import edu.jas.poly.ExpVector;
014import edu.jas.poly.GenPolynomial;
015import edu.jas.poly.GenPolynomialRing;
016import edu.jas.poly.PolyUtil;
017import edu.jas.structure.GcdRingElem;
018import edu.jas.structure.RingFactory;
019
020
021/**
022 * Greatest common divisor algorithms with modular evaluation algorithm for
023 * recursion.
024 * @author Heinz Kredel
025 */
026
027public class GreatestCommonDivisorModEval<MOD extends GcdRingElem<MOD> & Modular>
028                extends GreatestCommonDivisorAbstract<MOD> {
029
030
031    private static final Logger logger = LogManager.getLogger(GreatestCommonDivisorModEval.class);
032
033
034    private static final boolean debug = logger.isDebugEnabled();
035
036
037    /**
038     * Modular gcd algorithm to use.
039     */
040    protected final GreatestCommonDivisorAbstract<MOD> mufd = new GreatestCommonDivisorSimple<MOD>();
041    // not okay = new GreatestCommonDivisorPrimitive<MOD>();
042    // not okay = new GreatestCommonDivisorSubres<MOD>();
043
044
045    /**
046     * Univariate GenPolynomial greatest common divisor.
047     * @param P univariate GenPolynomial.
048     * @param S univariate GenPolynomial.
049     * @return gcd(P,S).
050     */
051    @Override
052    public GenPolynomial<MOD> baseGcd(GenPolynomial<MOD> P, GenPolynomial<MOD> S) {
053        // required as recursion base
054        return mufd.baseGcd(P, S);
055    }
056
057
058    /**
059     * Recursive univariate GenPolynomial greatest common divisor.
060     * @param P univariate recursive GenPolynomial.
061     * @param S univariate recursive GenPolynomial.
062     * @return gcd(P,S).
063     */
064    @Override
065    public GenPolynomial<GenPolynomial<MOD>> recursiveUnivariateGcd(GenPolynomial<GenPolynomial<MOD>> P,
066                    GenPolynomial<GenPolynomial<MOD>> S) {
067        //return mufd.recursiveUnivariateGcd(P, S);
068        // distributed polynomials gcd
069        GenPolynomialRing<GenPolynomial<MOD>> rfac = P.ring;
070        RingFactory<GenPolynomial<MOD>> rrfac = rfac.coFac;
071        GenPolynomialRing<MOD> cfac = (GenPolynomialRing<MOD>) rrfac;
072        GenPolynomialRing<MOD> dfac = cfac.extend(rfac.nvar);
073        GenPolynomial<MOD> Pd = PolyUtil.<MOD> distribute(dfac, P);
074        GenPolynomial<MOD> Sd = PolyUtil.<MOD> distribute(dfac, S);
075        GenPolynomial<MOD> Dd = gcd(Pd, Sd);
076        // convert to recursive
077        GenPolynomial<GenPolynomial<MOD>> C = PolyUtil.<MOD> recursive(rfac, Dd);
078        return C;
079    }
080
081
082    /**
083     * GenPolynomial greatest common divisor, modular evaluation algorithm.
084     * @param P GenPolynomial.
085     * @param S GenPolynomial.
086     * @return gcd(P,S).
087     */
088    @Override
089    public GenPolynomial<MOD> gcd(GenPolynomial<MOD> P, GenPolynomial<MOD> S) {
090        if (S == null || S.isZERO()) {
091            return P;
092        }
093        if (P == null || P.isZERO()) {
094            return S;
095        }
096        GenPolynomialRing<MOD> fac = P.ring;
097        // recusion base case for univariate polynomials
098        if (fac.nvar <= 1) {
099            GenPolynomial<MOD> T = baseGcd(P, S);
100            return T;
101        }
102        long e = P.degree(fac.nvar - 1);
103        long f = S.degree(fac.nvar - 1);
104        if (e == 0 && f == 0) {
105            GenPolynomialRing<GenPolynomial<MOD>> rfac = fac.recursive(1);
106            GenPolynomial<MOD> Pc = PolyUtil.<MOD> recursive(rfac, P).leadingBaseCoefficient();
107            GenPolynomial<MOD> Sc = PolyUtil.<MOD> recursive(rfac, S).leadingBaseCoefficient();
108            GenPolynomial<MOD> r = gcd(Pc, Sc);
109            return r.extend(fac, 0, 0L);
110        }
111        GenPolynomial<MOD> q;
112        GenPolynomial<MOD> r;
113        if (f > e) {
114            r = P;
115            q = S;
116            long g = f;
117            f = e;
118            e = g;
119        } else {
120            q = P;
121            r = S;
122        }
123        if (debug) {
124            logger.debug("degrees: e = " + e + ", f = " + f);
125        }
126        r = r.abs();
127        q = q.abs();
128        // setup factories
129        ModularRingFactory<MOD> cofac = (ModularRingFactory<MOD>) P.ring.coFac;
130        if (!cofac.isField()) {
131            logger.warn("cofac is not a field: " + cofac);
132        }
133        GenPolynomialRing<GenPolynomial<MOD>> rfac = fac.recursive(fac.nvar - 1);
134        GenPolynomialRing<MOD> mfac = new GenPolynomialRing<MOD>(cofac, rfac);
135        GenPolynomialRing<MOD> ufac = (GenPolynomialRing<MOD>) rfac.coFac;
136        // convert polynomials
137        GenPolynomial<GenPolynomial<MOD>> qr;
138        GenPolynomial<GenPolynomial<MOD>> rr;
139        qr = PolyUtil.<MOD> recursive(rfac, q);
140        rr = PolyUtil.<MOD> recursive(rfac, r);
141
142        // compute univariate contents and primitive parts
143        GenPolynomial<MOD> a = recursiveContent(rr);
144        GenPolynomial<MOD> b = recursiveContent(qr);
145        // gcd of univariate coefficient contents
146        GenPolynomial<MOD> c = gcd(a, b);
147        rr = PolyUtil.<MOD> recursiveDivide(rr, a);
148        qr = PolyUtil.<MOD> recursiveDivide(qr, b);
149        if (rr.isONE()) {
150            rr = rr.multiply(c);
151            r = PolyUtil.<MOD> distribute(fac, rr);
152            return r;
153        }
154        if (qr.isONE()) {
155            qr = qr.multiply(c);
156            q = PolyUtil.<MOD> distribute(fac, qr);
157            return q;
158        }
159        // compute normalization factor
160        GenPolynomial<MOD> ac = rr.leadingBaseCoefficient();
161        GenPolynomial<MOD> bc = qr.leadingBaseCoefficient();
162        GenPolynomial<MOD> cc = gcd(ac, bc);
163        // compute degrees and degree vectors
164        ExpVector rdegv = rr.degreeVector();
165        ExpVector qdegv = qr.degreeVector();
166        long rd0 = PolyUtil.<MOD> coeffMaxDegree(rr);
167        long qd0 = PolyUtil.<MOD> coeffMaxDegree(qr);
168        long cd0 = cc.degree(0);
169        long G = (rd0 < qd0 ? rd0 : qd0) + cd0 + 1L; // >=
170        //long Gm = (e < f ? e : f);
171
172        // initialize element and degree vector
173        ExpVector wdegv = rdegv.subst(0, rdegv.getVal(0) + 1);
174        // +1 seems to be a hack for the unlucky evaluation point test
175        MOD inc = cofac.getONE();
176        long i = 0;
177        long en = cofac.getIntegerModul().longValueExact() - 1; // just a stopper
178        MOD end = cofac.fromInteger(en);
179        MOD mi;
180        GenPolynomial<MOD> M = null;
181        GenPolynomial<MOD> mn;
182        GenPolynomial<MOD> qm;
183        GenPolynomial<MOD> rm;
184        GenPolynomial<MOD> cm;
185        GenPolynomial<GenPolynomial<MOD>> cp = null;
186        if (debug) {
187            logger.debug("c = " + c);
188            logger.debug("cc = " + cc);
189            logger.debug("G = " + G);
190            logger.info("wdegv = " + wdegv + ", in " + rfac.toScript());
191        }
192        for (MOD d = cofac.getZERO(); d.compareTo(end) <= 0; d = d.sum(inc)) {
193            if (++i >= en) {
194                logger.warn("elements of Z_p exhausted, en = " + en);
195                return mufd.gcd(P, S);
196                //throw new ArithmeticException("elements of Z_p exhausted, en = " + en);
197            }
198            // map normalization factor
199            MOD nf = PolyUtil.<MOD> evaluateMain(cofac, cc, d);
200            if (nf.isZERO()) {
201                continue;
202            }
203            // map polynomials
204            qm = PolyUtil.<MOD> evaluateFirstRec(ufac, mfac, qr, d);
205            if (qm.isZERO() || !qm.degreeVector().equals(qdegv)) {
206                continue;
207            }
208            rm = PolyUtil.<MOD> evaluateFirstRec(ufac, mfac, rr, d);
209            if (rm.isZERO() || !rm.degreeVector().equals(rdegv)) {
210                continue;
211            }
212            if (debug) {
213                logger.debug("eval d = " + d);
214            }
215            // compute modular gcd in recursion
216            cm = gcd(rm, qm);
217            if (debug) {
218                logger.debug("cm = " + cm + ", rm = " + rm + ", qm = " + qm);
219                //cm = mufd.gcd(rm,qm);
220                //logger.debug("cm = " + cm + ", rm = " + rm + ", qm = " + qm);
221            }
222            // test for constant g.c.d
223            if (cm.isConstant()) {
224                logger.debug("cm.isConstant = " + cm + ", c = " + c);
225                if (c.ring.nvar < cm.ring.nvar) {
226                    c = c.extend(mfac, 0, 0);
227                }
228                cm = cm.abs().multiply(c);
229                q = cm.extend(fac, 0, 0);
230                //logger.debug("q             = " + q + ", c = " + c);
231                return q;
232            }
233            // test for unlucky evaluation point
234            ExpVector mdegv = cm.degreeVector();
235            if (wdegv.equals(mdegv)) { // TL = 0
236                // evaluation point ok, next round
237                if (M != null) {
238                    if (M.degree(0) > G) {
239                        logger.info("deg(M) > G: " + M.degree(0) + " > " + G);
240                        // continue; // why should this be required?
241                    }
242                }
243            } else { // TL = 3
244                boolean ok = false;
245                if (wdegv.multipleOf(mdegv)) { // TL = 2
246                    M = null; // init chinese remainder
247                    ok = true; // evaluation point ok
248                }
249                if (mdegv.multipleOf(wdegv)) { // TL = 1
250                    continue; // skip this evaluation point
251                }
252                if (!ok) {
253                    M = null; // discard chinese remainder and previous work
254                    continue; // evaluation point not ok
255                }
256            }
257            // prepare interpolation algorithm
258            cm = cm.multiply(nf);
259            if (M == null) {
260                // initialize interpolation
261                M = ufac.getONE();
262                cp = rfac.getZERO();
263                wdegv = wdegv.gcd(mdegv); //EVGCD(wdegv,mdegv);
264            }
265            // interpolate
266            mi = PolyUtil.<MOD> evaluateMain(cofac, M, d);
267            mi = mi.inverse(); // mod p
268            cp = PolyUtil.interpolate(rfac, cp, M, mi, cm, d);
269            if (debug) {
270                logger.debug("cp = " + cp + ", cm = " + cm + ", M = " + M + " :: " + M.ring.toScript());
271            }
272            mn = ufac.getONE().multiply(d);
273            mn = ufac.univariate(0).subtract(mn);
274            M = M.multiply(mn); // M * (x-d)
275            // test for divisibility
276            boolean tt = false;
277            if (cp.leadingBaseCoefficient().equals(cc)) {
278                cp = recursivePrimitivePart(cp).abs();
279                logger.debug("test cp == cc: " + cp + " == " + cc);
280                tt = PolyUtil.<MOD> recursiveSparsePseudoRemainder(qr, cp).isZERO();
281                tt = tt && PolyUtil.<MOD> recursiveSparsePseudoRemainder(rr, cp).isZERO();
282                if (tt) {
283                    logger.debug("break: is gcd");
284                    break;
285                }
286                if (M.degree(0) > G) { // no && cp.degree(0) > Gm
287                    logger.debug("break: fail 1, cp = " + cp);
288                    cp = rfac.getONE();
289                    break;
290                }
291            }
292            // test for completion
293            if (M.degree(0) > G) { //  no && cp.degree(0) > Gm
294                logger.debug("break: M = " + M + ", G = " + G + ", mn = " + mn + ", M.deg(0) = "
295                                + M.degree(0));
296                cp = recursivePrimitivePart(cp).abs();
297                tt = PolyUtil.<MOD> recursiveSparsePseudoRemainder(qr, cp).isZERO();
298                tt = tt && PolyUtil.<MOD> recursiveSparsePseudoRemainder(rr, cp).isZERO();
299                if (!tt) {
300                    logger.debug("break: fail 2, cp = " + cp);
301                    cp = rfac.getONE();
302                }
303                break;
304            }
305            //long cmn = PolyUtil.<MOD>coeffMaxDegree(cp);
306            //if ( M.degree(0) > cmn ) {
307            // does not work: only if cofactors are also considered?
308            // break;
309            //}
310        }
311        // remove normalization
312        cp = recursivePrimitivePart(cp).abs();
313        cp = cp.multiply(c);
314        q = PolyUtil.<MOD> distribute(fac, cp);
315        return q;
316    }
317
318
319    /**
320     * Univariate GenPolynomial resultant.
321     * @param P univariate GenPolynomial.
322     * @param S univariate GenPolynomial.
323     * @return res(P,S).
324     */
325    @Override
326    public GenPolynomial<MOD> baseResultant(GenPolynomial<MOD> P, GenPolynomial<MOD> S) {
327        // required as recursion base
328        return mufd.baseResultant(P, S);
329    }
330
331
332    /**
333     * Univariate GenPolynomial recursive resultant.
334     * @param P univariate recursive GenPolynomial.
335     * @param S univariate recursive GenPolynomial.
336     * @return res(P,S).
337     */
338    @Override
339    public GenPolynomial<GenPolynomial<MOD>> recursiveUnivariateResultant(GenPolynomial<GenPolynomial<MOD>> P,
340                    GenPolynomial<GenPolynomial<MOD>> S) {
341        // only in this class
342        return recursiveResultant(P, S);
343    }
344
345
346    /**
347     * GenPolynomial resultant, modular evaluation algorithm.
348     * @param P GenPolynomial.
349     * @param S GenPolynomial.
350     * @return res(P,S).
351     */
352    @Override
353    public GenPolynomial<MOD> resultant(GenPolynomial<MOD> P, GenPolynomial<MOD> S) {
354        if (S == null || S.isZERO()) {
355            return S;
356        }
357        if (P == null || P.isZERO()) {
358            return P;
359        }
360        GenPolynomialRing<MOD> fac = P.ring;
361        // recusion base case for univariate polynomials
362        if (fac.nvar <= 1) {
363            GenPolynomial<MOD> T = baseResultant(P, S);
364            return T;
365        }
366        long e = P.degree(fac.nvar - 1);
367        long f = S.degree(fac.nvar - 1);
368        if (e == 0 && f == 0) {
369            GenPolynomialRing<GenPolynomial<MOD>> rfac = fac.recursive(1);
370            GenPolynomial<MOD> Pc = PolyUtil.<MOD> recursive(rfac, P).leadingBaseCoefficient();
371            GenPolynomial<MOD> Sc = PolyUtil.<MOD> recursive(rfac, S).leadingBaseCoefficient();
372            GenPolynomial<MOD> r = resultant(Pc, Sc);
373            return r.extend(fac, 0, 0L);
374        }
375        GenPolynomial<MOD> q;
376        GenPolynomial<MOD> r;
377        if (f > e) {
378            r = P;
379            q = S;
380            long g = f;
381            f = e;
382            e = g;
383        } else {
384            q = P;
385            r = S;
386        }
387        if (debug) {
388            logger.debug("degrees: e = " + e + ", f = " + f);
389        }
390        // setup factories
391        ModularRingFactory<MOD> cofac = (ModularRingFactory<MOD>) P.ring.coFac;
392        if (!cofac.isField()) {
393            logger.warn("cofac is not a field: " + cofac);
394        }
395        GenPolynomialRing<GenPolynomial<MOD>> rfac = fac.recursive(fac.nvar - 1);
396        GenPolynomialRing<MOD> mfac = new GenPolynomialRing<MOD>(cofac, rfac);
397        GenPolynomialRing<MOD> ufac = (GenPolynomialRing<MOD>) rfac.coFac;
398
399        // convert polynomials
400        GenPolynomial<GenPolynomial<MOD>> qr = PolyUtil.<MOD> recursive(rfac, q);
401        GenPolynomial<GenPolynomial<MOD>> rr = PolyUtil.<MOD> recursive(rfac, r);
402
403        // compute degrees and degree vectors
404        ExpVector qdegv = qr.degreeVector();
405        ExpVector rdegv = rr.degreeVector();
406
407        long qd0 = PolyUtil.<MOD> coeffMaxDegree(qr);
408        long rd0 = PolyUtil.<MOD> coeffMaxDegree(rr);
409        qd0 = (qd0 == 0 ? 1 : qd0);
410        rd0 = (rd0 == 0 ? 1 : rd0);
411        long qd1 = qr.degree();
412        long rd1 = rr.degree();
413        qd1 = (qd1 == 0 ? 1 : qd1);
414        rd1 = (rd1 == 0 ? 1 : rd1);
415        long G = qd0 * rd1 + rd0 * qd1 + 1;
416
417        // initialize element
418        MOD inc = cofac.getONE();
419        long i = 0;
420        long en = cofac.getIntegerModul().longValueExact() - 1; // just a stopper
421        MOD end = cofac.fromInteger(en);
422        MOD mi;
423        GenPolynomial<MOD> M = null;
424        GenPolynomial<MOD> mn;
425        GenPolynomial<MOD> qm;
426        GenPolynomial<MOD> rm;
427        GenPolynomial<MOD> cm;
428        GenPolynomial<GenPolynomial<MOD>> cp = null;
429        if (debug) {
430            //logger.info("qr    = " + qr + ", q = " + q);
431            //logger.info("rr    = " + rr + ", r = " + r);
432            //logger.info("qd0   = " + qd0);
433            //logger.info("rd0   = " + rd0);
434            logger.info("G     = " + G);
435            //logger.info("rdegv = " + rdegv); // + ", rr.degree(0) = " + rr.degree(0));
436            //logger.info("qdegv = " + qdegv); // + ", qr.degree(0) = " + qr.degree(0));
437        }
438        for (MOD d = cofac.getZERO(); d.compareTo(end) <= 0; d = d.sum(inc)) {
439            if (++i >= en) {
440                logger.warn("elements of Z_p exhausted, en = " + en + ", p = " + cofac.getIntegerModul());
441                return mufd.resultant(P, S);
442                //throw new ArithmeticException("prime list exhausted");
443            }
444            // map polynomials
445            qm = PolyUtil.<MOD> evaluateFirstRec(ufac, mfac, qr, d);
446            //logger.info("qr(" + d + ") = " + qm + ", qr = " + qr);
447            if (qm.isZERO() || !qm.degreeVector().equals(qdegv)) {
448                if (debug) {
449                    logger.info("un-lucky evaluation point " + d + ", qm = " + qm.degreeVector() + " < "
450                                    + qdegv);
451                }
452                continue;
453            }
454            rm = PolyUtil.<MOD> evaluateFirstRec(ufac, mfac, rr, d);
455            //logger.info("rr(" + d + ") = " + rm + ", rr = " + rr);
456            if (rm.isZERO() || !rm.degreeVector().equals(rdegv)) {
457                if (debug) {
458                    logger.info("un-lucky evaluation point " + d + ", rm = " + rm.degreeVector() + " < "
459                                    + rdegv);
460                }
461                continue;
462            }
463            // compute modular resultant in recursion
464            cm = resultant(rm, qm);
465            //System.out.println("cm = " + cm);
466
467            // prepare interpolation algorithm
468            if (M == null) {
469                // initialize interpolation
470                M = ufac.getONE();
471                cp = rfac.getZERO();
472            }
473            // interpolate
474            mi = PolyUtil.<MOD> evaluateMain(cofac, M, d);
475            mi = mi.inverse(); // mod p
476            cp = PolyUtil.interpolate(rfac, cp, M, mi, cm, d);
477            //logger.info("cp = " + cp);
478            mn = ufac.getONE().multiply(d);
479            mn = ufac.univariate(0).subtract(mn);
480            M = M.multiply(mn);
481            // test for completion
482            if (M.degree(0) > G) {
483                if (debug) {
484                    logger.info("last lucky evaluation point " + d);
485                }
486                break;
487            }
488            //logger.info("M  = " + M);
489        }
490        // distribute
491        q = PolyUtil.<MOD> distribute(fac, cp);
492        return q;
493    }
494
495}