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}