001/*
002 * $Id: CycloUtil.java 5643 2016-11-18 20:31:03Z 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;
012
013import org.apache.log4j.Logger;
014
015import edu.jas.arith.BigInteger;
016import edu.jas.arith.PrimeInteger;
017import edu.jas.poly.ExpVector;
018import edu.jas.poly.GenPolynomial;
019import edu.jas.poly.GenPolynomialRing;
020import edu.jas.poly.Monomial;
021import edu.jas.structure.Power;
022
023
024/**
025 * Cyclotomic polynomial utilities. Adapted from Sympy Python codes.
026 * @author Heinz Kredel
027 */
028
029public class CycloUtil {
030
031
032    private static final Logger logger = Logger.getLogger(CycloUtil.class);
033
034
035    //private static final boolean debug = logger.isDebugEnabled();
036
037
038    /**
039     * Compute n-th cyclotomic polynomial.
040     * @param n number of polynomial.
041     * @param ring univariate polynomial ring of cyclotomic polynomial.
042     * @return n-th cyclotomic polynomial.
043     */
044    public static GenPolynomial<BigInteger> cyclotomicPolynomial(GenPolynomialRing<BigInteger> ring, long n) {
045        GenPolynomialRing<BigInteger> pfac = ring;
046        if (pfac == null) {
047            throw new IllegalArgumentException("ring must be non null");
048        }
049        GenPolynomial<BigInteger> h = pfac.univariate(0).subtract(pfac.getONE());
050        //System.out.println("h = " + h);
051        SortedMap<Long, Integer> fac = PrimeInteger.factors(n);
052        logger.info("factors = " + fac);
053
054        for (Map.Entry<Long, Integer> m : fac.entrySet()) {
055            // h = dup_quo(dup_inflate(h, p, K), h, K)
056            // h = dup_inflate(h, p**(k - 1), K)
057            long p = m.getKey();
058            int k = m.getValue();
059            GenPolynomial<BigInteger> ih = h.inflate(p);
060            //System.out.println("ih = " + ih);
061            h = ih.divide(h);
062            //System.out.println("h = " + h);
063            h = h.inflate(Power.power(p, k - 1));
064            //System.out.println("h = " + h + ", power = " + Power.power(p, k-1));
065        }
066        return h;
067    }
068
069
070    /**
071     * Compute the factors of the n-th cyclotomic polynomial.
072     * @param n number of polynomial.
073     * @param ring univariate polynomial ring of cyclotomic polynomial.
074     * @return list of factors of n-th cyclotomic polynomial.
075     */
076    public static List<GenPolynomial<BigInteger>> cyclotomicDecompose(GenPolynomialRing<BigInteger> ring,
077                    long n) {
078        GenPolynomialRing<BigInteger> pfac = ring;
079        if (pfac == null) {
080            throw new IllegalArgumentException("ring must be non null");
081        }
082        GenPolynomial<BigInteger> q = pfac.univariate(0).subtract(pfac.getONE());
083        //System.out.println("q = " + q);
084        List<GenPolynomial<BigInteger>> H = new ArrayList<GenPolynomial<BigInteger>>();
085        H.add(q);
086
087        SortedMap<Long, Integer> fac = PrimeInteger.factors(n);
088        logger.info("factors = " + fac);
089
090        for (Map.Entry<Long, Integer> m : fac.entrySet()) {
091            //Q = [ dup_quo(dup_inflate(h, p, K), h, K) for h in H ]
092            //H.extend(Q)
093            long p = m.getKey();
094            int k = m.getValue();
095            List<GenPolynomial<BigInteger>> Q = new ArrayList<GenPolynomial<BigInteger>>();
096            for (GenPolynomial<BigInteger> h : H) {
097                GenPolynomial<BigInteger> g = h.inflate(p).divide(h);
098                Q.add(g);
099            }
100            //System.out.println("Q = " + Q);
101            H.addAll(Q);
102            //for i in xrange(1, k):
103            //    Q = [ dup_inflate(q, p, K) for q in Q ]
104            //    H.extend(Q)
105            for (int i = 1; i < k; i++) {
106                List<GenPolynomial<BigInteger>> P = new ArrayList<GenPolynomial<BigInteger>>();
107                for (GenPolynomial<BigInteger> h : Q) {
108                    GenPolynomial<BigInteger> g = h.inflate(p);
109                    P.add(g);
110                }
111                //System.out.println("P = " + P);
112                Q = P;
113                H.addAll(P);
114            }
115        }
116        return H;
117    }
118
119
120    /**
121     * Compute the factors of the cyclotomic polynomial.
122     * @param p cyclotomic polynomial.
123     * @return list of factors of cyclotomic polynomial p or emtpy list.
124     */
125    public static List<GenPolynomial<BigInteger>> cyclotomicFactors(GenPolynomial<BigInteger> p) {
126        List<GenPolynomial<BigInteger>> H = new ArrayList<GenPolynomial<BigInteger>>();
127        long n = p.degree();
128        if (n <= 0) {
129            return H;
130        }
131        BigInteger lc, tc;
132        lc = p.leadingBaseCoefficient();
133        tc = p.trailingBaseCoefficient();
134        if (!lc.isONE() || (!tc.isONE() && !tc.negate().isONE())) {
135            return H;
136        }
137        if (p.length() != 2) { // other coefficients must be zero
138            return H;
139        }
140        // only case: x**n +/- 1
141        //F = _dup_cyclotomic_decompose(n, K)
142        //if not K.is_one(tc_f):
143        //   return F
144        GenPolynomialRing<BigInteger> pfac = p.ring;
145        List<GenPolynomial<BigInteger>> F = cyclotomicDecompose(pfac, n);
146        if (!tc.isONE()) {
147            return F;
148        }
149        //else:
150        //   H = []
151        //   for h in _dup_cyclotomic_decompose(2*n, K):
152        //       if h not in F:
153        //          H.append(h)
154        //return H                         
155        H = new ArrayList<GenPolynomial<BigInteger>>();
156        List<GenPolynomial<BigInteger>> F2 = cyclotomicDecompose(pfac, 2L * n);
157        for (GenPolynomial<BigInteger> h : F2) {
158            if (!F.contains(h)) {
159                H.add(h);
160            }
161        }
162        return H;
163    }
164
165
166    /**
167     * Test for cyclotomic polynomial.
168     * @param p polynomial.
169     * @return true if p is a cyclotomic polynomial, else false.
170     */
171    public static boolean isCyclotomicPolynomial(GenPolynomial<BigInteger> p) {
172        long n = p.degree();
173        if (n <= 0) {
174            return false;
175        }
176        BigInteger lc, tc;
177        lc = p.leadingBaseCoefficient();
178        tc = p.trailingBaseCoefficient();
179        if (!lc.isONE() || (!tc.isONE() && !tc.negate().isONE())) {
180            //System.out.println("!lc.isONE() || (!tc.isONE() && !tc.negate().isONE())");
181            return false;
182        }
183        if (p.length() == 2) { // other coefficients must be zero
184            return true;
185        }
186        // ignore: if not irreducible:
187        GenPolynomialRing<BigInteger> ring = p.ring;
188        if (ring.nvar != 1) {
189            throw new IllegalArgumentException("not univariate polynomial");
190        }
191        GenPolynomial<BigInteger> g, h, f;
192        g = ring.getZERO().copy();
193        h = ring.getZERO().copy();
194        long x = n % 2;
195        for (Monomial<BigInteger> m : p) {
196            ExpVector e = m.e;
197            long d = e.getVal(0);
198            ExpVector e2 = e.subst(0, d / 2L);
199            if (d % 2 == x) {
200                g.doPutToMap(e2, m.c);
201            } else {
202                h.doPutToMap(e2, m.c);
203            }
204        }
205        //g = dup_sqr(dup_strip(g), K)
206        //h = dup_sqr(dup_strip(h), K)
207        g = g.multiply(g);
208        h = h.multiply(h);
209        //System.out.println("g = " + g);
210        //System.out.println("h = " + h);
211        //F = dup_sub(g, dup_lshift(h, 1, K), K)
212        ExpVector on = ExpVector.create(1, 0, 1L);
213        f = g.subtract(h.multiply(on)).abs();
214        //System.out.println("f = " + f + ", f==p: " + f.equals(p));
215        //if F == f: return True 
216        if (f.equals(p)) {
217            return true;
218        }
219        //g = dup_mirror(f, K)
220        //if F == g and dup_cyclotomic_p(g, K):
221        //   return True
222        g = ring.getZERO().copy();
223        for (Monomial<BigInteger> m : p) {
224            ExpVector e = m.e;
225            long d = e.getVal(0);
226            if (d % 2 == 1) {
227                g.doPutToMap(e, m.c.negate());
228            } else {
229                g.doPutToMap(e, m.c);
230            }
231        }
232        g = g.abs();
233        //System.out.println("g = " + g + ", f==g: " + f.equals(g));
234        if (f.equals(g) && isCyclotomicPolynomial(g)) {
235            return true;
236        }
237        //G = dup_sqf_part(F, K)
238        Squarefree<BigInteger> engine;
239        engine = SquarefreeFactory.getImplementation(lc);
240        GenPolynomial<BigInteger> G;
241        G = engine.squarefreePart(f);
242        //System.out.println("G = " + G + ", G^2==f: " + G.multiply(G).equals(f));
243        //if dup_sqr(G, K) == F and dup_cyclotomic_p(G, K):
244        //   return True
245        if (G.multiply(G).equals(f) && isCyclotomicPolynomial(G)) {
246            return true;
247        }
248        return false;
249    }
250
251}