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