001/*
002 * $Id: SquarefreeAbstract.java 5845 2018-05-31 09:24:18Z 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.TreeMap;
013
014import org.apache.log4j.Logger;
015
016import edu.jas.poly.GenPolynomial;
017import edu.jas.poly.GenPolynomialRing;
018import edu.jas.poly.PolyUtil;
019import edu.jas.structure.GcdRingElem;
020import edu.jas.structure.RingFactory;
021
022
023/**
024 * Abstract squarefree decomposition class.
025 * @author Heinz Kredel
026 */
027
028public abstract class SquarefreeAbstract<C extends GcdRingElem<C>> implements Squarefree<C> {
029
030
031    private static final Logger logger = Logger.getLogger(SquarefreeAbstract.class);
032
033
034    /**
035     * GCD engine for respective base coefficients.
036     */
037    protected final GreatestCommonDivisorAbstract<C> engine;
038
039
040    /**
041     * Constructor.
042     */
043    public SquarefreeAbstract(GreatestCommonDivisorAbstract<C> engine) {
044        this.engine = engine;
045    }
046
047
048    /**
049     * GenPolynomial polynomial greatest squarefree divisor.
050     * @param P GenPolynomial.
051     * @return squarefree(pp(P)).
052     */
053    public abstract GenPolynomial<C> baseSquarefreePart(GenPolynomial<C> P);
054
055
056    /**
057     * GenPolynomial polynomial squarefree factorization.
058     * @param A GenPolynomial.
059     * @return [p_1 -&gt; e_1, ..., p_k -&gt; e_k] with P = prod_{i=1,...,k}
060     *         p_i^{e_i} and p_i squarefree.
061     */
062    public abstract SortedMap<GenPolynomial<C>, Long> baseSquarefreeFactors(GenPolynomial<C> A);
063
064
065    /**
066     * GenPolynomial recursive polynomial greatest squarefree divisor.
067     * @param P recursive univariate GenPolynomial.
068     * @return squarefree(pp(P)).
069     */
070    public abstract GenPolynomial<GenPolynomial<C>> recursiveUnivariateSquarefreePart(
071                    GenPolynomial<GenPolynomial<C>> P);
072
073
074    /**
075     * GenPolynomial recursive univariate polynomial squarefree factorization.
076     * @param P recursive univariate GenPolynomial.
077     * @return [p_1 -&gt; e_1, ..., p_k -&gt; e_k] with P = prod_{i=1,...,k}
078     *         p_i^{e_i} and p_i squarefree.
079     */
080    public abstract SortedMap<GenPolynomial<GenPolynomial<C>>, Long> recursiveUnivariateSquarefreeFactors(
081                    GenPolynomial<GenPolynomial<C>> P);
082
083
084    /**
085     * GenPolynomial greatest squarefree divisor.
086     * @param P GenPolynomial.
087     * @return squarefree(P) a primitive respectively monic polynomial.
088     */
089    public abstract GenPolynomial<C> squarefreePart(GenPolynomial<C> P);
090
091
092    /**
093     * GenPolynomial test if is squarefree.
094     * @param P GenPolynomial.
095     * @return true if P is squarefree, else false.
096     */
097    public boolean isSquarefree(GenPolynomial<C> P) {
098        GenPolynomial<C> S = squarefreePart(P);
099        GenPolynomial<C> Ps = P;
100        if (P.ring.coFac.isField()) {
101            Ps = Ps.monic();
102        } else {
103            Ps = engine.basePrimitivePart(Ps);
104        }
105        boolean f = Ps.equals(S);
106        return f;
107    }
108
109
110    /**
111     * GenPolynomial list test if squarefree.
112     * @param L list of GenPolynomial.
113     * @return true if each P in L is squarefree, else false.
114     */
115    public boolean isSquarefree(List<GenPolynomial<C>> L) {
116        if (L == null || L.isEmpty()) {
117            return true;
118        }
119        for (GenPolynomial<C> P : L) {
120            if (!isSquarefree(P)) {
121                return false;
122            }
123        }
124        return true;
125    }
126
127
128    /**
129     * Recursive GenPolynomial test if is squarefree.
130     * @param P recursive univariate GenPolynomial.
131     * @return true if P is squarefree, else false.
132     */
133    public boolean isRecursiveSquarefree(GenPolynomial<GenPolynomial<C>> P) {
134        GenPolynomial<GenPolynomial<C>> S = recursiveUnivariateSquarefreePart(P);
135        boolean f = P.equals(S);
136        if (!f) {
137            logger.info("not Squarefree, S != P: " + P + " != " + S);
138        }
139        return f;
140    }
141
142
143    /**
144     * GenPolynomial squarefree factorization.
145     * @param P GenPolynomial.
146     * @return [p_1 -&gt; e_1, ..., p_k -&gt; e_k] with P = prod_{i=1,...,k}
147     *         p_i^{e_i} and p_i squarefree.
148     */
149    public abstract SortedMap<GenPolynomial<C>, Long> squarefreeFactors(GenPolynomial<C> P);
150
151
152    /**
153     * GenPolynomial squarefree and co-prime list.
154     * @param A list of GenPolynomials.
155     * @return B with gcd(b,c) = 1 for all b != c in B and for all non-constant
156     *         a in A there exists b in B with b|a and each b in B is
157     *         squarefree. B does not contain zero or constant polynomials.
158     */
159    public List<GenPolynomial<C>> coPrimeSquarefree(List<GenPolynomial<C>> A) {
160        if (A == null || A.isEmpty()) {
161            return A;
162        }
163        List<GenPolynomial<C>> S = new ArrayList<GenPolynomial<C>>();
164        for (GenPolynomial<C> g : A) {
165            SortedMap<GenPolynomial<C>, Long> sm = squarefreeFactors(g);
166            S.addAll(sm.keySet());
167        }
168        List<GenPolynomial<C>> B = engine.coPrime(S);
169        return B;
170    }
171
172
173    /**
174     * GenPolynomial squarefree and co-prime list.
175     * @param a polynomial.
176     * @param P squarefree co-prime list of GenPolynomials.
177     * @return B with gcd(b,c) = 1 for all b != c in B and for non-constant a
178     *         there exists b in P with b|a. B does not contain zero or constant
179     *         polynomials.
180     */
181    public List<GenPolynomial<C>> coPrimeSquarefree(GenPolynomial<C> a, List<GenPolynomial<C>> P) {
182        if (a == null || a.isZERO() || a.isConstant()) {
183            return P;
184        }
185        SortedMap<GenPolynomial<C>, Long> sm = squarefreeFactors(a);
186        List<GenPolynomial<C>> B = P;
187        for (GenPolynomial<C> f : sm.keySet()) {
188            B = engine.coPrime(f, B);
189        }
190        return B;
191    }
192
193
194    /**
195     * Test if list of GenPolynomials is squarefree and co-prime.
196     * @param B list of GenPolynomials.
197     * @return true, if for all b != c in B gcd(b,c) = 1 and each b in B is
198     *         squarefree, else false.
199     */
200    public boolean isCoPrimeSquarefree(List<GenPolynomial<C>> B) {
201        if (B == null || B.isEmpty()) {
202            return true;
203        }
204        if (!engine.isCoPrime(B)) {
205            return false;
206        }
207        return isSquarefree(B);
208    }
209
210
211    /**
212     * Normalize factorization. p'_i &gt; 0 for i &gt; 1 and p'_1 != 1 if k &gt;
213     * 1.
214     * @param F = [p_1-&gt;e_1;, ..., p_k-&gt;e_k].
215     * @return F' = [p'_1-&gt;e_1, ..., p'_k-&gt;e_k].
216     */
217    public SortedMap<GenPolynomial<C>, Long> normalizeFactorization(SortedMap<GenPolynomial<C>, Long> F) {
218        if (F == null || F.size() <= 1) {
219            return F;
220        }
221        List<GenPolynomial<C>> Fp = new ArrayList<GenPolynomial<C>>(F.keySet());
222        GenPolynomial<C> f0 = Fp.get(0);
223        if (f0.ring.characteristic().signum() != 0) { // only ordered coefficients
224            return F;
225        }
226        long e0 = F.get(f0);
227        SortedMap<GenPolynomial<C>, Long> Sp = new TreeMap<GenPolynomial<C>, Long>();
228        for (int i = 1; i < Fp.size(); i++) {
229            GenPolynomial<C> fi = Fp.get(i);
230            long ei = F.get(fi);
231            if (fi.signum() < 0) {
232                //System.out.println("e0 = " + e0 + ", f0 = " + f0);
233                //System.out.println("ei = " + ei + ", fi = " + fi);
234                if (ei % 2 != 0 && e0 % 2 != 0) { // bug
235                    fi = fi.negate();
236                    f0 = f0.negate();
237                }
238            }
239            Sp.put(fi, ei);
240        }
241        if (!f0.isONE()) {
242            Sp.put(f0, e0);
243        }
244        return Sp;
245    }
246
247
248    /**
249     * GenPolynomial is (squarefree) factorization.
250     * @param P GenPolynomial.
251     * @param F = [p_1,...,p_k].
252     * @return true if P = prod_{i=1,...,r} p_i, else false.
253     */
254    public boolean isFactorization(GenPolynomial<C> P, List<GenPolynomial<C>> F) {
255        if (P == null || F == null) {
256            throw new IllegalArgumentException("P and F may not be null");
257        }
258        GenPolynomial<C> t = P.ring.getONE();
259        for (GenPolynomial<C> f : F) {
260            t = t.multiply(f);
261        }
262        boolean f = P.equals(t) || P.equals(t.negate());
263        if (!f) {
264            logger.info("no factorization(list): F = " + F + ", P = " + P + ", t = " + t);
265        }
266        return f;
267    }
268
269
270    /**
271     * Count number of factors in a (squarefree) factorization.
272     * @param F = [p_1 -&gt; e_1, ..., p_k -&gt; e_k].
273     * @return sum_{i=1,...,k} e_i.
274     */
275    public long factorCount(SortedMap<GenPolynomial<C>, Long> F) {
276        if (F == null || F.isEmpty()) {
277            return 0L;
278        }
279        long f = 0L;
280        for (Long e : F.values()) {
281            f += e;
282        }
283        return f;
284    }
285
286
287    /**
288     * GenPolynomial is (squarefree) factorization.
289     * @param P GenPolynomial.
290     * @param F = [p_1 -&gt; e_1, ..., p_k -&gt; e_k].
291     * @return true if P = prod_{i=1,...,k} p_i**e_i, else false.
292     */
293    public boolean isFactorization(GenPolynomial<C> P, SortedMap<GenPolynomial<C>, Long> F) {
294        if (P == null || F == null) {
295            throw new IllegalArgumentException("P and F may not be null");
296        }
297        if (P.isZERO() && F.size() == 0) {
298            return true;
299        }
300        GenPolynomial<C> t = P.ring.getONE();
301        for (Map.Entry<GenPolynomial<C>, Long> me : F.entrySet()) {
302            GenPolynomial<C> f = me.getKey();
303            Long E = me.getValue(); 
304            long e = E.longValue();
305            GenPolynomial<C> g = f.power(e);
306            t = t.multiply(g);
307        }
308        boolean f = P.equals(t) || P.equals(t.negate());
309        if (!f) {
310            P = P.monic();
311            t = t.monic();
312            f = P.equals(t) || P.equals(t.negate());
313            if (f) {
314                return f;
315            }
316            logger.info("no factorization(map): F = " + F + ", P = " + P + ", t = " + t);
317            //RuntimeException e = new RuntimeException("fac-map");
318            //e.printStackTrace();
319            //throw e;
320        }
321        return f;
322    }
323
324
325    /**
326     * GenPolynomial is (squarefree) factorization.
327     * @param P GenPolynomial.
328     * @param F = [p_1 -&gt; e_1, ..., p_k -&gt; e_k].
329     * @return true if P = prod_{i=1,...,k} p_i**e_i, else false.
330     */
331    public boolean isRecursiveFactorization(GenPolynomial<GenPolynomial<C>> P,
332                    SortedMap<GenPolynomial<GenPolynomial<C>>, Long> F) {
333        if (P == null || F == null) {
334            throw new IllegalArgumentException("P and F may not be null");
335        }
336        if (P.isZERO() && F.size() == 0) {
337            return true;
338        }
339        GenPolynomial<GenPolynomial<C>> t = P.ring.getONE();
340        for (Map.Entry<GenPolynomial<GenPolynomial<C>>, Long> me : F.entrySet()) {
341            GenPolynomial<GenPolynomial<C>> f = me.getKey();
342            Long E = me.getValue(); // F.get(f);
343            long e = E.longValue();
344            GenPolynomial<GenPolynomial<C>> g = f.power(e);
345            t = t.multiply(g);
346        }
347        boolean f = P.equals(t) || P.equals(t.negate());
348        if (!f) {
349            GenPolynomialRing<C> cf = (GenPolynomialRing<C>) P.ring.coFac;
350            GreatestCommonDivisorAbstract<C> engine = GCDFactory.getProxy(cf.coFac);
351            GenPolynomial<GenPolynomial<C>> Pp = engine.recursivePrimitivePart(P);
352            Pp = PolyUtil.<C> monic(Pp);
353            GenPolynomial<GenPolynomial<C>> tp = engine.recursivePrimitivePart(t);
354            tp = PolyUtil.<C> monic(tp);
355            f = Pp.equals(tp) || Pp.equals(tp.negate());
356            if (f) {
357                return f;
358            }
359            logger.info("no factorization(map): F  = " + F + ", P  = " + P + ", t  = " + t 
360                        + ", Pp = " + Pp + ", tp = " + tp);
361            //RuntimeException e = new RuntimeException("fac-map");
362            //e.printStackTrace();
363            //throw e;
364        }
365        return f;
366    }
367
368
369    /**
370     * GenPolynomial recursive polynomial greatest squarefree divisor.
371     * @param P recursive GenPolynomial.
372     * @return squarefree(pp(P)).
373     */
374    public GenPolynomial<GenPolynomial<C>> recursiveSquarefreePart(GenPolynomial<GenPolynomial<C>> P) {
375        if (P == null || P.isZERO()) {
376            return P;
377        }
378        if (P.ring.nvar <= 1) {
379            return recursiveUnivariateSquarefreePart(P);
380        }
381        // distributed polynomials squarefree part
382        GenPolynomialRing<GenPolynomial<C>> rfac = P.ring;
383        RingFactory<GenPolynomial<C>> rrfac = rfac.coFac;
384        GenPolynomialRing<C> cfac = (GenPolynomialRing<C>) rrfac;
385        GenPolynomialRing<C> dfac = cfac.extend(rfac.nvar);
386        GenPolynomial<C> Pd = PolyUtil.<C> distribute(dfac, P);
387        GenPolynomial<C> Dd = squarefreePart(Pd);
388        // convert to recursive
389        GenPolynomial<GenPolynomial<C>> C = PolyUtil.<C> recursive(rfac, Dd);
390        return C;
391    }
392
393
394    /**
395     * GenPolynomial recursive polynomial squarefree factorization.
396     * @param P recursive GenPolynomial.
397     * @return [p_1 -&gt; e_1, ..., p_k -&gt; e_k] with P = prod_{i=1,...,k}
398     *         p_i^{e_i} and p_i squarefree.
399     */
400    public SortedMap<GenPolynomial<GenPolynomial<C>>, Long> recursiveSquarefreeFactors(
401                    GenPolynomial<GenPolynomial<C>> P) {
402        SortedMap<GenPolynomial<GenPolynomial<C>>, Long> factors;
403        factors = new TreeMap<GenPolynomial<GenPolynomial<C>>, Long>();
404        if (P == null || P.isZERO()) {
405            return factors;
406        }
407        if (P.ring.nvar <= 1) {
408            return recursiveUnivariateSquarefreeFactors(P);
409        }
410        // distributed polynomials squarefree part
411        GenPolynomialRing<GenPolynomial<C>> rfac = P.ring;
412        RingFactory<GenPolynomial<C>> rrfac = rfac.coFac;
413        GenPolynomialRing<C> cfac = (GenPolynomialRing<C>) rrfac;
414        GenPolynomialRing<C> dfac = cfac.extend(rfac.nvar);
415        GenPolynomial<C> Pd = PolyUtil.<C> distribute(dfac, P);
416        SortedMap<GenPolynomial<C>, Long> dfacs = squarefreeFactors(Pd);
417        // convert to recursive
418        for (Map.Entry<GenPolynomial<C>, Long> Dm : dfacs.entrySet()) {
419            GenPolynomial<C> Dd = Dm.getKey();
420            Long e = Dm.getValue();
421            GenPolynomial<GenPolynomial<C>> C = PolyUtil.<C> recursive(rfac, Dd);
422            factors.put(C, e);
423        }
424        return factors;
425    }
426
427
428    /**
429     * Univariate GenPolynomial partial fraction decomposition.
430     * @param A univariate GenPolynomial.
431     * @param D sorted map [d_1 -&gt; e_1, ..., d_k -&gt; e_k] with d_i
432     *            squarefree.
433     * @return [ [Ai0, Ai1,..., Aie_i], i=0,...,k ] with A/prod(D) = A0 + sum(
434     *         sum ( Aij/di^j ) ) with deg(Aij) < deg(di).
435     */
436    public List<List<GenPolynomial<C>>> basePartialFraction(GenPolynomial<C> A,
437                    SortedMap<GenPolynomial<C>, Long> D) {
438        if (D == null || A == null) {
439            throw new IllegalArgumentException("null A or D not allowed");
440        }
441        List<List<GenPolynomial<C>>> pf = new ArrayList<List<GenPolynomial<C>>>(D.size() + 1);
442        if (D.size() == 0) {
443            return pf;
444        }
445        //List<GenPolynomial<C>> fi;
446        if (A.isZERO()) {
447            for (Map.Entry<GenPolynomial<C>, Long> me : D.entrySet()) {
448                long e = me.getValue();
449                int e1 = (int) e + 1;
450                List<GenPolynomial<C>> fi = new ArrayList<GenPolynomial<C>>(e1);
451                for (int i = 0; i < e1; i++) {
452                    fi.add(A);
453                }
454                pf.add(fi);
455            }
456            List<GenPolynomial<C>> fi = new ArrayList<GenPolynomial<C>>(1);
457            fi.add(A);
458            pf.add(0, fi);
459            return pf;
460        }
461        // A != 0, D != empty
462        List<GenPolynomial<C>> Dp = new ArrayList<GenPolynomial<C>>(D.size());
463        for (Map.Entry<GenPolynomial<C>, Long> me : D.entrySet()) {
464            GenPolynomial<C> d = me.getKey();
465            long e = me.getValue(); //D.get(d);
466            GenPolynomial<C> f = d.power(e);
467            Dp.add(f);
468        }
469        List<GenPolynomial<C>> F = engine.basePartialFraction(A, Dp);
470        //System.out.println("fraction list = " + F.size());
471        GenPolynomial<C> A0 = F.remove(0);
472        List<GenPolynomial<C>> fi = new ArrayList<GenPolynomial<C>>(1);
473        fi.add(A0);
474        pf.add(fi);
475        int i = 0;
476        for (Map.Entry<GenPolynomial<C>, Long> me : D.entrySet()) { // assume fixed sequence order
477            GenPolynomial<C> d = me.getKey();
478            long e = me.getValue(); 
479            int ei = (int) e;
480            GenPolynomial<C> gi = F.get(i); // assume fixed sequence order
481            List<GenPolynomial<C>> Fi = engine.basePartialFraction(gi, d, ei);
482            pf.add(Fi);
483            i++;
484        }
485        return pf;
486    }
487
488
489    /**
490     * Test for Univariate GenPolynomial partial fraction decomposition.
491     * @param A univariate GenPolynomial.
492     * @param D sorted map [d_1 -&gt; e_1, ..., d_k -&gt; e_k] with d_i
493     *            squarefree.
494     * @param F a list of lists [ [Ai0, Ai1,..., Aie_i], i=0,...,k ]
495     * @return true, if A/prod(D) = A0 + sum( sum ( Aij/di^j ) ), else false.
496     */
497    public boolean isBasePartialFraction(GenPolynomial<C> A, SortedMap<GenPolynomial<C>, Long> D,
498                    List<List<GenPolynomial<C>>> F) {
499        if (D == null || A == null || F == null) {
500            throw new IllegalArgumentException("null A, D or F not allowed");
501        }
502        if (D.isEmpty() && F.isEmpty()) {
503            return true;
504        }
505        if (D.isEmpty() || F.isEmpty()) {
506            return false;
507        }
508        List<GenPolynomial<C>> Dp = new ArrayList<GenPolynomial<C>>(D.size());
509        for (Map.Entry<GenPolynomial<C>, Long> me : D.entrySet()) {
510            GenPolynomial<C> d = me.getKey();
511            long e = me.getValue(); // D.get(d);
512            GenPolynomial<C> f = d.power(e);
513            Dp.add(f);
514        }
515        List<GenPolynomial<C>> fi = F.get(0);
516        if (fi.size() != 1) {
517            logger.info("size(fi) != 1 " + fi);
518            return false;
519        }
520        boolean t;
521        GenPolynomial<C> A0 = fi.get(0);
522        //System.out.println("A0 = " + A0);
523        List<GenPolynomial<C>> Qp = new ArrayList<GenPolynomial<C>>(D.size() + 1);
524        Qp.add(A0);
525
526        //         List<GenPolynomial<C>> Fp = engine.basePartialFraction(A,Dp);
527        //         System.out.println("fraction list = " + F.size());
528        //         t = engine.isBasePartialFraction(A,Dp,Fp);
529        //         if ( ! t ) {
530        //             System.out.println("not recursion isPartFrac = " + Fp);
531        //             return false;
532        //         }
533        //         GenPolynomial<C> A0p = Fp.remove(0);
534        //         if ( ! A0.equals(A0p) ) {
535        //             System.out.println("A0 != A0p " + A0p);
536        //             return false;
537        //         }
538
539        int i = 0;
540        for (Map.Entry<GenPolynomial<C>, Long> me : D.entrySet()) { // assume fixed sequence order
541            GenPolynomial<C> d = me.getKey();
542            long e = me.getValue(); 
543            int ei = (int) e;
544            List<GenPolynomial<C>> Fi = F.get(i + 1); // assume fixed sequence order
545
546            //            GenPolynomial<C> pi = Fp.get(i);        // assume fixed sequence order
547            //             t = engine.isBasePartialFraction(pi,d,ei,Fi);
548            //             if ( ! t ) {
549            //                 System.out.println("not isPartFrac exp = " + pi + ", d = " + d + ", e = " + ei);
550            //                 System.out.println("not isPartFrac exp = " + Fi);
551            //                 return false;
552            //             }
553
554            GenPolynomial<C> qi = engine.basePartialFractionValue(d, ei, Fi);
555            Qp.add(qi);
556
557            //             t = qi.equals(pi);
558            //             if ( ! t ) {
559            //                 System.out.println("not isPartFrac exp = " + pi + ", d = " + d + ", e = " + ei + ", qi = " + qi);
560            //             }
561
562            i++;
563        }
564
565        t = engine.isBasePartialFraction(A, Dp, Qp);
566        if (!t) {
567            System.out.println("not final isPartFrac " + Qp);
568        }
569        return t;
570    }
571
572
573    /**
574     * Coefficients greatest squarefree divisor.
575     * @param P coefficient.
576     * @return squarefree part of P.
577     */
578    public C squarefreePart(C P) {
579        if (P == null) {
580            return null;
581        }
582        C s = null;
583        SortedMap<C, Long> factors = squarefreeFactors(P);
584        if (logger.isWarnEnabled()) {
585            logger.warn("sqfPart, better use sqfFactors, factors = " + factors);
586        }
587        for (C sp : factors.keySet()) {
588            if (s == null) {
589                s = sp;
590            } else {
591                s = s.multiply(sp);
592            }
593        }
594        return s;
595    }
596
597
598    /**
599     * Coefficients squarefree factorization.
600     * @param P coefficient.
601     * @return [p_1 -&gt; e_1, ..., p_k -&gt; e_k] with P = prod_{i=1,...,k}
602     *         p_i^{e_i} and p_i squarefree.
603     */
604    public abstract SortedMap<C, Long> squarefreeFactors(C P);
605    /* not possible:
606    {
607        if (P == null) {
608            return null;
609        }
610        SortedMap<C, Long> factors = new TreeMap<C, Long>();
611        SquarefreeAbstract<C> reng = SquarefreeFactory.getImplementation((RingFactory<C>) P.factory());
612            System.out.println("fcp,reng = " + reng);
613            SortedMap<C, Long> rfactors = reng.squarefreeFactors(P);
614            for (C c : rfactors.keySet()) {
615                if (!c.isONE()) {
616                    C cr = (C) (Object) c;
617                    Long rk = rfactors.get(c);
618                    factors.put(cr, rk);
619                }
620            }
621    
622        return factors;
623    }
624    */
625
626}