001/*
002 * $Id: GreatestCommonDivisorModEval.java 5756 2017-06-05 21:42:52Z kredel $
003 */
004
005package edu.jas.ufd;
006
007
008import org.apache.log4j.Logger;
009
010import edu.jas.arith.Modular;
011import edu.jas.arith.ModularRingFactory;
012import edu.jas.poly.ExpVector;
013import edu.jas.poly.GenPolynomial;
014import edu.jas.poly.GenPolynomialRing;
015import edu.jas.poly.PolyUtil;
016import edu.jas.structure.GcdRingElem;
017import edu.jas.structure.RingFactory;
018
019
020/**
021 * Greatest common divisor algorithms with modular evaluation algorithm for
022 * recursion.
023 * @author Heinz Kredel
024 */
025
026public class GreatestCommonDivisorModEval <MOD extends GcdRingElem<MOD> & Modular> 
027        extends GreatestCommonDivisorAbstract<MOD> {
028
029
030    private static final Logger logger = Logger.getLogger(GreatestCommonDivisorModEval.class);
031
032
033    private static final boolean debug = logger.isDebugEnabled();
034
035
036    /**
037     * Modular gcd algorithm to use.
038     */
039    protected final GreatestCommonDivisorAbstract<MOD> mufd 
040       = 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(
066            GenPolynomial<GenPolynomial<MOD>> P, 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        //GenPolynomialRing<MOD> mfac = new GenPolynomialRing<MOD>(cofac, fac.nvar - 1, fac.tord);
137        //GenPolynomialRing<MOD> ufac = new GenPolynomialRing<MOD>(cofac, 1, fac.tord);
138        //GenPolynomialRing<GenPolynomial<MOD>> rfac = new GenPolynomialRing<GenPolynomial<MOD>>(ufac, fac.nvar - 1, fac.tord);
139        // convert polynomials
140        GenPolynomial<GenPolynomial<MOD>> qr;
141        GenPolynomial<GenPolynomial<MOD>> rr;
142        qr = PolyUtil.<MOD> recursive(rfac, q);
143        rr = PolyUtil.<MOD> recursive(rfac, r);
144
145        // compute univariate contents and primitive parts
146        GenPolynomial<MOD> a = recursiveContent(rr);
147        GenPolynomial<MOD> b = recursiveContent(qr);
148        // gcd of univariate coefficient contents
149        GenPolynomial<MOD> c = gcd(a, b);
150        rr = PolyUtil.<MOD> recursiveDivide(rr, a);
151        qr = PolyUtil.<MOD> recursiveDivide(qr, b);
152        if (rr.isONE()) {
153            rr = rr.multiply(c);
154            r = PolyUtil.<MOD> distribute(fac, rr);
155            return r;
156        }
157        if (qr.isONE()) {
158            qr = qr.multiply(c);
159            q = PolyUtil.<MOD> distribute(fac, qr);
160            return q;
161        }
162        // compute normalization factor
163        GenPolynomial<MOD> ac = rr.leadingBaseCoefficient();
164        GenPolynomial<MOD> bc = qr.leadingBaseCoefficient();
165        GenPolynomial<MOD> cc = gcd(ac, bc);
166        // compute degrees and degree vectors
167        ExpVector rdegv = rr.degreeVector();
168        ExpVector qdegv = qr.degreeVector();
169        long rd0 = PolyUtil.<MOD> coeffMaxDegree(rr);
170        long qd0 = PolyUtil.<MOD> coeffMaxDegree(qr);
171        long cd0 = cc.degree(0);
172        long G = (rd0 >= qd0 ? rd0 : qd0) + cd0;
173
174        // initialize element and degree vector
175        ExpVector wdegv = rdegv.subst(0, rdegv.getVal(0) + 1);
176        // +1 seems to be a hack for the unlucky evaluation point test
177        MOD inc = cofac.getONE();
178        long i = 0;
179        long en = cofac.getIntegerModul().longValue() - 1; // just a stopper
180        MOD end = cofac.fromInteger(en);
181        MOD mi;
182        GenPolynomial<MOD> M = null;
183        GenPolynomial<MOD> mn;
184        GenPolynomial<MOD> qm;
185        GenPolynomial<MOD> rm;
186        GenPolynomial<MOD> cm;
187        GenPolynomial<GenPolynomial<MOD>> cp = null;
188        if (debug) {
189            logger.debug("c = " + c);
190            logger.debug("cc = " + cc);
191            logger.debug("G = " + G);
192            logger.info("wdegv = " + wdegv);
193        }
194        for (MOD d = cofac.getZERO(); d.compareTo(end) <= 0; d = d.sum(inc)) {
195            if (++i >= en) {
196                logger.warn("elements of Z_p exhausted, en = " + en);
197                return mufd.gcd(P, S);
198                //throw new ArithmeticException("prime list exhausted");
199            }
200            // map normalization factor
201            MOD nf = PolyUtil.<MOD> evaluateMain(cofac, cc, d);
202            if (nf.isZERO()) {
203                continue;
204            }
205            // map polynomials
206            qm = PolyUtil.<MOD> evaluateFirstRec(ufac, mfac, qr, d);
207            if (qm.isZERO() || !qm.degreeVector().equals(qdegv)) {
208                continue;
209            }
210            rm = PolyUtil.<MOD> evaluateFirstRec(ufac, mfac, rr, d);
211            if (rm.isZERO() || !rm.degreeVector().equals(rdegv)) {
212                continue;
213            }
214            if (debug) {
215                logger.debug("eval d = " + d);
216            }
217            // compute modular gcd in recursion
218            cm = gcd(rm, qm);
219            //System.out.println("cm = " + cm);
220            // test for constant g.c.d
221            if (cm.isConstant()) {
222                logger.debug("cm.isConstant = " + cm + ", c = " + c);
223                if (c.ring.nvar < cm.ring.nvar) {
224                    c = c.extend(mfac, 0, 0);
225                }
226                cm = cm.abs().multiply(c);
227                q = cm.extend(fac, 0, 0);
228                logger.debug("q             = " + q + ", c = " + c);
229                return q;
230            }
231            // test for unlucky evaluation point
232            ExpVector mdegv = cm.degreeVector();
233            if (wdegv.equals(mdegv)) { // TL = 0
234                // evaluation point ok, next round
235                if (M != null) {
236                    if (M.degree(0) > G) {
237                        logger.info("deg(M) > G: " + M.degree(0) + " > " + G);
238                        // continue; // why should this be required?
239                    }
240                }
241            } else { // TL = 3
242                boolean ok = false;
243                if (wdegv.multipleOf(mdegv)) { // TL = 2
244                    M = null; // init chinese remainder
245                    ok = true; // evaluation point ok
246                }
247                if (mdegv.multipleOf(wdegv)) { // TL = 1
248                    continue; // skip this evaluation point
249                }
250                if (!ok) {
251                    M = null; // discard chinese remainder and previous work
252                    continue; // evaluation point not ok
253                }
254            }
255            // prepare interpolation algorithm
256            cm = cm.multiply(nf);
257            if (M == null) {
258                // initialize interpolation
259                M = ufac.getONE();
260                cp = rfac.getZERO();
261                wdegv = wdegv.gcd(mdegv); //EVGCD(wdegv,mdegv);
262            }
263            // interpolate
264            mi = PolyUtil.<MOD> evaluateMain(cofac, M, d);
265            mi = mi.inverse(); // mod p
266            cp = PolyUtil.interpolate(rfac, cp, M, mi, cm, d);
267            mn = ufac.getONE().multiply(d);
268            mn = ufac.univariate(0).subtract(mn);
269            M = M.multiply(mn);
270            // test for completion
271            if (M.degree(0) > G) {
272                break;
273            }
274            //long cmn = PolyUtil.<MOD>coeffMaxDegree(cp);
275            //if ( M.degree(0) > cmn ) {
276            // does not work: only if cofactors are also considered?
277            // break;
278            //}
279        }
280        // remove normalization
281        cp = recursivePrimitivePart(cp).abs();
282        cp = cp.multiply(c);
283        q = PolyUtil.<MOD> distribute(fac, cp);
284        return q;
285    }
286
287
288    /**
289     * Univariate GenPolynomial resultant. 
290     * @param P univariate GenPolynomial.
291     * @param S univariate GenPolynomial.
292     * @return res(P,S).
293     */
294    @Override
295    public GenPolynomial<MOD> baseResultant(GenPolynomial<MOD> P, GenPolynomial<MOD> S) { 
296        // required as recursion base
297        return mufd.baseResultant(P, S);
298    }
299
300
301    /**
302     * Univariate GenPolynomial recursive resultant. 
303     * @param P univariate recursive GenPolynomial.
304     * @param S univariate recursive GenPolynomial.
305     * @return res(P,S).
306     */
307    @Override
308    public GenPolynomial<GenPolynomial<MOD>> recursiveUnivariateResultant(GenPolynomial<GenPolynomial<MOD>> P,
309            GenPolynomial<GenPolynomial<MOD>> S) { 
310        // only in this class
311        return recursiveResultant(P,S);
312    }
313
314
315    /**
316     * GenPolynomial resultant, modular evaluation algorithm.
317     * @param P GenPolynomial.
318     * @param S GenPolynomial.
319     * @return res(P,S).
320     */
321    @Override
322    public GenPolynomial<MOD> resultant(GenPolynomial<MOD> P, GenPolynomial<MOD> S) {
323        if (S == null || S.isZERO()) {
324            return S;
325        }
326        if (P == null || P.isZERO()) {
327            return P;
328        }
329        GenPolynomialRing<MOD> fac = P.ring;
330        // recusion base case for univariate polynomials
331        if (fac.nvar <= 1) {
332            GenPolynomial<MOD> T = baseResultant(P, S);
333            return T;
334        }
335        long e = P.degree(fac.nvar-1);
336        long f = S.degree(fac.nvar-1);
337        if ( e == 0 && f == 0 ) {
338            GenPolynomialRing<GenPolynomial<MOD>> rfac = fac.recursive(1);
339            GenPolynomial<MOD> Pc = PolyUtil.<MOD> recursive(rfac, P).leadingBaseCoefficient();
340            GenPolynomial<MOD> Sc = PolyUtil.<MOD> recursive(rfac, S).leadingBaseCoefficient();
341            GenPolynomial<MOD> r = resultant(Pc,Sc);
342            return r.extend(fac,0,0L);
343        }
344        GenPolynomial<MOD> q;
345        GenPolynomial<MOD> r;
346        if (f > e) {
347            r = P;
348            q = S;
349            long g = f;
350            f = e;
351            e = g;
352        } else {
353            q = P;
354            r = S;
355        }
356        if (debug) {
357            logger.debug("degrees: e = " + e + ", f = " + f);
358        }
359        // setup factories
360        ModularRingFactory<MOD> cofac = (ModularRingFactory<MOD>) P.ring.coFac;
361        if (!cofac.isField()) {
362            logger.warn("cofac is not a field: " + cofac);
363        }
364        GenPolynomialRing<GenPolynomial<MOD>> rfac = fac.recursive(fac.nvar - 1);
365        GenPolynomialRing<MOD> mfac = new GenPolynomialRing<MOD>(cofac, rfac);
366        GenPolynomialRing<MOD> ufac = (GenPolynomialRing<MOD>) rfac.coFac;
367
368        // convert polynomials
369        GenPolynomial<GenPolynomial<MOD>> qr = PolyUtil.<MOD> recursive(rfac, q);
370        GenPolynomial<GenPolynomial<MOD>> rr = PolyUtil.<MOD> recursive(rfac, r);
371
372        // compute degrees and degree vectors
373        ExpVector qdegv = qr.degreeVector();
374        ExpVector rdegv = rr.degreeVector();
375
376        long qd0 = PolyUtil.<MOD> coeffMaxDegree(qr);
377        long rd0 = PolyUtil.<MOD> coeffMaxDegree(rr);
378        qd0 = ( qd0 == 0 ? 1 : qd0 );
379        rd0 = ( rd0 == 0 ? 1 : rd0 );
380        long qd1 = qr.degree(); 
381        long rd1 = rr.degree();
382        qd1 = ( qd1 == 0 ? 1 : qd1 );
383        rd1 = ( rd1 == 0 ? 1 : rd1 );
384        long G = qd0 * rd1 + rd0 * qd1 + 1;
385
386        // initialize element
387        MOD inc = cofac.getONE();
388        long i = 0;
389        long en = cofac.getIntegerModul().longValue() - 1; // just a stopper
390        MOD end = cofac.fromInteger(en);
391        MOD mi;
392        GenPolynomial<MOD> M = null;
393        GenPolynomial<MOD> mn;
394        GenPolynomial<MOD> qm;
395        GenPolynomial<MOD> rm;
396        GenPolynomial<MOD> cm;
397        GenPolynomial<GenPolynomial<MOD>> cp = null;
398        if (debug) {
399            //logger.info("qr    = " + qr + ", q = " + q);
400            //logger.info("rr    = " + rr + ", r = " + r);
401            //logger.info("qd0   = " + qd0);
402            //logger.info("rd0   = " + rd0);
403            logger.info("G     = " + G);
404            //logger.info("rdegv = " + rdegv); // + ", rr.degree(0) = " + rr.degree(0));
405            //logger.info("qdegv = " + qdegv); // + ", qr.degree(0) = " + qr.degree(0));
406        }
407        for (MOD d = cofac.getZERO(); d.compareTo(end) <= 0; d = d.sum(inc)) {
408            if (++i >= en) {
409                logger.warn("elements of Z_p exhausted, en = " + en + ", p = " + cofac.getIntegerModul());
410                return mufd.resultant(P, S);
411                //throw new ArithmeticException("prime list exhausted");
412            }
413            // map polynomials
414            qm = PolyUtil.<MOD> evaluateFirstRec(ufac, mfac, qr, d);
415            //logger.info("qr(" + d + ") = " + qm + ", qr = " + qr);
416            if (qm.isZERO() || !qm.degreeVector().equals(qdegv)) {
417                if (debug) {
418                   logger.info("un-lucky evaluation point " + d + ", qm = " + qm.degreeVector() + " < " + qdegv);
419                }
420                continue;
421            }
422            rm = PolyUtil.<MOD> evaluateFirstRec(ufac, mfac, rr, d);
423            //logger.info("rr(" + d + ") = " + rm + ", rr = " + rr);
424            if (rm.isZERO() || !rm.degreeVector().equals(rdegv)) {
425                if (debug) {
426                    logger.info("un-lucky evaluation point " + d + ", rm = " + rm.degreeVector() + " < " + rdegv);
427                }
428                continue;
429            }
430            // compute modular resultant in recursion
431            cm = resultant(rm, qm);
432            //System.out.println("cm = " + cm);
433
434            // prepare interpolation algorithm
435            if (M == null) {
436                // initialize interpolation
437                M = ufac.getONE();
438                cp = rfac.getZERO();
439            }
440            // interpolate
441            mi = PolyUtil.<MOD> evaluateMain(cofac, M, d);
442            mi = mi.inverse(); // mod p
443            cp = PolyUtil.interpolate(rfac, cp, M, mi, cm, d);
444            //logger.info("cp = " + cp);
445            mn = ufac.getONE().multiply(d);
446            mn = ufac.univariate(0).subtract(mn);
447            M = M.multiply(mn);
448            // test for completion
449            if (M.degree(0) > G) {
450                if (debug) {
451                    logger.info("last lucky evaluation point " + d);
452                }
453                break;
454            }
455            //logger.info("M  = " + M);
456        }
457        // distribute
458        q = PolyUtil.<MOD> distribute(fac, cp);
459        return q;
460    }
461
462}