Jeroen Demeyer on Tue, 10 Feb 2009 12:53:05 +0100 |
[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]
PATCH: polcyclo_eval in roots of unity |
Hello pari-dev,This patch fixes the libpari function polcyclo_eval (the GP function polcyclo) such that evaluating in a root of unity now works. Additionally, I changed it such that it can compute n-th cyclotomic polynomials where n is a t_INT (instead of a long).
The following now all work correctly: gp> polcyclo(20, I) %8 = 5 gp> polcyclo(10, Mod(3,11)) %17 = Mod(6, 11) gp> polcyclo(10, 2 + O(11)) %32 = O(11) gp> z15 = Mod(t,polcyclo(15,t)); gp> polcyclo(15, z15) %23 = 0 gp> polcyclo(30, z15) %24 = Mod(2*t^7 - 2*t^5 - 2*t^3 + 2*t, t^8 - t^7 + t^5 - t^4 + t^3 - t + 1) gp> polcyclo(30, -1.0) %5 = 1.0000000000000000000000000000000000000 gp> polcyclo(2^128-1, Mod(x,x^10)) %13 = Mod(-x^7 - x^6 - x^5 + x^2 + x + 1, x^10)However, there are still problems when working in a ring with zero-divisors, for example:
gp> polcyclo(10, Mod(3,4)) *** at top-level: polcyclo(10,Mod(3,4) *** ^-------------------- *** polcyclo: impossible inverse modulo: Mod(2, 4). Please test and let me know what you think. Cheers, Jeroen.
Index: src/basemath/arith2.c =================================================================== --- src/basemath/arith2.c (revision 11590) +++ src/basemath/arith2.c (working copy) @@ -862,10 +862,10 @@ GEN f=factor_Aurifeuille_prime(p,d[i]); B = Z_factor(f); A = merge_factor(A, B, (void*)&cmpii, cmp_nodata); - B = Z_factor(diviiexact(polcyclo_eval(d[i],p), f)); + B = Z_factor(diviiexact(polcyclo_eval(utoipos(d[i]), p), f)); } else - B = Z_factor(polcyclo_eval(d[i],p)); + B = Z_factor(polcyclo_eval(utoipos(d[i]), p)); A = merge_factor(A, B, (void*)&cmpii, cmp_nodata); } return gerepilecopy(av, A); Index: src/basemath/bibli2.c =================================================================== --- src/basemath/bibli2.c (revision 11590) +++ src/basemath/bibli2.c (working copy) @@ -389,56 +389,126 @@ return gerepilecopy(av, RgX_inflate(T,q)); } -/* cyclotomic polynomial */ +/* n-th cyclotomic polynomial evaluated in x */ GEN -polcyclo_eval(long n, GEN x) +polcyclo_eval(GEN n, GEN x) { - pari_sp av= avma; - GEN P, md, xd, yn, yd; - long l, s, i, j, q, mu, tx; + pari_sp av = avma; + GEN P, s; + GEN xd, md, ypos, yneg; + long l, i, j, tx, root_of_unity = 0; + GEN X = NULL; /* Assign NULL to prevent compiler warning */ - if (!x) return polcyclo(n, 0); - tx = typ(x); - if (gcmpX(x)) return polcyclo(n, varn(x)); - if (n <= 0) pari_err(talker, "argument must be positive in polcyclo"); - if (n == 1) return gsubgs(x, 1); - /* n >= 2 */ - P = gel(factoru(n), 1); l = lg(P)-1; - s = P[1]; for (i = 2; i <= l; i++) s *= P[i]; - q = n/s; - if (tx == t_INT && is_pm1(x)) + if (typ(n) != t_INT) pari_err(typeer, "polcyclo_eval"); + + if (!x) return polcyclo(itos(n), 0); + if (gcmpX(x)) return polcyclo(itos(n), varn(x)); /* x is a variable */ + + /* Special case n <= 1, so in what follows + * we may assume that n >= 2. */ + if (signe(n) <= 0) pari_err(talker, "argument must be positive in polcyclo_eval"); + if (cmpiu(n,1) == 0) return gsubgs(x, 1); /* n == 1: return x - 1 */ + + /* Factor n and let s be the largest squarefree divisor of n */ + P = gel(Z_factor(n), 1); l = lg(P)-1; + s = gel(P,1); for (i = 2; i <= l; i++) s = mulii(s, gel(P,i)); + + /* If n is not equal to s then n is not squarefree. By replacing x + * with x^(n/s), we may assume that n is squarefree in what follows. */ + if (cmpii(n,s) != 0) { - avma = av; - if (signe(x) > 0 || !odd(q)) return l == 1? utoipos(P[1]): gen_1; - /* return Phi_s(-1) */ - if (n == 2) return gen_0; - if (!odd(n) && l == 2) return utoipos(P[2]); - return gen_1; + x = powgi(x, divii(n,s)); + n = s; } - if (q != 1) { x = gpowgs(x, q); n = s; } /* replace n by squarefree part */ - if (tx == t_POL || tx == t_MAT || lg(x) > n) - return gerepileupto(av, poleval(polcyclo(n,0), x)); - xd = cgetg((1<<l) + 1, t_VEC); /* the x^d, where d | n */ + /* Handle the case x == 1 */ + if (gcmp1(x)) + { + /* n is prime => return n. + * However, we multiply with x to keep the type. + * For example if x is Mod(1,11) we want to return Mod(n, 11). */ + if (l == 1) return gerepileupto(av, gmul(x, n)); + + /* n is composite => return 1. */ + avma = av; return x; + } + + /* Heuristics: would it be more efficient to compute the n-th + * cyclotomic polynomial and then substituting the GEN x? */ + tx = typ(x); + if (tx == t_POL || tx == t_MAT || cmpui(lg(x), n) > 0) + return gerepileupto(av, poleval(polcyclo(itos(n), 0), x)); + + xd = cgetg((1<<l) + 1, t_VEC); /* the x^d, where d | n */ md = cgetg((1<<l) + 1, t_VECSMALL); /* the mu(d), where d | n */ + + /* We compute the n-th cyclotomic polynomial using the formula + * Prod_{d|n} (x^d-1)^mu(n/d). + * Note that n is squarefree, therefore mu(n/d) is either 1 or -1. + * We store the factors with mu(d) == 1 in ypos, the factors with + * mu(d) == -1 in yneg. Then at the end we return ypos/yneg if + * mu(n) == 1 and yneg/ypos if mu(n) == -1. */ + /* Initialize (handle the divisor d == 1) */ gel(xd, 1) = x; md[1] = 1; - mu = odd(l)? -1: 1; /* mu(n) */ - if (mu == 1) { yd = gen_1; yn = gsubgs(x,1); } - else { yn = gen_1; yd = gsubgs(x,1); } - for (i = 1; i <= l; i++) /* compute Prod_{d|n} (x^d-1)^mu(n/d) */ + ypos = gsubgs(x,1); + yneg = gen_1; + + for (i = 1; i <= l; i++) { - long ti = 1<<(i-1), p = P[i]; + long ti = 1<<(i-1); + GEN p = gel(P,i); for (j = 1; j <= ti; j++) { - GEN X = gpowgs(gel(xd,j), p); - gel(xd,ti+j) = X; + X = powgi(gel(xd,j), p); + gel(xd, ti+j) = X; md[ti+j] = -md[j]; - if (mu == md[ti+j]) yn = gmul(yn, gsubgs(X,1)); - else yd = gmul(yd, gsubgs(X,1)); + /* Handle the case where X == 1, i.e. when x^d = 1. + * We let the variable root_of_unity denote the smallest index + * ti+j such that X == 1. If no such index exists, then + * root_of_unity remains 0. Furthermore, we do not multiply + * with X - 1 if X == 1, we handle these factors at the end. */ + if (gcmp1(X)) + { + if (root_of_unity == 0) root_of_unity = ti+j; + } + else + { + if (md[ti+j] == 1) ypos = gmul(ypos, gsubgs(X, 1)); + else yneg = gmul(yneg, gsubgs(X, 1)); + } } } - return gerepileupto(av, gdiv(yn,yd)); + + /* Compute result in ypos */ + if (odd(l)) + ypos = gdiv(yneg, ypos); /* mu(n) == -1 */ + else + ypos = gdiv(ypos, yneg); /* mu(n) == 1 */ + + if (root_of_unity) + { + /* x is a root of unity. If root_of_unity == (1<<l), then x was a + * primitive n-th root of unity, so the result should be zero. + * We return x^n - 1 to preserve the type (instead of returning gen_0). + * Note that X is equal to x^n, which should be (inexactly) 1. */ + if (root_of_unity == (1<<l)) return gerepileupto(av, gsubgs(X, 1)); + + /* x is a primitive d-th root of unity, where d|n and d<n. + * We first multiply ypos by X = x^n = 1 to preserve the type. */ + ypos = gmul(ypos, X); + + /* Check bits of root_of_unity. If root_of_unity = (1<<l) - (1<<(i-1)) + * for some i < l, then n/d == P[i] and we need to multiply with P[i]; + * otherwise n/d is composite and nothing more needs to be done. */ + for (i = 1; i <= l; i++) + { + if (root_of_unity + (1<<(i-1)) == (1<<l)) {ypos = gmul(ypos, gel(P,i)); break;} + } + } + + return gerepileupto(av, ypos); } + /********************************************************************/ /** **/ /** HILBERT & PASCAL MATRICES **/ Index: src/functions/polynomials/polcyclo =================================================================== --- src/functions/polynomials/polcyclo (revision 11590) +++ src/functions/polynomials/polcyclo (working copy) @@ -1,20 +1,20 @@ Function: polcyclo Section: polynomials C-Name: polcyclo_eval -Prototype: LDG +Prototype: GDG Help: polcyclo(n,{a = 'x}): n-th cyclotomic polynomial evaluated at a. Description: - (small,?var):gen polcyclo($1,$2) - (small,gen):gen polcyclo_eval($1,$2) + (gen,?var):gen polcyclo($1,$2) + (gen,gen):gen polcyclo_eval($1,$2) Doc: $n$-th cyclotomic polynomial, evaluated at $a$ (\kbd{'x} by default). The integer $n$ must be positive. Algorithm used: reduce to the case where $n$ is squarefree; to compute the cyclotomic polynomial, use $\Phi_{np}(x)=\Phi_n(x^p)/\Phi(x)$; to compute it evaluated, use $\Phi_n(x) = \prod_{d\mid n} (x^d-1)^{\mu(n/d)}$. In the - evaluated case, the algorithm can deal with all rational values $a$; - otherwise it assumes that $a^d - 1$ is invertible for all $d\mid n$. If this - is not the case, use \kbd{subst(polcyclo(n),x,a)}. + evaluated case, the algorithm assumes the following: for all $d\mid n$, + either $a^d = 1$ or $a^d - 1$ is invertible. + If this is not the case, use \kbd{subst(polcyclo(n),x,a)}. Variant: The variant \fun{GEN}{polcyclo}{long n, long v} returns the $n$-th cyclotomic polynomial in variable $v$. Index: src/headers/paridecl.h =================================================================== --- src/headers/paridecl.h (revision 11590) +++ src/headers/paridecl.h (working copy) @@ -1074,7 +1074,7 @@ GEN convol(GEN x, GEN y); int gen_cmp_RgX(void *data, GEN x, GEN y); GEN polcyclo(long n, long v); -GEN polcyclo_eval(long n, GEN x); +GEN polcyclo_eval(GEN n, GEN x); GEN dirdiv(GEN x, GEN y); GEN dirmul(GEN x, GEN y); GEN gen_indexsort(GEN x, void *E, int (*cmp)(void*,GEN,GEN));