/* $Id: alglin2.c,v 1.223.2.1 2006/10/03 23:15:33 kb Exp $ Copyright (C) 2000 The PARI group. This file is part of the PARI/GP package. PARI/GP is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY WHATSOEVER. Check the License for details. You should have received a copy of it, along with the package; see the file 'COPYING'. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /********************************************************************/ /** **/ /** LINEAR ALGEBRA **/ /** (second part) **/ /** **/ /********************************************************************/ #include "pari.h" #include "paripriv.h" /*******************************************************************/ /* */ /* CHARACTERISTIC POLYNOMIAL */ /* */ /*******************************************************************/ GEN charpoly0(GEN x, long v, long flag) { if (v<0) v = 0; switch(flag) { case 0: return caradj(x,v,NULL); case 1: return caract(x,v); case 2: return carhess(x,v); } pari_err(flagerr,"charpoly"); return NULL; /* not reached */ } /* (v - x)^d */ static GEN caract_const(GEN x, long v, long d) { pari_sp av = avma; return gerepileupto(av, gpowgs(gadd(pol_x[v], gneg_i(x)), d)); } static GEN caract2_i(GEN p, GEN x, long v, GEN (subres_f)(GEN,GEN,GEN*)) { pari_sp av = avma; long d = degpol(p), dx; GEN ch, L; if (typ(x) != t_POL) return caract_const(x, v, d); dx = degpol(x); if (dx <= 0) return dx? monomial(gen_1, d, v): caract_const(gel(x,2), v, d); x = gneg_i(x); if (varn(x) == MAXVARN) { setvarn(x, 0); p = shallowcopy(p); setvarn(p, 0); } gel(x,2) = gadd(gel(x,2), pol_x[MAXVARN]); ch = subres_f(p, x, NULL); if (v != MAXVARN) { if (typ(ch) == t_POL && varn(ch) == MAXVARN) setvarn(ch, v); else ch = gsubst(ch, MAXVARN, pol_x[v]); } L = leading_term(ch); if (!gcmp1(L)) ch = gdiv(ch, L); return gerepileupto(av, ch); } /* return caract(Mod(x,p)) in variable v */ GEN caract2(GEN p, GEN x, long v) { return caract2_i(p,x,v, subresall); } GEN caractducos(GEN p, GEN x, long v) { return caract2_i(p,x,v, (GEN (*)(GEN,GEN,GEN*))resultantducos); } /* characteristic pol. Easy cases. Return NULL in case it's not so easy. */ static GEN easychar(GEN x, long v, GEN *py) { pari_sp av; long lx; GEN p1; switch(typ(x)) { case t_INT: case t_REAL: case t_INTMOD: case t_FRAC: case t_PADIC: p1=cgetg(4,t_POL); p1[1]=evalsigne(1) | evalvarn(v); gel(p1,2) = gneg(x); gel(p1,3) = gen_1; if (py) *py = mkmat(mkcolcopy(x)); return p1; case t_COMPLEX: case t_QUAD: if (py) pari_err(typeer,"easychar"); p1 = cgetg(5,t_POL); p1[1] = evalsigne(1) | evalvarn(v); gel(p1,2) = gnorm(x); av = avma; gel(p1,3) = gerepileupto(av, gneg(gtrace(x))); gel(p1,4) = gen_1; return p1; case t_POLMOD: if (py) pari_err(typeer,"easychar"); return caract2(gel(x,1), gel(x,2), v); case t_MAT: lx=lg(x); if (lx==1) { if (py) *py = cgetg(1,t_MAT); return pol_1[v]; } if (lg(x[1]) != lx) break; return NULL; } pari_err(mattype1,"easychar"); return NULL; /* not reached */ } GEN caract(GEN x, long v) { long k, n; pari_sp av=avma; GEN p1, p2, p3, x_k, Q; if ((p1 = easychar(x,v,NULL))) return p1; p1 = gen_0; Q = p2 = gen_1; n = lg(x)-1; x_k = monomial(gen_1, 1, v); for (k=0; k<=n; k++) { GEN mk = stoi(-k); gel(x_k,2) = mk; p3 = det(gaddmat_i(mk, x)); p1 = gadd(gmul(p1, x_k), gmul(gmul(p2, p3), Q)); if (k == n) break; Q = gmul(Q, x_k); p2 = divis(mulsi(k-n,p2), k+1); /* (-1)^{k} binomial(n,k) */ } return gerepileupto(av, gdiv(p1, mpfact(n))); } GEN caradj0(GEN x, long v) { return caradj(x,v,NULL); } /* assume x square matrice */ static GEN mattrace(GEN x) { pari_sp av; long i, lx = lg(x); GEN t; if (lx < 3) return lx == 1? gen_0: gcopy(gcoeff(x,1,1)); av = avma; t = gcoeff(x,1,1); for (i = 2; i < lx; i++) t = gadd(t, gcoeff(x,i,i)); return gerepileupto(av, t); } /* Using traces: return the characteristic polynomial of x (in variable v). * If py != NULL, the adjoint matrix is put there. */ GEN caradj(GEN x, long v, GEN *py) { pari_sp av, av0; long i, k, l; GEN p, y, t; if ((p = easychar(x, v, py))) return p; l = lg(x); av0 = avma; p = cgetg(l+2,t_POL); p[1] = evalsigne(1) | evalvarn(v); gel(p,l+1) = gen_1; if (l == 1) { if (py) *py = cgetg(1,t_MAT); return p; } av = avma; t = gerepileupto(av, gneg(mattrace(x))); gel(p,l) = t; if (l == 2) { if (py) *py = matid(1); return p; } if (l == 3) { GEN a = gcoeff(x,1,1), b = gcoeff(x,1,2); GEN c = gcoeff(x,2,1), d = gcoeff(x,2,2); if (py) { y = cgetg(3, t_MAT); gel(y,1) = mkcol2(gcopy(d), gneg(c)); gel(y,2) = mkcol2(gneg(b), gcopy(a)); *py = y; } av = avma; gel(p,2) = gerepileupto(av, gadd(gmul(a,d), gneg(gmul(b,c)))); return p; } av = avma; y = shallowcopy(x); for (i = 1; i < l; i++) gcoeff(y,i,i) = gadd(gcoeff(y,i,i), t); for (k = 2; k < l-1; k++) { GEN y0 = y; y = gmul(y, x); t = gdivgs(mattrace(y), -k); for (i = 1; i < l; i++) gcoeff(y,i,i) = gadd(gcoeff(y,i,i), t); y = gclone(y); /* beware: since y is a clone and t computed from it some components * may be out of stack (eg. INTMOD/POLMOD) */ gel(p,l-k+1) = gerepileupto(av, gcopy(t)); av = avma; if (k > 2) gunclone(y0); } t = gen_0; for (i=1; i1) pari_warn(warnmem,"hess, m = %ld", m); x = gerepilecopy(av2, x); } break; } return gerepilecopy(av,x); } GEN carhess(GEN x, long v) { pari_sp av; long lx, r, i; GEN y, H, X_h; if ((H = easychar(x,v,NULL))) return H; lx = lg(x); av = avma; y = cgetg(lx+1, t_VEC); gel(y,1) = pol_1[v]; H = hess(x); X_h = monomial(gen_1, 1, v); for (r = 1; r < lx; r++) { GEN p3 = gen_1, p4 = gen_0; for (i = r-1; i; i--) { p3 = gmul(p3, gcoeff(H,i+1,i)); p4 = gadd(p4, gmul(gmul(p3,gcoeff(H,i,r)), gel(y,i))); } gel(X_h,2) = gneg(gcoeff(H,r,r)); gel(y,r+1) = gsub(gmul(gel(y,r), X_h), p4); } return gerepileupto(av, gel(y,lx)); } /*******************************************************************/ /* */ /* NORM */ /* */ /*******************************************************************/ GEN cxnorm(GEN x) { return gadd(gsqr(gel(x,1)), gsqr(gel(x,2))); } GEN quadnorm(GEN x) { GEN u = gel(x,3), v = gel(x,2); GEN X = gel(x,1), b = gel(X,3), c = gel(X,2); GEN z = signe(b)? gmul(v, gadd(u,v)): gsqr(v); return gadd(z, gmul(c, gsqr(u))); } GEN RgXQ_norm(GEN x, GEN T) { pari_sp av; GEN L, y; if (typ(x) != t_POL) return gpowgs(x, degpol(T)); av = avma; y = subres(T, x); L = leading_term(T); if (gcmp1(L) || gcmp0(x)) return y; return gerepileupto(av, gdiv(y, gpowgs(L, degpol(x)))); } GEN gnorm(GEN x) { pari_sp av; long lx, i, tx=typ(x); GEN y; switch(tx) { case t_INT: return sqri(x); case t_REAL: return mulrr(x,x); case t_FRAC: return gsqr(x); case t_COMPLEX: av = avma; return gerepileupto(av, cxnorm(x)); case t_QUAD: av = avma; return gerepileupto(av, quadnorm(x)); case t_POL: case t_SER: case t_RFRAC: av = avma; return gerepileupto(av, greal(gmul(gconj(x),x))); case t_POLMOD: return RgXQ_norm(gel(x,2), gel(x,1)); case t_VEC: case t_COL: case t_MAT: lx=lg(x); y=cgetg(lx,tx); for (i=1; i1) pari_warn(warnmem,"gnorml2"); y = gerepileupto(av, y); } } return gerepileupto(av,y); } GEN QuickNormL2(GEN x, long prec) { pari_sp av = avma; GEN y = gmul(x, real_1(prec)); if (typ(x) == t_POL) *++y = evaltyp(t_VEC) | evallg(lg(x)-1); return gerepileupto(av, gnorml2(y)); } GEN gnorml1(GEN x,long prec) { pari_sp av = avma; long lx,i; GEN s; switch(typ(x)) { case t_INT: case t_REAL: case t_COMPLEX: case t_FRAC: case t_QUAD: return gabs(x,prec); case t_POL: lx = lg(x); s = gen_0; for (i=2; i= 2*/ if (lx != lg(x[1])) pari_err(mattype1,"gtrace"); return mattrace(x); } pari_err(typeer,"gtrace"); return NULL; /* not reached */ } /* Cholesky Decomposition for positive definite matrix a [matrix Q, Algo 2.7.6] * If a is not positive definite: * if flag is zero: raise an error * else: return NULL. */ GEN sqred1intern(GEN a) { pari_sp av = avma, lim=stack_lim(av,1); GEN b,p; long i,j,k, n = lg(a); if (typ(a)!=t_MAT) pari_err(typeer,"sqred1"); if (n == 1) return cgetg(1, t_MAT); if (lg(a[1])!=n) pari_err(mattype1,"sqred1"); b=cgetg(n,t_MAT); for (j=1; j1) pari_warn(warnmem,"sqred1"); b=gerepilecopy(av,b); } } return gerepilecopy(av,b); } GEN sqred1(GEN a) { GEN x = sqred1intern(a); if (!x) pari_err(talker,"not a positive definite matrix in sqred1"); return x; } GEN sqred3(GEN a) { pari_sp av = avma, lim = stack_lim(av,1); long i,j,k,l, n = lg(a); GEN p1,b; if (typ(a)!=t_MAT) pari_err(typeer,"sqred3"); if (lg(a[1])!=n) pari_err(mattype1,"sqred3"); av=avma; b=cgetg(n,t_MAT); for (j=1; j1) pari_warn(warnmem,"sqred3"); b=gerepilecopy(av,b); } } return gerepilecopy(av,b); } /* Gauss reduction (arbitrary symetric matrix, only the part above the * diagonal is considered). If signature is zero, return only the * signature, in which case gsigne() should be defined for elements of a. */ static GEN sqred2(GEN a, long signature) { GEN r, p; pari_sp av, av1, lim; long n, i, j, k, l, sp, sn, t; if (typ(a) != t_MAT) pari_err(typeer,"sqred2"); n = lg(a); if (n > 1 && lg(a[1]) != n) pari_err(mattype1,"sqred2"); n--; av = avma; r = const_vecsmall(n, 1); av1= avma; lim = stack_lim(av1,1); a = shallowcopy(a); t = n; sp = sn = 0; while (t) { k=1; while (k<=n && (!r[k] || gcmp0(gcoeff(a,k,k)))) k++; if (k<=n) { p = gcoeff(a,k,k); if (signature) { /* don't check if signature = 0: gsigne may fail ! */ if (gsigne(p) > 0) sp++; else sn++; } r[k] = 0; t--; for (j=1; j<=n; j++) gcoeff(a,k,j) = r[j] ? gdiv(gcoeff(a,k,j),p) : gen_0; for (i=1; i<=n; i++) if (r[i]) for (j=1; j<=n; j++) gcoeff(a,i,j) = r[j] ? gsub(gcoeff(a,i,j), gmul(gmul(gcoeff(a,k,i),gcoeff(a,k,j)),p)) : gen_0; gcoeff(a,k,k) = p; } else { for (k=1; k<=n; k++) if (r[k]) { l=k+1; while (l<=n && (!r[l] || gcmp0(gcoeff(a,k,l)))) l++; if (l <= n) { p = gcoeff(a,k,l); r[k] = r[l] = 0; sp++; sn++; t -= 2; for (i=1; i<=n; i++) if (r[i]) for (j=1; j<=n; j++) gcoeff(a,i,j) = r[j]? gsub(gcoeff(a,i,j), gdiv(gadd(gmul(gcoeff(a,k,i),gcoeff(a,l,j)), gmul(gcoeff(a,k,j),gcoeff(a,l,i))), p)) : gen_0; for (i=1; i<=n; i++) if (r[i]) { GEN u = gcoeff(a,k,i); gcoeff(a,k,i) = gdiv(gadd(u, gcoeff(a,l,i)), p); gcoeff(a,l,i) = gdiv(gsub(u, gcoeff(a,l,i)), p); } gcoeff(a,k,l) = gen_1; gcoeff(a,l,k) = gen_m1; gcoeff(a,k,k) = gmul2n(p,-1); gcoeff(a,l,l) = gneg(gcoeff(a,k,k)); if (low_stack(lim, stack_lim(av1,1))) { if(DEBUGMEM>1) pari_warn(warnmem,"sqred2"); a = gerepilecopy(av1, a); } break; } } if (k > n) break; } } if (!signature) return gerepilecopy(av, a); avma = av; return mkvec2s(sp, sn); } GEN sqred(GEN a) { return sqred2(a,0); } GEN signat(GEN a) { return sqred2(a,1); } static void rot(GEN x, GEN y, GEN s, GEN u) { GEN x1 = subrr(x,mulrr(s,addrr(y,mulrr(u,x)))); GEN y1 = addrr(y,mulrr(s,subrr(x,mulrr(u,y)))); affrr(x1,x); affrr(y1,y); } /* Diagonalization of a REAL symetric matrix. Return a vector [L, r]: * L = vector of eigenvalues * r = matrix of eigenvectors */ GEN jacobi(GEN a, long prec) { pari_sp av1; long de, e, e1, e2, i, j, p, q, l = lg(a); GEN c, s, t, u, ja, L, r, unr, x, y; if (typ(a) != t_MAT) pari_err(mattype1,"jacobi"); ja = cgetg(3,t_VEC); L = cgetg(l,t_COL); gel(ja,1) = L; r = cgetg(l,t_MAT); gel(ja,2) = r; if (l == 1) return ja; if (lg(a[1]) != l) pari_err(mattype1,"jacobi"); e1 = HIGHEXPOBIT-1; for (j=1; j e2) { e2 = e; p = i; q = j; } } } a = c; unr = real_1(prec); de = bit_accuracy(prec); /* e1 = min expo(a[i,i]) * e2 = max expo(a[i,j]), i != j */ while (e1-e2 < de) { pari_sp av2 = avma; /* compute associated rotation in the plane formed by basis vectors number * p and q */ x = divrr(subrr(gel(L,q),gel(L,p)), shiftr(gcoeff(a,p,q),1)); y = sqrtr(addrr(unr, mulrr(x,x))); t = divrr(unr, (signe(x)>0)? addrr(x,y): subrr(x,y)); c = sqrtr(addrr(unr,mulrr(t,t))); s = divrr(t,c); u = divrr(t,addrr(unr,c)); /* compute successive transforms of a and the matrix of accumulated * rotations (r) */ for (i=1; i e2) { e2=e; p=i; q=j; } for (i=j+1; i e2) { e2=e; p=j; q=i; } } avma = av2; } avma = av1; return ja; } /*************************************************************************/ /** **/ /** MATRICE RATIONNELLE --> ENTIERE **/ /** **/ /*************************************************************************/ GEN matrixqz0(GEN x,GEN p) { if (typ(p)!=t_INT) pari_err(typeer,"matrixqz0"); if (signe(p)>=0) return matrixqz(x,p); if (equaliu(p,1)) return matrixqz2(x); if (equaliu(p,2)) return matrixqz3(x); pari_err(flagerr,"matrixqz"); return NULL; /* not reached */ } static int ZV_isin(GEN x) { long i,l = lg(x); for (i=1; i m) pari_err(talker,"more rows than columns in matrixqz"); if (n==m) { p1 = det(x); if (gcmp0(p1)) pari_err(talker,"matrix of non-maximal rank in matrixqz"); avma = av; return matid(n); } /* m > n */ p1 = x; x = cgetg(n+1,t_MAT); for (j=1; j<=n; j++) { gel(x,j) = primpart(gel(p1,j)); if (!ZV_isin(gel(x,j))) pari_err(talker, "not a rational matrix in matrixqz"); } /* x integral */ if (gcmp0(p)) { p1 = gtrans(x); setlg(p1,n+1); p2 = det(p1); p1[n] = p1[n+1]; p2 = ggcd(p2,det(p1)); if (!signe(p2)) pari_err(impl,"matrixqz when the first 2 dets are zero"); if (gcmp1(p2)) return gerepilecopy(av,x); p1 = (GEN)factor(p2)[1]; } else p1 = mkvec(p); nfact = lg(p1)-1; av1 = avma; lim = stack_lim(av1,1); for (i=1; i<=nfact; i++) { p = gel(p1,i); for(;;) { p2 = FpM_ker(x, p); if (lg(p2)==1) break; p2 = centermod(p2,p); p3 = gdiv(gmul(x,p2), p); for (j=1; j1) pari_warn(warnmem,"matrixqz"); x = gerepilecopy(av1,x); } } } return gerepilecopy(av,x); } static GEN Z_V_mul(GEN u, GEN A) { if (gcmp1(u)) return A; if (gcmp_1(u)) return gneg(A); if (gcmp0(u)) return zerocol(lg(A)-1); return gmul(u,A); } static GEN QV_lincomb(GEN u, GEN v, GEN A, GEN B) { if (!signe(u)) return Z_V_mul(v,B); if (!signe(v)) return Z_V_mul(u,A); return gadd(Z_V_mul(u,A), Z_V_mul(v,B)); } /* cf ZV_elem */ /* zero aj = Aij (!= 0) using ak = Aik (maybe 0), via linear combination of * A[j] and A[k] of determinant 1. */ static void QV_elem(GEN aj, GEN ak, GEN A, long j, long k) { GEN p1,u,v,d, D; if (gcmp0(ak)) { lswap(A[j],A[k]); return; } D = lcmii(denom(aj), denom(ak)); if (!is_pm1(D)) { aj = gmul(aj,D); ak = gmul(ak,D); } d = bezout(aj,ak,&u,&v); /* frequent special case (u,v) = (1,0) or (0,1) */ if (!signe(u)) { /* ak | aj */ p1 = negi(diviiexact(aj,ak)); gel(A,j) = QV_lincomb(gen_1, p1, gel(A,j), gel(A,k)); return; } if (!signe(v)) { /* aj | ak */ p1 = negi(diviiexact(ak,aj)); gel(A,k) = QV_lincomb(gen_1, p1, gel(A,k), gel(A,j)); lswap(A[j], A[k]); return; } if (!is_pm1(d)) { aj = diviiexact(aj,d); ak = diviiexact(ak,d); } p1 = gel(A,k); aj = negi(aj); gel(A,k) = QV_lincomb(u,v, gel(A,j),p1); gel(A,j) = QV_lincomb(aj,ak, p1,gel(A,j)); } static GEN matrixqz_aux(GEN A) { pari_sp av = avma, lim = stack_lim(av,1); long i,j,k,n,m; GEN a; n = lg(A); if (n == 1) return cgetg(1,t_MAT); if (n == 2) return hnf(A); /* 1 col, maybe 0 */ m = lg(A[1]); for (i=1; i1) pari_warn(warnmem,"matrixqz_aux"); A = gerepilecopy(av,A); } } return m > 100? hnfall_i(A,NULL,1): hnf(A); } GEN matrixqz2(GEN x) { pari_sp av = avma; if (typ(x)!=t_MAT) pari_err(typeer,"matrixqz2"); x = shallowcopy(x); return gerepileupto(av, matrixqz_aux(x)); } GEN matrixqz3(GEN x) { pari_sp av = avma, av1, lim; long j,j1,k,m,n; GEN c; if (typ(x)!=t_MAT) pari_err(typeer,"matrixqz3"); n = lg(x); if (n==1) return gcopy(x); m = lg(x[1]); x = shallowcopy(x); c = cgetg(n,t_VECSMALL); for (j=1; j1) pari_warn(warnmem,"matrixqz3"); x = gerepilecopy(av1,x); } } return gerepileupto(av, matrixqz_aux(x)); } GEN intersect(GEN x, GEN y) { pari_sp av,tetpil; long j, lx = lg(x); GEN z; if (typ(x)!=t_MAT || typ(y)!=t_MAT) pari_err(typeer,"intersect"); if (lx==1 || lg(y)==1) return cgetg(1,t_MAT); av=avma; z=ker(shallowconcat(x,y)); for (j=lg(z)-1; j; j--) setlg(z[j],lx); tetpil=avma; return gerepile(av,tetpil,gmul(x,z)); } /**************************************************************/ /** **/ /** HERMITE NORMAL FORM REDUCTION **/ /** **/ /**************************************************************/ /* negate in place, except universal constants */ static GEN mynegi(GEN x) { long s = signe(x); if (!s) return gen_0; if (is_pm1(x)) return (s > 0)? gen_m1: gen_1; setsigne(x,-s); return x; } void ZV_neg_ip(GEN M) { long i; for (i = lg(M)-1; i; i--) gel(M,i) = mynegi(gel(M,i)); } GEN mathnf0(GEN x, long flag) { switch(flag) { case 0: return hnf(x); case 1: return hnfall(x); case 3: return hnfperm(x); case 4: return hnflll(x); default: pari_err(flagerr,"mathnf"); } return NULL; /* not reached */ } static GEN init_hnf(GEN x, GEN *denx, long *co, long *li, pari_sp *av) { if (typ(x) != t_MAT) pari_err(typeer,"mathnf"); *co=lg(x); if (*co==1) return NULL; /* empty matrix */ *li=lg(x[1]); *denx=denom(x); *av=avma; if (gcmp1(*denx)) /* no denominator */ { *denx = NULL; return shallowcopy(x); } return Q_muli_to_int(x, *denx); } /* check whether x is upper trinagular with positive diagonal coeffs */ int RgM_ishnf(GEN x) { long i,j, lx = lg(x); for (i=2; i 0); } /* same x is known to be integral */ int ZM_ishnf(GEN x) { long i,j, lx = lg(x); for (i=2; i 0); } GEN ZV_to_nv(GEN z) { long i, l = lg(z); GEN x = cgetg(l, t_VECSMALL); for (i=1; i 0) { for (i=1; i 0) A = ZV_lincomb1 (u, Y, X); else A = ZV_lincomb_1(u, Y, X); } } else if (is_pm1(u)) { if (su > 0) A = ZV_lincomb1 (v, X, Y); else A = ZV_lincomb_1(v, X, Y); } else { lx = lg(X); A = cgetg(lx,t_COL); m = lgefint(u)+lgefint(v); for (i=1; ico)? li-co: 0; if (lg(x2) != co) pari_err(talker,"incompatible matrices in hnf_special"); x2 = shallowcopy(x2); for (i=li-1; i>ldef; i--) { for (j=def-1; j; j--) { a = gcoeff(x,i,j); if (!signe(a)) continue; k = (j==1)? def: j-1; b = gcoeff(x,i,k); d = bezout(a,b,&u,&v); if (!is_pm1(d)) { a = diviiexact(a,d); b = diviiexact(b,d); } p1 = gel(x,j); b = negi(b); gel(x,j) = ZV_lincomb(a,b, gel(x,k), p1); gel(x,k) = ZV_lincomb(u,v, p1, gel(x,k)); p1 = gel(x2,j); gel(x2,j) = gadd(gmul(a, gel(x2,k)), gmul(b,p1)); gel(x2,k) = gadd(gmul(u,p1), gmul(v, gel(x2,k))); if (low_stack(lim, stack_lim(av,1))) { GEN *gptr[2]; gptr[0]=&x; gptr[1]=&x2; if (DEBUGMEM>1) pari_warn(warnmem,"hnf_special[1]. i=%ld",i); gerepilemany(av,gptr,2); } } p1 = gcoeff(x,i,def); s = signe(p1); if (s) { if (s < 0) { gel(x,def) = gneg(gel(x,def)); p1 = gcoeff(x,i,def); gel(x2,def)= gneg(gel(x2,def)); } for (j=def+1; j1) pari_warn(warnmem,"hnf_special[2]. i=%ld",i); gerepilemany(av,gptr,2); } } if (remove) { /* remove null columns */ for (i=1,j=1; j5) { fprintferr("Entering hnffinal:\n"); if (DEBUGLEVEL>6) { if (nlze) fprintferr("dep = %Z\n",dep); fprintferr("mit = %Z\n",matgen); fprintferr("B = %Z\n",B); } } /* H: lnz x lnz [disregarding initial 0 cols], U: col x col */ H = hnflll_i(matgen, &U, 0); H += (lg(H)-1 - lnz); H[0] = evaltyp(t_MAT) | evallg(lnz+1); /* Only keep the part above the H (above the 0s is 0 since the dep rows * are dependent from the ones in matgen) */ zc = col - lnz; /* # of 0 columns, correspond to units */ if (nlze) { dep = gmul(dep,U); dep += zc; } diagH1 = new_chunk(lnz+1); /* diagH1[i] = 0 iff H[i,i] != 1 (set later) */ av = avma; lim = stack_lim(av,1); Cnew = cgetg(co, typ(C)); setlg(C, col+1); p1 = gmul(C,U); for (j=1; j<=col; j++) Cnew[j] = p1[j]; for ( ; j5) fprintferr(" hnflll done\n"); /* Clean up B using new H */ for (s=0,i=lnz; i; i--) { GEN Di = gel(dep,i), Hi = gel(H,i); GEN h = gel(Hi,i); /* H[i,i] */ if ( (diagH1[i] = gcmp1(h)) ) { h = NULL; s++; } for (j=col+1; j1) pari_warn(warnmem,"hnffinal, i = %ld",i); gerepileall(av, 2, &Cnew, &B); } } p1 = cgetg(lnz+1,t_VEC); p2 = perm + nlze; for (i1=0, j1=lnz-s, i=1; i<=lnz; i++) /* push the 1 rows down */ if (diagH1[i]) p1[++j1] = p2[i]; else p2[++i1] = p2[i]; for (i=i1+1; i<=lnz; i++) p2[i] = p1[i]; if (DEBUGLEVEL>5) fprintferr(" first pass in hnffinal done\n"); /* s = # extra redundant generators taken from H * zc col-s co zc = col - lnz * [ 0 |dep | ] i = lnze + lnz - s = lig - s * nlze [--------| B' ] * [ 0 | H' | ] H' = H minus the s rows with a 1 on diagonal * i [--------|-----] lig-s (= "1-rows") * [ 0 | Id ] * [ | ] li */ lig -= s; col -= s; lnz -= s; Hnew = cgetg(lnz+1,t_MAT); depnew = cgetg(lnz+1,t_MAT); /* only used if nlze > 0 */ Bnew = cgetg(co-col,t_MAT); C = shallowcopy(Cnew); for (j=1,i1=j1=0; j<=lnz+s; j++) { GEN z = gel(H,j); if (diagH1[j]) { /* hit exactly s times */ i1++; C[i1+col] = Cnew[j+zc]; p1 = cgetg(lig+1,t_COL); gel(Bnew,i1) = p1; for (i=1; i<=nlze; i++) gel(p1,i) = gcoeff(dep,i,j); p1 += nlze; } else { j1++; C[j1+zc] = Cnew[j+zc]; p1 = cgetg(lnz+1,t_COL); gel(Hnew,j1) = p1; depnew[j1] = dep[j]; } for (i=k=1; k<=lnz; i++) if (!diagH1[i]) p1[k++] = z[i]; } for (j=s+1; j5) { fprintferr("Leaving hnffinal\n"); if (DEBUGLEVEL>6) { if (nlze) fprintferr("dep = %Z\n",depnew); fprintferr("mit = %Z\nB = %Z\nC = %Z\n", Hnew, Bnew, C); } } *ptdep = depnew; *ptC = C; *ptB = Bnew; return Hnew; } /* for debugging */ static void p_mat(long **mat, GEN perm, long k) { pari_sp av = avma; perm = vecslice(perm, k+1, lg(perm)-1); fprintferr("Permutation: %Z\n",perm); if (DEBUGLEVEL > 6) fprintferr("matgen = %Z\n", zm_to_ZM( rowpermute((GEN)mat, perm) )); avma = av; } /* M_k <- M_k + q M_i (col operations) */ static void elt_col(GEN Mk, GEN Mi, GEN q) { long j; if (is_pm1(q)) { if (signe(q) > 0) { for (j = lg(Mk)-1; j; j--) if (signe(Mi[j])) gel(Mk,j) = addii(gel(Mk,j), gel(Mi,j)); } else { for (j = lg(Mk)-1; j; j--) if (signe(Mi[j])) gel(Mk,j) = subii(gel(Mk,j), gel(Mi,j)); } } else for (j = lg(Mk)-1; j; j--) if (signe(Mi[j])) gel(Mk,j) = addii(gel(Mk,j), mulii(q, gel(Mi,j))); } static GEN col_dup(long l, GEN col) { GEN c = new_chunk(l); memcpy(c,col,l * sizeof(long)); return c; } /* HNF reduce a relation matrix (column operations + row permutation) ** Input: ** mat = (li-1) x (co-1) matrix of long ** C = r x (co-1) matrix of GEN ** perm= permutation vector (length li-1), indexing the rows of mat: easier ** to maintain perm than to copy rows. For columns we can do it directly ** using e.g. lswap(mat[i], mat[j]) ** k0 = integer. The k0 first lines of mat are dense, the others are sparse. ** Output: cf ASCII art in the function body ** ** row permutations applied to perm ** column operations applied to C. IN PLACE **/ GEN hnfspec_i(long** mat0, GEN perm, GEN* ptdep, GEN* ptB, GEN* ptC, long k0) { pari_sp av, lim; long co, n, s, nlze, lnz, nr, i, j, k, lk0, col, lig, *p, *matj, **mat; GEN p1, p2, matb, matbnew, vmax, matt, T, extramat, B, C, H, dep, permpro; const long li = lg(perm); /* = lg(mat0[1]) */ const long CO = lg(mat0); n = 0; /* -Wall */ C = *ptC; co = CO; if (co > 300 && co > 1.5 * li) { /* treat the rest at the end */ co = (long)(1.2 * li); setlg(C, co); } if (DEBUGLEVEL>5) { fprintferr("Entering hnfspec\n"); p_mat(mat0,perm,0); } matt = cgetg(co, t_MAT); /* dense part of mat (top) */ mat = (long**)cgetg(co, t_MAT); for (j = 1; j < co; j++) { mat[j] = col_dup(li, gel(mat0,j)); p1 = cgetg(k0+1,t_COL); gel(matt,j) = p1; matj = mat[j]; for (i=1; i<=k0; i++) gel(p1,i) = stoi(matj[perm[i]]); } vmax = cgetg(co,t_VECSMALL); av = avma; lim = stack_lim(av,1); i = lig = li-1; col = co-1; lk0 = k0; T = (k0 || (lg(C) > 1 && lg(C[1]) > 1))? matid(col): NULL; /* Look for lines with a single non-0 entry, equal to 1 in absolute value */ while (i > lk0) switch( count(mat,perm[i],col,&n) ) { case 0: /* move zero lines between k0+1 and lk0 */ lk0++; lswap(perm[i], perm[lk0]); i = lig; continue; case 1: /* move trivial generator between lig+1 and li */ lswap(perm[i], perm[lig]); if (T) lswap(T[n], T[col]); swap(mat[n], mat[col]); p = mat[col]; if (p[perm[lig]] < 0) /* = -1 */ { /* convert relation -g = 0 to g = 0 */ for (i=lk0+1; i5) { fprintferr(" after phase1:\n"); p_mat(mat,perm,0); } #define absmax(s,z) {long _z; _z = labs(z); if (_z > s) s = _z;} /* Get rid of all lines containing only 0 and +/- 1, keeping track of column * operations in T. Leave the rows 1..lk0 alone [up to k0, coefficient * explosion, between k0+1 and lk0, row is 0] */ s = 0; while (lig > lk0 && s < (long)(HIGHBIT>>1)) { for (i=lig; i>lk0; i--) if (count(mat,perm[i],col,&n) > 0) break; if (i == lk0) break; /* only 0, +/- 1 entries, at least 2 of them non-zero */ lswap(perm[i], perm[lig]); swap(mat[n], mat[col]); p = mat[col]; if (T) lswap(T[n], T[col]); if (p[perm[lig]] < 0) { for (i=lk0+1; i<=lig; i++) p[perm[i]] = -p[perm[i]]; if (T) ZV_neg_ip(gel(T,col)); } for (j=1; j1) pari_warn(warnmem,"hnfspec[1]"); if (T) T = gerepilecopy(av, T); else avma = av; } } /* As above with lines containing a +/- 1 (no other assumption). * Stop when single precision becomes dangerous */ for (j=1; j<=col; j++) { matj = mat[j]; for (s=0, i=lk0+1; i<=lig; i++) absmax(s, matj[i]); vmax[j] = s; } while (lig > lk0) { for (i=lig; i>lk0; i--) if ( (n = count2(mat,perm[i],col)) ) break; if (i == lk0) break; lswap(vmax[n], vmax[col]); lswap(perm[i], perm[lig]); swap(mat[n], mat[col]); p = mat[col]; if (T) lswap(T[n], T[col]); if (p[perm[lig]] < 0) { for (i=lk0+1; i<=lig; i++) p[perm[i]] = -p[perm[i]]; if (T) ZV_neg_ip(gel(T,col)); } for (j=1; j= (HIGHBIT-vmax[j]) / vmax[col]) goto END2; for (s=0, i=lk0+1; i<=lig; i++) absmax(s, matj[perm[i]] -= t*p[perm[i]]); vmax[j] = s; if (T) elt_col(gel(T,j), gel(T,col), stoi(-t)); } lig--; col--; if (low_stack(lim, stack_lim(av,1))) { if(DEBUGMEM>1) pari_warn(warnmem,"hnfspec[2]"); if (T) T = gerepilecopy(av,T); else avma = av; } } END2: /* clean up mat: remove everything to the right of the 1s on diagonal */ /* go multiprecision first */ matb = cgetg(co,t_MAT); /* bottom part (complement of matt) */ for (j=1; j5) { fprintferr(" after phase2:\n"); p_mat(mat,perm,lk0); } for (i=li-2; i>lig; i--) { long h, i0 = i - k0, k = i + co-li; GEN Bk = gel(matb,k); for (j=k+1; j 0) /* v = 1 */ { for (h=1; h1) pari_warn(warnmem,"hnfspec[3], (i,j) = %ld,%ld", i,j); for (h=1; h5) fprintferr(" matb cleaned up (using Id block)\n"); nlze = lk0 - k0; /* # of 0 rows */ lnz = lig-nlze+1; /* 1 + # of non-0 rows (!= 0...0 1 0 ... 0) */ if (T) matt = gmul(matt,T); /* update top rows */ extramat = cgetg(col+1,t_MAT); /* = new C minus the 0 rows */ for (j=1; j<=col; j++) { GEN z = gel(matt,j); GEN t = (gel(matb,j)) + nlze - k0; p2=cgetg(lnz,t_COL); gel(extramat,j) = p2; for (i=1; i<=k0; i++) p2[i] = z[i]; /* top k0 rows */ for ( ; i [%ld x %ld]",li-1,co-1, lig-1,col-1); if (CO > co) { /* treat the rest, N cols at a time (hnflll slow otherwise) */ const long N = 300; long a, L = CO - co, l = min(L, N); /* L columns to add */ GEN CC = *ptC, m0 = (GEN)mat0; setlg(CC, CO); /* restore */ CC += co-1; m0 += co-1; for (a = l;;) { GEN mat = cgetg(l + 1, t_MAT), emb = cgetg(l + 1, t_MAT); for (j = 1 ; j <= l; j++) { mat[j] = m0[j]; emb[j] = CC[j]; } H = hnfadd_i(H, perm, ptdep, ptB, &C, mat, emb); if (a == L) break; CC += l; m0 += l; a += l; if (a > L) { l = L - (a - l); a = L; } gerepileall(av, 4, &H,&C,ptB,ptdep); } } *ptC = C; return H; } GEN hnfspec(long** mat, GEN perm, GEN* ptdep, GEN* ptB, GEN* ptC, long k0) { pari_sp av = avma; GEN H = hnfspec_i(mat, perm, ptdep, ptB, ptC, k0); gerepileall(av, 4, ptC, ptdep, ptB, &H); return H; } /* HNF reduce x, apply same transforms to C */ GEN mathnfspec(GEN x, GEN *ptperm, GEN *ptdep, GEN *ptB, GEN *ptC) { long i,j,k,ly,lx = lg(x); GEN p1,p2,z,perm; if (lx == 1) return gcopy(x); ly = lg(x[1]); z = cgetg(lx,t_MAT); perm = cgetg(ly,t_VECSMALL); *ptperm = perm; for (i=1; i 1 && lg((*ptC)[1]) > 1) pari_err(impl,"mathnfspec with large entries"); x = hnf(x); lx = lg(x); j = ly; k = 0; for (i=1; i5) fprintferr(" 1st phase done\n"); permpro = imagecomplspec(extramat, &nlze); extramat = rowpermute(extramat, permpro); *ptB = rowpermute(B, permpro); permpro = vecpermute(perm, permpro); for (i=1; i<=lig; i++) perm[i] = permpro[i]; /* perm o= permpro */ *ptdep = rowslice(extramat, 1, nlze); matb = rowslice(extramat, nlze+1, lig); if (DEBUGLEVEL>5) fprintferr(" 2nd phase done\n"); H = hnffinal(matb,perm,ptdep,ptB,&Cnew); *ptC = shallowconcat(vecslice(C, 1, col-lH), Cnew); if (DEBUGLEVEL) { msgtimer("hnfadd (%ld + %ld)", lg(extratop)-1, lg(dep)-1); if (DEBUGLEVEL>7) fprintferr("H = %Z\nC = %Z\n",H,*ptC); } return H; } GEN hnfadd(GEN H, GEN perm, GEN* ptdep, GEN* ptB, GEN* ptC, /* cf hnfspec */ GEN extramat,GEN extraC) { pari_sp av = avma; H = hnfadd_i(H, perm, ptdep, ptB, ptC, ZM_to_zm(extramat), extraC); gerepileall(av, 4, ptC, ptdep, ptB, &H); return H; } static void ZV_neg(GEN x) { long i, lx = lg(x); for (i=1; i 0; k--) { t = gcoeff(C,k,c); if (gcmp0(t)) continue; setlg(C[c], k+1); ZV_elem(t, gcoeff(C,k,k), C, U, c, k); if (lgefint(gcoeff(C,k,k)) > lb) gel(C,k) = FpC_red(gel(C,k), b); if (j > 4) { GEN u = gel(U,k); long h; for (h=1; h lb) gel(u,h) = remii(gel(u,h), b); } } if (j == 1) t = gcoeff(C,1,1); else { GEN u,v; t = bezout(b, gcoeff(C,1,1), &u, &v); /* >= 0 */ if (signe(v) && !gcmp1(v)) gel(U,1) = ZV_Z_mul(gel(U,1), v); gcoeff(C,1,1) = t; } if (signe(t) && is_pm1(t)) break; } if (j >= l) pari_err(talker, "non coprime ideals in hnfmerge"); return gerepileupto(av, gmul(A,gel(U,1))); } /* remove: throw away lin.dep.columns */ GEN hnf0(GEN A, int remove) { pari_sp av0 = avma, av, lim; long s,li,co,i,j,k,def,ldef; GEN denx, a; if (typ(A) == t_VEC) return hnf_special(A,remove); A = init_hnf(A,&denx,&co,&li,&av); if (!A) return cgetg(1,t_MAT); lim = stack_lim(av,1); def=co-1; ldef=(li>co)? li-co: 0; for (i=li-1; i>ldef; i--) { for (j=def-1; j; j--) { a = gcoeff(A,i,j); if (!signe(a)) continue; /* zero a = Aij using b = Aik */ k = (j==1)? def: j-1; ZV_elem(a,gcoeff(A,i,k), A,NULL, j,k); if (low_stack(lim, stack_lim(av,1))) { if (DEBUGMEM>1) pari_warn(warnmem,"hnf[1]. i=%ld",i); A = gerepilecopy(av, A); } } s = signe(gcoeff(A,i,def)); if (s) { if (s < 0) ZV_neg(gel(A,def)); ZM_reduce(A, NULL, i,def); def--; } else if (ldef) ldef--; if (low_stack(lim, stack_lim(av,1))) { if (DEBUGMEM>1) pari_warn(warnmem,"hnf[2]. i=%ld",i); A = gerepilecopy(av, A); } } if (remove) { /* remove null columns */ for (i=1,j=1; j 0) { for (i = 1; i <= k; i++) if (signe(z[i])) gel(z,i) = modii(gel(z,i), p); } else { for (i = 1; i <= k; i++) if (signe(z[i])) gel(z,i) = modii(negi(gel(z,i)), p); } } else { for (i = 1; i <= k; i++) if (signe(z[i])) gel(z,i) = modii(mulii(u,gel(z,i)), p); } } static void FpV_red_part_ip(GEN z, GEN p, long k) { long i; for (i = 1; i <= k; i++) gel(z,i) = modii(gel(z,i), p); } /* dm = multiple of diag element (usually detint(x)) * flag & MODID: reduce mod dm * matid [ otherwise as above ]. * flag & PART: don't reduce once diagonal is known; */ static GEN allhnfmod(GEN x, GEN dm, int flag) { pari_sp av, lim; const int modid = (flag & hnf_MODID); long li, co, i, j, k, def, ldef, ldm; GEN a, b, p1, p2, u, v; if (typ(dm)!=t_INT) pari_err(typeer,"allhnfmod"); if (!signe(dm)) return hnf(x); if (typ(x)!=t_MAT) pari_err(typeer,"allhnfmod"); co = lg(x); if (co == 1) return cgetg(1,t_MAT); li = lg(x[1]); av = avma; lim = stack_lim(av,1); x = shallowcopy(x); ldef = 0; if (li > co) { ldef = li - co; if (!modid) pari_err(talker,"nb lines > nb columns in hnfmod"); } /* To prevent coeffs explosion, only reduce mod dm when lg() > ldm */ ldm = lgefint(dm); for (def = co-1,i = li-1; i > ldef; i--,def--) { gcoeff(x,i,def) = remii(gcoeff(x,i,def), dm); for (j = def-1; j; j--) { gcoeff(x,i,j) = remii(gcoeff(x,i,j), dm); a = gcoeff(x,i,j); if (!signe(a)) continue; k = (j==1)? def: j-1; gcoeff(x,i,k) = remii(gcoeff(x,i,k), dm); ZV_elem(a,gcoeff(x,i,k), x,NULL, j,k); p1 = gel(x,j); p2 = gel(x,k); for (k = 1; k < i; k++) { if (lgefint(p1[k]) > ldm) gel(p1,k) = remii(gel(p1,k), dm); if (lgefint(p2[k]) > ldm) gel(p2,k) = remii(gel(p2,k), dm); } if (low_stack(lim, stack_lim(av,1))) { if (DEBUGMEM>1) pari_warn(warnmem,"allhnfmod[1]. i=%ld",i); x = gerepilecopy(av, x); } } if (modid && !signe(gcoeff(x,i,def))) { /* missing pivot on line i, insert column */ GEN a = cgetg(co + 1, t_MAT); for (k = 1; k <= def; k++) a[k] = x[k]; gel(a,k++) = ZC_Cei(li-1, i, dm); for ( ; k <= co; k++) a[k] = x[k-1]; ldef--; if (ldef < 0) ldef = 0; co++; def++; x = a; } } x += co - li; if (modid) { /* w[li] is an accumulator, discarded at the end */ GEN w = cgetg(li+1, t_MAT); for (i = li-1; i > ldef; i--) w[i] = x[i]; for ( ; i > 0; i--) gel(w,i) = ZC_Cei(li-1, i, dm); x = w; for (i = li-1; i > 0; i--) { /* check that dm*Id \subset L + add up missing dm*Id components */ GEN d, c; d = bezout(gcoeff(x,i,i),dm, &u,&v); gcoeff(x,i,i) = d; if (is_pm1(d)) { FpV_Fp_mul_part_ip(gel(x,i), u, dm, i-1); continue; } c = cgetg(li, t_COL); for (j = 1; j < i; j++) gel(c,j) = remii(gcoeff(x,j,i),d); for ( ; j ldef; j--) { GEN a = gcoeff(x, j, li); if (!signe(a)) continue; ZV_elem(a, gcoeff(x,j,j), x, NULL, li,j); FpV_red_part_ip(gel(x,li), dm, j-1); FpV_red_part_ip(gel(x,j), dm, j-1); if (low_stack(lim, stack_lim(av,1))) { if (DEBUGMEM>1) pari_warn(warnmem,"allhnfmod[2]. i=%ld", i); x = gerepilecopy(av, x); } } } } else { b = dm; for (i = li-1; i > 0; i--) { GEN d = bezout(gcoeff(x,i,i),b, &u,&v); gcoeff(x,i,i) = d; FpV_Fp_mul_part_ip(gel(x,i), u, b, i-1); if (i > 1) b = diviiexact(b,d); } } x[0] = evaltyp(t_MAT) | evallg(li); /* kill 0 columns / discard accumulator */ if (flag & hnf_PART) return x; if (modid) b = const_vec(li-1, dm); else { /* compute optimal value for dm */ b = cgetg(li, t_VEC); gel(b,1) = gcoeff(x,1,1); for (i = 2; i < li; i++) gel(b,i) = mulii(gel(b,i-1), gcoeff(x,i,i)); } dm = b; for (i = li-2; i > 0; i--) { GEN diag = gcoeff(x,i,i); ldm = lgefint(dm[i]); for (j = i+1; j < li; j++) { b = negi(truedivii(gcoeff(x,i,j), diag)); p1 = ZV_lincomb(gen_1,b, gel(x,j), gel(x,i)); gel(x,j) = p1; for (k=1; k ldm) gel(p1,k) = remii(gel(p1,k), gel(dm,i)); if (low_stack(lim, stack_lim(av,1))) { if (DEBUGMEM>1) pari_warn(warnmem,"allhnfmod[2]. i=%ld", i); gerepileall(av, 2, &x, &dm); diag = gcoeff(x,i,i); } } } return gerepilecopy(av, x); } GEN hnfmod(GEN x, GEN detmat) { return allhnfmod(x,detmat, 0); } GEN hnfmodid(GEN x, GEN p) { return allhnfmod(x, p, hnf_MODID); } GEN hnfmodidpart(GEN x, GEN p) { return allhnfmod(x, p, hnf_MODID|hnf_PART); } /***********************************************************************/ /* */ /* HNFLLL (Havas, Majewski, Mathews) */ /* */ /***********************************************************************/ static void Minus(long j, GEN **lambda) { long k, n = lg(lambda); for (k=1 ; k 0) q = diviiround(lambda[k][j], D[j]); else return; if (signe(q)) { GEN *Lk = lambda[k], *Lj = lambda[j]; q = mynegi(q); if (*row0) elt_col(gel(A,k),gel(A,j),q); if (B) elt_col(gel(B,k),gel(B,j),q); Lk[j] = addii(Lk[j], mulii(q,D[j])); if (is_pm1(q)) { if (signe(q) > 0) { for (i=1; i>1; i; i--) { cB[h-i] = cA[i]; cB[i] = cA[h-i]; } } return B; } GEN hnflll_i(GEN A, GEN *ptB, int remove) { pari_sp av = avma, lim = stack_lim(av,3); long m1 = 1, n1 = 1; /* alpha = m1/n1. Maybe 3/4 here ? */ long do_swap,i,n,k; GEN z,B, **lambda, *D; if (typ(A) != t_MAT) pari_err(typeer,"hnflll"); n = lg(A); A = ZM_copy(fix_rows(A)); B = ptB? matid(n-1): NULL; D = (GEN*)cgetg(n+1,t_VEC); lambda = (GEN**) cgetg(n,t_MAT); D++; for (i=0; i 2) k--; } else { for (i=k-2; i; i--) { long row0, row1; reduce2(A,B,k,i,&row0,&row1,lambda,D); if (low_stack(lim, stack_lim(av,3))) { GEN b = (GEN)(D-1); if (DEBUGMEM) pari_warn(warnmem,"hnflll (reducing), i = %ld",i); gerepileall(av, B? 4: 3, &A, &lambda, &b, &B); D = (GEN*)(b+1); } } k++; } if (low_stack(lim, stack_lim(av,3))) { GEN b = (GEN)(D-1); if (DEBUGMEM) pari_warn(warnmem,"hnflll, k = %ld / %ld",k,n-1); gerepileall(av, B? 4: 3, &A, &lambda, &b, &B); D = (GEN*)(b+1); } } /* handle trivial case: return negative diag coefficient otherwise */ if (n == 2) (void)findi_normalize(gel(A,1), B,1,lambda); A = fix_rows(A); if (remove) { for (i = 1; i < n; i++) if (findi(gel(A,i))) break; i--; A += i; A[0] = evaltyp(t_MAT) | evallg(n-i); } gerepileall(av, B? 2: 1, &A, &B); if (B) *ptB = B; return A; } GEN hnflll(GEN A) { GEN B, z = cgetg(3, t_VEC); gel(z,1) = hnflll_i(A, &B, 0); gel(z,2) = B; return z; } /* Variation on HNFLLL: Extended GCD */ static void reduce1(GEN A, GEN B, long k, long j, GEN **lambda, GEN *D) { GEN q; long i; if (signe(A[j])) q = diviiround(gel(A,k),gel(A,j)); else if (absi_cmp(shifti(lambda[k][j], 1), D[j]) > 0) q = diviiround(lambda[k][j], D[j]); else return; if (signe(q)) { GEN *Lk = lambda[k], *Lj = lambda[j]; q = mynegi(q); gel(A,k) = addii(gel(A,k), mulii(q,gel(A,j))); elt_col(gel(B,k),gel(B,j),q); Lk[j] = addii(Lk[j], mulii(q,D[j])); for (i=1; i 2) k--; } else { for (i=k-2; i; i--) reduce1(A,B,k,i,lambda,D); k++; } } if (signe(A[n-1]) < 0) { gel(A,n-1) = mynegi(gel(A,n-1)); ZV_neg_ip(gel(B,n-1)); } return gerepilecopy(av, mkvec2(gel(A,n-1), B)); } /* HNF with permutation. */ GEN hnfperm_i(GEN A, GEN *ptU, GEN *ptperm) { GEN U, c, l, perm, d, p, q, b; pari_sp av = avma, av1, lim; long r, t, i, j, j1, k, m, n; if (typ(A) != t_MAT) pari_err(typeer,"hnfperm"); n = lg(A)-1; if (!n) { if (ptU) *ptU = cgetg(1,t_MAT); if (ptperm) *ptperm = cgetg(1,t_VEC); return cgetg(1, t_MAT); } m = lg(A[1])-1; c = const_vecsmall(m, 0); l = const_vecsmall(n, 0); perm = cgetg(m+1, t_VECSMALL); av1 = avma; lim = stack_lim(av1,1); A = shallowcopy(A); U = ptU? matid(n): NULL; /* U base change matrix : A0*U = A all along */ for (r=0, k=1; k <= n; k++) { for (j=1; j 0) { p = q; t = i; } } perm[++r] = l[k] = t; c[t] = k; if (signe(p) < 0) { ZV_neg(gel(A,k)); if (U) ZV_neg(gel(U,k)); p = gcoeff(A,t,k); } /* p > 0 */ for (j=1; j1) pari_warn(warnmem,"hnfperm"); gerepileall(av1, U? 2: 1, &A, &U); } } if (r < m) { for (i=1,k=r; i<=m; i++) if (!c[i]) perm[++k] = i; } /* We have A0*U=A, U in Gl(n,Z) * basis for Im(A): columns of A s.t l[j]>0 (r cols) * basis for Ker(A): columns of U s.t l[j]=0 (n-r cols) */ p = cgetg(r+1,t_MAT); for (i=1; i<=m/2; i++) lswap(perm[i], perm[m+1-i]); if (U) { GEN u = cgetg(n+1,t_MAT); for (t=1,k=r,j=1; j<=n; j++) if (l[j]) { u[k + n-r] = U[j]; gel(p,k--) = vecpermute(gel(A,j), perm); } else u[t++] = U[j]; *ptU = u; *ptperm = perm; gerepileall(av, 3, &p, ptU, ptperm); } else { for (k=r,j=1; j<=n; j++) if (l[j]) gel(p,k--) = vecpermute(gel(A,j), perm); if (ptperm) *ptperm = perm; gerepileall(av, ptperm? 2: 1, &p, ptperm); } return p; } GEN hnfperm(GEN A) { GEN U, perm, y = cgetg(4, t_VEC); gel(y,1) = hnfperm_i(A, &U, &perm); gel(y,2) = U; gel(y,3) = vecsmall_to_vec(perm); return y; } /* Hermite Normal Form */ GEN hnfall_i(GEN A, GEN *ptB, long remove) { GEN B,c,h,x,p,a; pari_sp av = avma, av1, lim; long m,n,r,i,j,k,li; if (typ(A)!=t_MAT) pari_err(typeer,"hnfall"); n=lg(A)-1; if (!n) { if (ptB) *ptB = cgetg(1,t_MAT); return cgetg(1,t_MAT); } m = lg(A[1])-1; c = cgetg(m+1,t_VECSMALL); for (i=1; i<=m; i++) c[i]=0; h = cgetg(n+1,t_VECSMALL); for (j=1; j<=n; j++) h[j]=m; av1 = avma; lim = stack_lim(av1,1); A = shallowcopy(A); B = ptB? matid(n): NULL; r = n+1; for (li=m; li; li--) { for (j=1; jli; i--) { a = gcoeff(A,i,j); if (!signe(a)) continue; k = c[i]; /* zero a = Aij using Aik */ ZV_elem(a,gcoeff(A,i,k), A,B,j,k); ZM_reduce(A,B, i,k); if (low_stack(lim, stack_lim(av1,1))) { if (DEBUGMEM>1) pari_warn(warnmem,"hnfall[1], li = %ld", li); gerepileall(av1, B? 2: 1, &A, &B); } } x = gcoeff(A,li,j); if (signe(x)) break; h[j] = li-1; } if (j == r) continue; r--; if (j < r) /* A[j] != 0 */ { lswap(A[j], A[r]); if (B) lswap(B[j], B[r]); h[j]=h[r]; h[r]=li; c[li]=r; } p = gcoeff(A,li,r); if (signe(p) < 0) { ZV_neg(gel(A,r)); if (B) ZV_neg(gel(B,r)); p = gcoeff(A,li,r); } ZM_reduce(A,B, li,r); if (low_stack(lim, stack_lim(av1,1))) { if (DEBUGMEM>1) pari_warn(warnmem,"hnfall[2], li = %ld", li); gerepileall(av1, B? 2: 1, &A, &B); } } if (DEBUGLEVEL>5) fprintferr("\nhnfall, final phase: "); r--; /* first r cols are in the image the n-r (independent) last ones */ for (j=1; j<=r; j++) for (i=h[j]; i; i--) { a = gcoeff(A,i,j); if (!signe(a)) continue; k = c[i]; ZV_elem(a,gcoeff(A,i,k), A,B, j,k); ZM_reduce(A,B, i,k); if (low_stack(lim, stack_lim(av1,1))) { if (DEBUGMEM>1) pari_warn(warnmem,"hnfall[3], j = %ld", j); gerepileall(av1, B? 2: 1, &A, &B); } } if (DEBUGLEVEL>5) fprintferr("\n"); /* remove the first r columns */ if (remove) { A += r; A[0] = evaltyp(t_MAT) | evallg(n-r+1); } gerepileall(av, B? 2: 1, &A, &B); if (B) *ptB = B; return A; } GEN hnfall0(GEN A, long remove) { GEN B, z = cgetg(3, t_VEC); gel(z,1) = hnfall_i(A, &B, remove); gel(z,2) = B; return z; } GEN hnfall(GEN x) {return hnfall0(x,1);} /***************************************************************/ /** **/ /** SMITH NORMAL FORM REDUCTION **/ /** **/ /***************************************************************/ static GEN col_mul(GEN x, GEN c) { if (typ(x) == t_INT) { long s = signe(x); GEN xc = NULL; if (s) { if (!is_pm1(x)) xc = gmul(x,c); else xc = (s>0)? c: gneg_i(c); } return xc; } else return gmul(x, c); } static void do_zero(GEN x) { long i, lx = lg(x); for (i=1; i 0) {*pu=gen_1; return a;} else {*pu=gen_m1; return absi(a);} } if (sa > 0) { *pa = *pu = gen_1; *pb = gen_m1; return a; } *pa = *pu = gen_m1; *pb = gen_1; return b; } d = bezout(a,b, pu,pv); *pa = diviiexact(a, d); *pb = diviiexact(b, d); return d; } static int negcmpii(GEN x, GEN y) { return -cmpii(x,y); } /* Return the SNF D of matrix X. If ptU/ptV non-NULL set them to U/V * to that D = UXV */ GEN smithall(GEN x, GEN *ptU, GEN *ptV) { pari_sp av0 = avma, av, lim = stack_lim(av0,1); long i, j, k, m0, m, n0, n; GEN p1, u, v, U, V, V0, mdet, ys, perm = NULL; if (typ(x)!=t_MAT) pari_err(typeer,"smithall"); n0 = n = lg(x)-1; if (!n) { if (ptU) *ptU = cgetg(1,t_MAT); if (ptV) *ptV = cgetg(1,t_MAT); return cgetg(1,(ptV||ptU)?t_MAT:t_VEC); } av = avma; m0 = m = lg(x[1])-1; for (j=1; j<=n; j++) for (i=1; i<=m; i++) if (typ(gcoeff(x,i,j)) != t_INT) pari_err(talker,"non integral matrix in smithall"); U = ptU? gen_1: NULL; /* TRANSPOSE of row transform matrix [act on columns]*/ V = ptV? gen_1: NULL; V0 = NULL; x = shallowcopy(x); if (m == n && ZM_ishnf(x)) { mdet = dethnf_i(x); if (V) *ptV = matid(n); } else { mdet = detint(x); if (signe(mdet)) { if (!V) p1 = hnfmod(x,mdet); else { if (m == n) { p1 = hnfmod(x,mdet); *ptV = gauss(x,p1); } else p1 = hnfperm_i(x, ptV, ptU? &perm: NULL); } mdet = dethnf_i(p1); } else p1 = hnfperm_i(x, ptV, ptU? &perm: NULL); x = p1; } n = lg(x)-1; if (V) { V = *ptV; if (n != n0) { V0 = vecslice(V, 1, n0 - n); /* kernel */ V = vecslice(V, n0-n+1, n0); av = avma; } } /* independent columns */ if (!signe(mdet)) { if (n) { x = smithall(shallowtrans(x), ptV, ptU); /* ptV, ptU swapped! */ if (typ(x) == t_MAT && n != m) x = shallowtrans(x); if (V) V = gmul(V, shallowtrans(*ptV)); if (U) U = *ptU; /* TRANSPOSE */ } else /* 0 matrix */ { x = cgetg(1,t_MAT); if (V) V = cgetg(1, t_MAT); if (U) U = matid(m); } goto THEEND; } if (U) U = matid(n); /* square, maximal rank n */ p1 = gen_sort(mattodiagonal_i(x), cmp_IND | cmp_C, &negcmpii); ys = cgetg(n+1,t_MAT); for (j=1; j<=n; j++) gel(ys,j) = vecpermute(gel(x, p1[j]), p1); x = ys; if (U) U = vecpermute(U, p1); if (V) V = vecpermute(V, p1); p1 = hnfmod(x, mdet); if (V) V = gmul(V, gauss(x,p1)); x = p1; if (DEBUGLEVEL>7) fprintferr("starting SNF loop"); for (i=n; i>1; i--) { if (DEBUGLEVEL>7) fprintferr("\ni = %ld: ",i); for(;;) { int c = 0; GEN a, b; for (j=i-1; j>=1; j--) { b = gcoeff(x,i,j); if (!signe(b)) continue; a = gcoeff(x,i,i); ZV_elem(b, a, x,V, j,i); if (low_stack(lim, stack_lim(av,1))) { if (DEBUGMEM>1) pari_warn(warnmem,"[1]: smithall i = %ld", i); snf_pile(av, &x,&U,&V); } } if (DEBUGLEVEL>7) fprintferr("; "); for (j=i-1; j>=1; j--) { GEN d; b = gcoeff(x,j,i); if (!signe(b)) continue; a = gcoeff(x,i,i); d = bezout_step(&a, &b, &u, &v); for (k = 1; k < i; k++) { GEN t = addii(mulii(u,gcoeff(x,i,k)),mulii(v,gcoeff(x,j,k))); gcoeff(x,j,k) = subii(mulii(a,gcoeff(x,j,k)), mulii(b,gcoeff(x,i,k))); gcoeff(x,i,k) = t; } gcoeff(x,j,i) = gen_0; gcoeff(x,i,i) = d; if (U) update(u,v,a,b,(GEN*)(U+i),(GEN*)(U+j)); if (low_stack(lim, stack_lim(av,1))) { if (DEBUGMEM>1) pari_warn(warnmem,"[2]: smithall, i = %ld", i); snf_pile(av, &x,&U,&V); } c = 1; } if (!c) { b = gcoeff(x,i,i); if (!signe(b) || is_pm1(b)) break; for (k=1; k1) pari_warn(warnmem,"[3]: smithall"); snf_pile(av, &x,&U,&V); } } } if (DEBUGLEVEL>7) fprintferr("\n"); for (k=1; k<=n; k++) if (signe(gcoeff(x,k,k)) < 0) { if (V) gel(V,k) = gneg(gel(V,k)); gcoeff(x,k,k) = negi(gcoeff(x,k,k)); } THEEND: if (!U && !V) { if (typ(x) == t_MAT) x = mattodiagonal_i(x); m = lg(x)-1; if (m0 > m) x = shallowconcat(zerovec(m0-m), x); return gerepilecopy(av0, x); } if (V0) { x = shallowconcat(zeromat(m,n0-n), x); if (V) V = shallowconcat(V0, V); } if (U) { U = shallowtrans(U); if (perm) U = vecpermute(U, perm_inv(perm)); } snf_pile(av0, &x,&U,&V); if (ptU) *ptU = U; if (ptV) *ptV = V; return x; } GEN smith(GEN x) { return smithall(x, NULL,NULL); } GEN smith2(GEN x) { GEN z = cgetg(4, t_VEC); gel(z,3) = smithall(x, (GEN*)(z+1),(GEN*)(z+2)); return z; } /* Assume z was computed by [g]smithall(). Remove the 1s on the diagonal */ GEN smithclean(GEN z) { long i,j,l,c; GEN u,v,y,d,p1; if (typ(z) != t_VEC) pari_err(typeer,"smithclean"); l = lg(z); if (l == 1) return cgetg(1,t_VEC); u=gel(z,1); if (l != 4 || typ(u) != t_MAT) { for (c=1; c=2; i--) { for(;;) { GEN a, b, d; int c = 0; for (j=i-1; j>=1; j--) { b = gcoeff(x,i,j); if (gcmp0(b)) continue; a = gcoeff(x,i,i); d = gbezout_step(&b, &a, &v, &u); for (k = 1; k < i; k++) { GEN t = gadd(gmul(u,gcoeff(x,k,i)),gmul(v,gcoeff(x,k,j))); gcoeff(x,k,j) = gsub(gmul(a,gcoeff(x,k,j)),gmul(b,gcoeff(x,k,i))); gcoeff(x,k,i) = t; } gcoeff(x,i,j) = gen_0; gcoeff(x,i,i) = d; if (all) update(u,v,a,b,(GEN*)(V+i),(GEN*)(V+j)); } for (j=i-1; j>=1; j--) { b = gcoeff(x,j,i); if (gcmp0(b)) continue; a = gcoeff(x,i,i); d = gbezout_step(&b, &a, &v, &u); for (k = 1; k < i; k++) { GEN t = gadd(gmul(u,gcoeff(x,i,k)),gmul(v,gcoeff(x,j,k))); gcoeff(x,j,k) = gsub(gmul(a,gcoeff(x,j,k)),gmul(b,gcoeff(x,i,k))); gcoeff(x,i,k) = t; } gcoeff(x,j,i) = gen_0; gcoeff(x,i,i) = d; if (all) update(u,v,a,b,(GEN*)(U+i),(GEN*)(U+j)); c = 1; } if (!c) { b = gcoeff(x,i,i); if (degpol(b) <= 0) break; for (k=1; k 16 + gexpo(b) - bit_accuracy(gprecision(r))) ) break; } if (j != i) break; } if (k == i) break; for (j=1; j<=i; j++) gcoeff(x,i,j) = gadd(gcoeff(x,i,j),gcoeff(x,k,j)); if (all) gel(U,i) = gadd(gel(U,i),gel(U,k)); } if (low_stack(lim, stack_lim(av,1))) { if (DEBUGMEM>1) pari_warn(warnmem,"gsmithall"); gerepileall(av, all? 3: 1, &x, &U, &V); } } } for (k=1; k<=n; k++) { GEN T = gcoeff(x,k,k); if (typ(T) == t_POL && signe(T)) { GEN d = leading_term(T); while (gcmp0(d) || ( typ(d) == t_REAL && lg(d) == 3 && gexpo(T) - expo(d) > (long)BITS_IN_LONG)) { T = normalizepol_i(T, lg(T)-1); if (!signe(T)) { gcoeff(x,k,k) = T; continue; } d = leading_term(T); } if (!gcmp1(d)) { gcoeff(x,k,k) = gdiv(T,d); if (all) gel(V,k) = gdiv(gel(V,k), d); } } } z = all? mkvec3(shallowtrans(U), V, x): mattodiagonal_i(x); return gerepilecopy(av, z); } GEN matsnf0(GEN x,long flag) { pari_sp av = avma; if (flag > 7) pari_err(flagerr,"matsnf"); if (typ(x) == t_VEC && flag & 4) return smithclean(x); if (flag & 2) x = flag&1 ? gsmith2(x): gsmith(x); else x = flag&1 ? smith2(x): smith(x); if (flag & 4) x = gerepileupto(av, smithclean(x)); return x; } GEN gsmith(GEN x) { return gsmithall(x,0); } GEN gsmith2(GEN x) { return gsmithall(x,1); } /* H relation matrix among row of generators g in HNF. Let URV = D its SNF, * newU R newV = newD its clean SNF (no 1 in Dnew). Return the diagonal of * newD, newU and newUi such that 1/U = (newUi, ?). * Rationale: let (G,0) = g Ui be the new generators then * 0 = G U R --> G D = 0, g = G newU and G = g newUi */ GEN smithrel(GEN H, GEN *newU, GEN *newUi) { GEN U, V, Ui, D = smithall(H, &U, newUi? &V: NULL); long i, j, c, l = lg(D); for (c=1; c U^-1 mod H = H(VD^-1 mod 1) mod H */ if (c == 1) *newUi = cgetg(1, t_MAT); else { /* Ui = ZM_inv(U, gen_1); setlg(Ui, c); */ setlg(V, c); V = FpM_red(V, gel(D,1)); Ui = gmul(H, V); for (i = 1; i < c; i++) gel(Ui,i) = gdivexact(gel(Ui,i), gel(D,i)); *newUi = reducemodHNF(Ui, H, NULL); } } return D; } /*********************************************************************** **** **** **** Frobenius form and Jordan form of a matrix **** **** **** ***********************************************************************/ GEN Frobeniusform(GEN V, long n) { long i,j,k; GEN M = cgetg(n+1, t_MAT); for(i=1; i<=n; i++) gel(M,i) = zerocol(n); for (k=1,i=1;i n) pari_err(talker, "accuracy lost in matfrobenius"); for (j=0; j n) pari_err(talker, "accuracy lost in matfrobenius"); gcoeff(M,k,i) = gen_1; for (j=1; j2) pari_err(flagerr,"matfrobenius"); A = matsnf0(M_x,3); D = smithclean(mattodiagonal_i(gel(A,3))); N = Frobeniusform(D, n); B = build_frobeniusbc(D, n); R = build_basischange(N, gmul(B,gel(A,1))); return gerepilecopy(ltop, mkvec2(N,R)); }