/* $Id: alglin2.c,v 1.32 2001/10/01 12:11:28 karim 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" /*******************************************************************/ /* */ /* CHARACTERISTIC POLYNOMIAL */ /* */ /*******************************************************************/ GEN charpoly0(GEN x, int v, long flag) { if (v<0) v = 0; switch(flag) { case 0: return caradj(x,v,0); case 1: return caract(x,v); case 2: return carhess(x,v); } err(flagerr,"charpoly"); return NULL; /* not reached */ } static GEN caract2_i(GEN p, GEN x, int v, GEN (subres_f)(GEN,GEN,GEN*)) { long av = avma, d; GEN p1, p2 = leading_term(p); if (!signe(x)) return gpowgs(polx[v], degpol(p)); if (typ(x) != t_POL) x = scalarpol(x,v); x = gneg_i(x); x[2] = ladd((GEN)x[2], polx[MAXVARN]); p1=subres_f(p, x, NULL); if (typ(p1) == t_POL && varn(p1)==MAXVARN) setvarn(p1,v); else p1 = gsubst(p1,MAXVARN,polx[v]); if (!gcmp1(p2) && (d=degpol(x)) > 0) p1 = gdiv(p1, gpuigs(p2,d)); return gerepileupto(av,p1); } /* return caract(Mod(x,p)) in variable v */ GEN caract2(GEN p, GEN x, int v) { return caract2_i(p,x,v, subresall); } GEN caractducos(GEN p, GEN x, int 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, int v, GEN *py) { long l,tetpil,lx; GEN p1,p2; switch(typ(x)) { case t_INT: case t_REAL: case t_INTMOD: case t_FRAC: case t_FRACN: case t_PADIC: p1=cgetg(4,t_POL); p1[1]=evalsigne(1) | evallgef(4) | evalvarn(v); p1[2]=lneg(x); p1[3]=un; if (py) { p2=cgetg(2,t_MAT); p2[1]=lgetg(2,t_COL); coeff(p2,1,1)=lcopy(x); *py=p2; } return p1; case t_COMPLEX: case t_QUAD: if (py) err(typeer,"easychar"); p1=cgetg(5,t_POL); p1[1]=evalsigne(1) | evallgef(5) | evalvarn(v); p1[2]=lnorm(x); l=avma; p2=gtrace(x); tetpil=avma; p1[3]=lpile(l,tetpil,gneg(p2)); p1[4]=un; return p1; case t_POLMOD: if (py) err(typeer,"easychar"); return caract2((GEN)x[1], (GEN)x[2], v); case t_MAT: lx=lg(x); if (lx==1) { if (py) *py = cgetg(1,t_MAT); return polun[v]; } if (lg(x[1]) != lx) break; return NULL; } err(mattype1,"easychar"); return NULL; /* not reached */ } GEN caract(GEN x, int v) { long n,k,l=avma,tetpil; GEN p1,p2,p3,p4,p5,p6; if ((p1 = easychar(x,v,NULL))) return p1; p1=gzero; p2=gun; n=lg(x)-1; if (n&1) p2=gneg_i(p2); p4=cgetg(3,t_RFRACN); p5=dummycopy(polx[v]); p4[2]=(long)p5; p6=cgeti(3); p6[1] = evalsigne(-1) | evallgefint(3); for (k=0; k<=n; k++) { p3=det(gsub(gscalmat(stoi(k),n), x)); p4[1]=lmul(p3,p2); p6[2]=k; p1=gadd(p4,p1); p5[2]=(long)p6; if (k!=n) p2=gdivgs(gmulsg(k-n,p2),k+1); } p2=mpfact(n); tetpil=avma; return gerepile(l,tetpil,gdiv((GEN) p1[1],p2)); } GEN caradj0(GEN x, long v) { return caradj(x,v,NULL); } /* 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) { long i,j,k,l,av,tetpil; GEN p,y,t,*gptr[2]; if ((p = easychar(x,v,py))) return p; l=lg(x); if (l==1) { p=polun[v]; if (py) *py=gcopy(x); return p; } if (l==2) { p=gsub(polx[v],gtrace(x)); if (py) *py=idmat(1); return p; } p=cgetg(l+2,t_POL); p[1]=evalsigne(1) | evallgef(l+2) | evalvarn(v); av=avma; t=gtrace(x); tetpil=avma; t=gerepile(av,tetpil,gneg(t)); p[l]=(long)t; p[l+1]=un; av=avma; y=cgetg(l,t_MAT); for (j=1; j1) err(warnmem,"gnorml2"); y = gerepileupto(av, y); } } return gerepileupto(av,y); } GEN QuickNormL2(GEN x, long prec) { long av = avma; GEN y = gmul(x, realun(prec)); if (typ(x) == t_POL) *++y = evaltyp(t_VEC) | evallg(lgef(x)-1); return gerepileupto(av, gnorml2(y)); } GEN gnorml1(GEN x,long prec) { ulong av = avma; long lx,i; GEN s; switch(typ(x)) { case t_INT: case t_REAL: case t_COMPLEX: case t_FRAC: case t_FRACN: case t_QUAD: return gabs(x,prec); case t_POL: lx = lgef(x); s = gzero; for (i=2; i=2*/ if (lx!=lg(x[1])) err(mattype1,"gtrace"); l=avma; p1=gcoeff(x,1,1); if (lx==2) return gcopy(p1); for (i=2; i1) err(warnmem,"sqred1"); b=gerepilecopy(av,b); } } return gerepilecopy(av,b); } GEN sqred1(GEN a) { return sqred1intern(a,0); } GEN sqred3(GEN a) { ulong av = avma, lim = stack_lim(av,1); long i,j,k,l, n = lg(a); GEN p1,b; if (typ(a)!=t_MAT) err(typeer,"sqred3"); if (lg(a[1])!=n) err(mattype1,"sqred3"); av=avma; b=cgetg(n,t_MAT); for (j=1; j1) err(warnmem,"sqred3"); b=gerepilecopy(av,b); } } return gerepilecopy(av,b); } /* Gauss reduction (arbitrary symetric matrix, only the part above the * diagonal is considered). If no_signature is zero, return only the * signature */ static GEN sqred2(GEN a, long no_signature) { GEN r,p,mun; ulong av,av1,lim; long n,i,j,k,l,sp,sn,t; if (typ(a)!=t_MAT) err(typeer,"sqred2"); n=lg(a); if (lg(a[1]) != n) err(mattype1,"sqred2"); av=avma; mun=negi(gun); r=new_chunk(n); for (i=1; i0) sp++; else sn++; r[k]=0; t--; for (j=1; j<=n; j++) coeff(a,k,j)=r[j] ? ldiv(gcoeff(a,k,j),p) : zero; for (i=1; i<=n; i++) if (r[i]) for (j=1; j<=n; j++) coeff(a,i,j) = r[j] ? lsub(gcoeff(a,i,j), gmul(gmul(gcoeff(a,k,i),gcoeff(a,k,j)),p)) : zero; coeff(a,k,k)=(long)p; } else { for (k=1; k<=n; k++) if (r[k]) { l=k+1; while (l<=n && (gcmp0(gcoeff(a,k,l)) || !r[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++) coeff(a,i,j) = r[j]? lsub(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)) : zero; coeff(a,k,i)=ldiv(gadd(gcoeff(a,k,i),gcoeff(a,l,i)),p); coeff(a,l,i)=ldiv(gsub(gcoeff(a,k,i),gcoeff(a,l,i)),p); } coeff(a,k,l)=un; coeff(a,l,k)=(long)mun; coeff(a,k,k)=lmul2n(p,-1); coeff(a,l,l)=lneg(gcoeff(a,k,k)); if (low_stack(lim, stack_lim(av1,1))) { if(DEBUGMEM>1) err(warnmem,"sqred2"); a=gerepilecopy(av1,a); } break; } } if (k>n) break; } } if (no_signature) return gerepilecopy(av,a); avma=av; a=cgetg(3,t_VEC); a[1]=lstoi(sp); a[2]=lstoi(sn); return a; } GEN sqred(GEN a) { return sqred2(a,1); } GEN signat(GEN a) { return sqred2(a,0); } /* Diagonalization of a REAL symetric matrix. Return a 2-component vector: * 1st comp = vector of eigenvalues * 2nd comp = matrix of eigenvectors */ GEN jacobi(GEN a, long prec) { long de,e,e1,e2,l,n,i,j,p,q,av1,av2; GEN c,s,t,u,ja,lambda,r,unr,x,y,x1,y1; if (typ(a)!=t_MAT) err(mattype1,"jacobi"); ja=cgetg(3,t_VEC); l=lg(a); n=l-1; e1=HIGHEXPOBIT-1; lambda=cgetg(l,t_COL); ja[1]=(long)lambda; for (j=1; j<=n; j++) { lambda[j]=lgetr(prec); gaffect(gcoeff(a,j,j), (GEN)lambda[j]); e=expo(lambda[j]); if (ee2) { e2=e; p=i; q=j; } } } a=c; unr = realun(prec); de=bit_accuracy(prec); /* e1 = min des expo des coeff diagonaux * e2 = max des expo des coeff extra-diagonaux * Test d'arret: e2 < e1-precision */ while (e1-e20)? divrr(unr,addrr(x,y)) : divrr(unr,subrr(x,y)); c=divrr(unr,mpsqrt(addrr(unr,mulrr(t,t)))); s=mulrr(t,c); u=divrr(s,addrr(unr,c)); /* Recalcul des transformees successives de a et de la matrice cumulee (r) des rotations : */ for (i=1; i e2) { e2=e; p=i; q=j; } for (i=j+1; i<=n; i++) if ((e=expo(gcoeff(a,j,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) err(typeer,"matrixqz0"); if (signe(p)>=0) return matrixqz(x,p); if (!cmpsi(-1,p)) return matrixqz2(x); if (!cmpsi(-2,p)) return matrixqz3(x); err(flagerr,"matrixqz"); return NULL; /* not reached */ } GEN matrixqz(GEN x, GEN p) { ulong av = avma, av1, lim; long i,j,j1,m,n,t,nfact; GEN p1,p2,p3,unmodp; if (typ(x)!=t_MAT) err(typeer,"matrixqz"); n=lg(x)-1; if (!n) return gcopy(x); m=lg(x[1])-1; if (n > m) err(talker,"more rows than columns in matrixqz"); if (n==m) { p1=det(x); if (gcmp0(p1)) err(talker,"matrix of non-maximal rank in matrixqz"); avma=av; return idmat(n); } p1=cgetg(n+1,t_MAT); for (j=1; j<=n; j++) { p2=gun; p3=(GEN)x[j]; for (i=1; i<=m; i++) { t=typ(p3[i]); if (t != t_INT && !is_frac_t(t)) err(talker,"not a rational or integral matrix in matrixqz"); p2=ggcd(p2,(GEN)p3[i]); } p1[j]=ldiv(p3,p2); } x=p1; unmodp=cgetg(3,t_INTMOD); unmodp[2]=un; if (gcmp0(p)) { p1=cgetg(n+1,t_MAT); p2=gtrans(x); for (j=1; j<=n; j++) p1[j]=p2[j]; p3=det(p1); p1[n]=p2[n+1]; p3=ggcd(p3,det(p1)); if (!signe(p3)) err(impl,"matrixqz when the first 2 dets are zero"); if (gcmp1(p3)) return gerepilecopy(av,x); p3=factor(p3); p1=(GEN)p3[1]; nfact=lg(p1)-1; } else { p1=cgetg(2,t_VEC); p1[1]=(long)p; nfact=1; } av1=avma; lim=stack_lim(av1,1); for (i=1; i<=nfact; i++) { p=(GEN)p1[i]; unmodp[1]=(long)p; for(;;) { p2=ker(gmul(unmodp,x)); if (lg(p2)==1) break; p2=centerlift(p2); p3=gdiv(gmul(x,p2),p); for (j=1; j1) err(warnmem,"matrixqz"); x=gerepilecopy(av1,x); } } } return gerepilecopy(av,x); } static GEN matrixqz_aux(GEN x, long m, long n) { ulong av = avma, lim = stack_lim(av,1); long i,j,k,in[2]; GEN p1; for (i=1; i<=m; i++) { for(;;) { long fl=0; for (j=1; j<=n; j++) if (!gcmp0(gcoeff(x,i,j))) { in[fl]=j; fl++; if (fl==2) break; } if (j>n) break; j=(gcmp(gabs(gcoeff(x,i,in[0]),DEFAULTPREC), gabs(gcoeff(x,i,in[1]),DEFAULTPREC)) > 0)? in[1]: in[0]; p1=gcoeff(x,i,j); for (k=1; k<=n; k++) if (k!=j) x[k]=lsub((GEN)x[k],gmul(ground(gdiv(gcoeff(x,i,k),p1)),(GEN)x[j])); } j=1; while (j<=n && gcmp0(gcoeff(x,i,j))) j++; if (j<=n) { p1=denom(gcoeff(x,i,j)); if (!gcmp1(p1)) x[j]=lmul(p1,(GEN)x[j]); } if (low_stack(lim, stack_lim(av,1))) { if(DEBUGMEM>1) err(warnmem,"matrixqz_aux"); x=gerepilecopy(av,x); } } return hnf(x); } GEN matrixqz2(GEN x) { long av = avma,m,n; if (typ(x)!=t_MAT) err(typeer,"matrixqz2"); n=lg(x)-1; if (!n) return gcopy(x); m=lg(x[1])-1; x=dummycopy(x); return gerepileupto(av, matrixqz_aux(x,m,n)); } GEN matrixqz3(GEN x) { long av=avma,av1,j,j1,k,m,n,lim; GEN c; if (typ(x)!=t_MAT) err(typeer,"matrixqz3"); n=lg(x)-1; if (!n) return gcopy(x); m=lg(x[1])-1; x=dummycopy(x); c=new_chunk(n+1); for (j=1; j<=n; j++) c[j]=0; av1=avma; lim=stack_lim(av1,1); for (k=1; k<=m; k++) { j=1; while (j<=n && (c[j] || gcmp0(gcoeff(x,k,j)))) j++; if (j<=n) { c[j]=k; x[j]=ldiv((GEN)x[j],gcoeff(x,k,j)); for (j1=1; j1<=n; j1++) if (j1!=j) x[j1]=lsub((GEN)x[j1],gmul(gcoeff(x,k,j1),(GEN)x[j])); if (low_stack(lim, stack_lim(av1,1))) { if(DEBUGMEM>1) err(warnmem,"matrixqz3"); x=gerepilecopy(av1,x); } } } return gerepileupto(av, matrixqz_aux(x,m,n)); } GEN intersect(GEN x, GEN y) { long av,tetpil,j, lx = lg(x); GEN z; if (typ(x)!=t_MAT || typ(y)!=t_MAT) err(typeer,"intersect"); if (lx==1 || lg(y)==1) return cgetg(1,t_MAT); av=avma; z=ker(concatsp(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 **/ /** **/ /**************************************************************/ 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: err(flagerr,"mathnf"); } return NULL; /* not reached */ } static GEN init_hnf(GEN x, GEN *denx, long *co, long *li, long *av) { if (typ(x) != t_MAT) 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 dummycopy(x); } return gmul(x,*denx); } GEN ZV_copy(GEN x) { long i, lx = lg(x); GEN y = cgetg(lx, t_COL); for (i=1; i 0) { for (i=1; i 0) { for (i=1; ico)? li-co: 0; if (lg(x2) != co) err(talker,"incompatible matrices in hnf_special"); x2 = dummycopy(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 = divii(a,d); b = divii(b,d); } p1 = (GEN)x[j]; b = negi(b); x[j] = (long)ZV_lincomb(a,b, (GEN)x[k], p1); x[k] = (long)ZV_lincomb(u,v, p1, (GEN)x[k]); p1 = (GEN)x2[j]; x2[j]= ladd(gmul(a, (GEN)x2[k]), gmul(b,p1)); x2[k] = ladd(gmul(u,p1), gmul(v, (GEN)x2[k])); if (low_stack(lim, stack_lim(av,1))) { GEN *gptr[2]; gptr[0]=&x; gptr[1]=&x2; if (DEBUGMEM>1) err(warnmem,"hnf_special[1]. i=%ld",i); gerepilemany(av,gptr,2); } } p1 = gcoeff(x,i,def); s = signe(p1); if (s) { if (s < 0) { x[def] = lneg((GEN)x[def]); p1 = gcoeff(x,i,def); x2[def]= lneg((GEN)x2[def]); } for (j=def+1; j1) err(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); } } p1 = hnflll(matgen); H = (GEN)p1[1]; /* lnz x lnz */ U = (GEN)p1[2]; /* col x col */ /* Only keep the part above the H (above the 0s is 0 since the dep rows * are dependant 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,t_MAT); 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 h = gcoeff(H,i,i); if ( (diagH1[i] = gcmp1(h)) ) { h = NULL; s++; } for (j=col+1; j1) err(warnmem,"hnffinal, i = %ld",i); gerepilemany(av,gptr,2); } } 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 = dummycopy(Cnew); for (j=1,i1=j1=0; j<=lnz+s; j++) { GEN z = (GEN)H[j]; if (diagH1[j]) { /* hit exactly s times */ i1++; p1 = cgetg(lig+1,t_COL); Bnew[i1] = (long)p1; C[i1+col] = Cnew[j+zc]; for (i=1; i<=nlze; i++) p1[i] = coeff(dep,i,j); p1 += nlze; } else { j1++; p1 = cgetg(lnz+1,t_COL); Hnew[j1] = (long)p1; C[j1+zc] = Cnew[j+zc]; if (nlze) 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); } } if (nlze) *ptdep = depnew; *ptC = C; *ptB = Bnew; return Hnew; } /* for debugging */ static void p_mat(long **mat, long *perm, long k0) { long av=avma, i,j; GEN p1, matj, matgen; long co = lg(mat); long li = lg(perm); fprintferr("Permutation: %Z\n",perm); matgen = cgetg(co,t_MAT); for (j=1; j 6) fprintferr("matgen = %Z\n",matgen); avma=av; } static GEN col_dup(long n, GEN col) { GEN c = new_chunk(n+1); memcpy(c,col,(n+1)*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. swap(mat[i], mat[j]) ** k0 = integer. The k0 first lines of mat are dense, the others are sparse. ** Also if k0=0, mat is modified in place [from mathnfspec], otherwise ** it is left alone [from buchall] ** Output: cf ASCII art in the function body ** ** row permutations applied to perm ** column operations applied to C. **/ GEN hnfspec(long** mat0, GEN perm, GEN* ptdep, GEN* ptB, GEN* ptC, long k0) { long av=avma,av2,*p,i,j,k,lk0,col,lig,*matj, **mat; long n,s,t,lim,nlze,lnz,nr; GEN p1,p2,matb,matbnew,vmax,matt,T,extramat; GEN B,H,dep,permpro; GEN *gptr[4]; long co = lg(mat0); long li = lg(perm); /* = lg(mat0[1]) */ int updateT = 1; if (!k0) mat = mat0; /* in place */ else { /* keep original mat0 safe, modify a copy */ mat = (long**)new_chunk(co); mat[0] = mat0[0]; for (j=1; j5) { fprintferr("Entering hnfspec\n"); p_mat(mat,perm,0); } matt = cgetg(co,t_MAT); /* dense part of mat (top) */ for (j=1; j 1 && lg((*ptC)[1]) > 1)) T = idmat(col); else { /* dummy ! */ GEN z = cgetg(1,t_COL); T = cgetg(co, t_MAT); updateT = 0; for (j=1; j lk0) switch( count(mat,perm[i],col,&n) ) { case 0: /* move zero lines between k0+1 and lk0 */ lk0++; swap(perm[i], perm[lk0]); i=lig; continue; case 1: /* move trivial generator between lig+1 and li */ swap(perm[i], perm[lig]); swap(T[n], T[col]); gswap(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;} #if 0 /* TODO: check, and put back in */ /* 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, coeff * explosion, between k0+1 and lk0, row is 0] */ s = 0; while (lig > lk0 && s < (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 */ swap(perm[i], perm[lig]); swap(T[n], T[col]); p1 = (GEN)T[col]; gswap(mat[n], mat[col]); p = mat[col]; if (p[perm[lig]] < 0) { for (i=lk0+1; i<=lig; i++) p[perm[i]] = -p[perm[i]]; T[col] = lneg(p1); } for (j=1; j1) err(warnmem,"hnfspec[1]"); T = gerepilecopy(av2, T); } } #endif /* 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; swap(perm[i], perm[lig]); swap(vmax[n], vmax[col]); gswap(mat[n], mat[col]); p = mat[col]; swap(T[n], T[col]); p1 = (GEN)T[col]; if (p[perm[lig]] < 0) { for (i=lk0+1; i<=lig; i++) p[perm[i]] = -p[perm[i]]; p1 = gneg(p1); T[col] = (long)p1; } 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; T[j] = (long)ZV_lincomb(gun,stoi(-t), (GEN)T[j],p1); } lig--; col--; if (low_stack(lim, stack_lim(av2,1))) { if(DEBUGMEM>1) err(warnmem,"hnfspec[2]"); T = gerepilecopy(av2,T); } } 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,k0); } for (i=li-2; i>lig; i--) { long i1, i0 = i - k0, k = i + co-li; GEN Bk = (GEN)matb[k]; GEN Tk = (GEN)T[k]; for (j=k+1; j 0) { /* p2 = 1 */ for (i1=1; i11) err(warnmem,"hnfspec[3], i = %ld", i); for (j=1; j5) { fprintferr(" matb cleaned up (using Id block)\n"); if (DEBUGLEVEL>6) outerr(matb); } nlze = lk0 - k0; /* # of 0 rows */ lnz = lig-nlze+1; /* 1 + # of non-0 rows (!= 0...0 1 0 ... 0) */ if (updateT) 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 = (GEN)matt[j]; GEN t = ((GEN)matb[j]) + nlze - k0; p2=cgetg(lnz,t_COL); extramat[j]=(long)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); 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) err(impl,"mathnfspec with large entries"); x = hnf(x); lx = lg(x); j = ly; k = 0; for (i=1; i5) { fprintferr("Entering hnfadd:\n"); if (DEBUGLEVEL>6) fprintferr("extramat = %Z\n",extramat); } /* col co * [ 0 |dep | ] * nlze [--------| B ] * [ 0 | H | ] * [--------|-----] lig * [ 0 | Id ] * [ | ] li */ lextra = lg(extramat)-1; extratop = cgetg(lextra+1,t_MAT); /* [1..lig] part (top) */ p2 = cgetg(lextra+1,t_MAT); /* bottom */ for (j=1; j<=lextra; j++) { GEN z = (GEN)extramat[j]; p1=cgetg(lig+1,t_COL); extratop[j] = (long)p1; for (i=1; i<=lig; i++) p1[i] = z[i]; p1=cgetg(lB,t_COL); p2[j] = (long)p1; p1 -= lig; for ( ; i5) { fprintferr(" 1st phase done\n"); if (DEBUGLEVEL>6) fprintferr("extramat = %Z\n",extramat); } permpro = imagecomplspec(extramat, &nlze); p1 = new_chunk(lig+1); for (i=1; i<=lig; i++) p1[i] = perm[permpro[i]]; for (i=1; i<=lig; i++) perm[i] = p1[i]; matb = cgetg(colnew+1,t_MAT); dep = cgetg(colnew+1,t_MAT); for (j=1; j<=colnew; j++) { GEN z = (GEN)extramat[j]; p1=cgetg(nlze+1,t_COL); dep[j]=(long)p1; p2=cgetg(lig+1-nlze,t_COL); matb[j]=(long)p2; p2 -= nlze; for (i=1; i<=nlze; i++) p1[i] = z[permpro[i]]; for ( ; i<= lig; i++) p2[i] = z[permpro[i]]; } p3 = cgetg(lB,t_MAT); for (j=1; j5) fprintferr(" 2nd phase done\n"); *ptdep = dep; *ptB = B; H = hnffinal(matb,perm,ptdep,ptB,&Cnew); p1 = cgetg(co+lextra,t_MAT); for (j=1; j <= col-lH; j++) p1[j] = C[j]; C = Cnew - (col-lH); for ( ; j < co+lextra; j++) p1[j] = C[j]; gptr[0]=ptC; *ptC=p1; gptr[1]=ptdep; gptr[2]=ptB; gptr[3]=&H; gerepilemany(av,gptr,4); if (DEBUGLEVEL) { if (DEBUGLEVEL>7) fprintferr("mit = %Z\nC = %Z\n",H,*ptC); msgtimer("hnfadd (%ld)",lextra); } return H; } static void ZV_neg(GEN x) { long i, lx = lg(x); for (i=1; ico)? 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) err(warnmem,"hnf[1]. i=%ld",i); tetpil=avma; A=gerepile(av,tetpil,ZM_copy(A)); } } p1 = gcoeff(A,i,def); s = signe(p1); if (s) { if (s < 0) ZV_neg((GEN)A[def]); ZM_reduce(A, NULL, i,def); def--; } else if (ldef) ldef--; if (low_stack(lim, stack_lim(av,1))) { if (DEBUGMEM>1) err(warnmem,"hnf[2]. i=%ld",i); tetpil=avma; A=gerepile(av,tetpil,ZM_copy(A)); } } if (remove) { /* remove null columns */ for (i=1,j=1; j co) err(talker,"nb lines > nb columns in hnfmod"); } else { /* concatenate dm * Id to x */ x = concatsp(x, idmat_intern(li-1,dm,gzero)); co += li-1; } /* Avoid wasteful divisions. we only want to prevent coeff explosion, so * only reduce mod dm when lg(coeff) > ldm */ ldm = lgefint(dm); def = co-1; ldef = 0; for (i=li-1; i>ldef; i--,def--) for (j=def-1; j; j--) { coeff(x,i,j) = lresii(gcoeff(x,i,j), dm); a = gcoeff(x,i,j); if (!signe(a)) continue; k = (j==1)? def: j-1; /* do not reduce the appended dm [hnfmodid] */ if (flag || j != 1) coeff(x,i,k) = lresii(gcoeff(x,i,k), dm); ZV_elem(a,gcoeff(x,i,k), x,NULL, j,k); p1 = (GEN)x[j]; p2 = (GEN)x[k]; for (k=1; k ldm) p1[k] = lresii((GEN)p1[k], dm); if (lgefint(p2[k]) > ldm) p2[k] = lresii((GEN)p2[k], dm); } if (low_stack(lim, stack_lim(av,1))) { if (DEBUGMEM>1) err(warnmem,"allhnfmod[1]. i=%ld",i); tetpil=avma; x=gerepile(av,tetpil,ZM_copy(x)); } } w = cgetg(li,t_MAT); b = dm; for (i=li-1; i>=1; i--) { d = bezout(gcoeff(x,i,i+def),b,&u,&v); w[i] = lmod(gmul(u,(GEN)x[i+def]), b); if (!signe(gcoeff(w,i,i))) coeff(w,i,i) = (long)d; if (flag && i > 1) b = diviiexact(b,d); } if (flag) { /* compute optimal value for dm */ dm = gcoeff(w,1,1); for (i=2; i=1; i--) { GEN diag = gcoeff(w,i,i); for (j=i+1; j ldm) p1[k] = lresii((GEN)p1[k], dm); if (low_stack(lim, stack_lim(av,1))) { if (DEBUGMEM>1) err(warnmem,"allhnfmod[2]. i=%ld", i); tetpil=avma; w=gerepile(av,tetpil,ZM_copy(w)); diag = gcoeff(w,i,i); } } } tetpil=avma; return gerepile(av,tetpil,ZM_copy(w)); } GEN hnfmod(GEN x, GEN detmat) { return allhnfmod(x,detmat,1); } GEN hnfmodid(GEN x, GEN p) { return allhnfmod(x,p,0); } /***********************************************************************/ /* */ /* HNFLLL (Havas, Majewski, Mathews) */ /* */ /***********************************************************************/ /* negate in place, except universal constants */ static GEN mynegi(GEN x) { static long mun[]={evaltyp(t_INT)|m_evallg(3),evalsigne(-1)|evallgefint(3),1}; long s = signe(x); if (!s) return gzero; if (is_pm1(x)) return (s>0)? mun: gun; setsigne(x,-s); return x; } /* We assume y > 0 */ static GEN divnearest(GEN x, GEN y) { GEN r, q = dvmdii(x,y,&r); long av = avma, s=signe(r), t; if (!s) { cgiv(r); return q; } if (s<0) r = mynegi(r); y = shifti(y,-1); t = cmpii(r,y); avma=av; cgiv(r); if (t < 0 || (t == 0 && s > 0)) return q; return addsi(s,q); } static void Minus(long j, GEN **lambda) { long k, n = lg(lambda); for (k=1 ; k 0) { for (j = lg(Mk)-1; j; j--) if (signe(Mi[j])) Mk[j] = laddii((GEN)Mk[j], (GEN)Mi[j]); } else { for (j = lg(Mk)-1; j; j--) if (signe(Mi[j])) Mk[j] = lsubii((GEN)Mk[j], (GEN)Mi[j]); } } else for (j = lg(Mk)-1; j; j--) if (signe(Mi[j])) Mk[j] = laddii((GEN)Mk[j], mulii(q, (GEN)Mi[j])); } /* index of first non-zero entry */ static long findi(GEN M) { long i, n = lg(M); for (i=1; i 0) q = divnearest(lambda[k][j], D[j]); else return; if (signe(q)) { GEN *Lk = lambda[k], *Lj = lambda[j]; q = mynegi(q); if (row0) elt_col((GEN)A[k],(GEN)A[j],q); if (B) elt_col((GEN)B[k],(GEN)B[j],q); Lk[j] = addii(Lk[j], mulii(q,D[j])); if (is_pm1(q)) { if (signe(q) > 0) { for (i=1; i nz, A[j] = 0 for all j < nz. * "Deep insert" A[k] at nz */ static void trivswap(GEN *A, long nz, long k, GEN **lambda, GEN *D) { GEN p1; long i,j,n = lg(A); p1 = A[nz]; /* cycle A */ for (j = nz; j < k; j++) SWAP(A[j+1], p1); A[nz] = p1; /* = [0...0] */ p1 = D[nz]; /* cycle D */ for (j = nz; j < k; j++) SWAP(D[j+1], p1); D[nz] = gun; for (j=k-1; j>=nz; j--) /* cycle lambda */ for (i=k-1; i>=nz; i--) lambda[i+1][j+1] = lambda[i][j]; for (j=n-1; j>k; j--) for (i=k-1; i>=nz; i--) { lambda[i+1][j] = lambda[i][j]; lambda[j][i+1] = lambda[j][i]; } 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) { ulong av = avma, lim = stack_lim(av,3); long m1 = 1, n1 = 1; /* alpha = m1/n1. Maybe 3/4 here ? */ long row[2], do_swap,i,n,k; long nzcol = 1; /* index of 1st (maybe) non-0 col [only updated when !B] */ GEN z,B, **lambda, *D; GEN *gptr[4]; if (typ(A) != t_MAT) err(typeer,"hnflll"); n = lg(A); A = ZM_copy(fix_rows(A)); B = ptB? idmat(n-1): NULL; D = (GEN*) cgetg(n+1, t_VEC); D++; /* hack: need a "sentinel" D[0] */ if (n == 2) /* handle trivial case: return negative diag coeff otherwise */ { i = findi((GEN)A[1]); if (i && signe(gcoeff(A,i,1)) < 0) { neg_col((GEN)A[1]); if (B) neg_col((GEN)B[1]); } } lambda = (GEN**) cgetg(n,t_MAT); for (i=1; i nzcol) {trivswap((GEN*)A,nzcol,k-1, lambda,D); nzcol++;} if (!row[1] && k > nzcol) {trivswap((GEN*)A,nzcol,k , lambda,D); nzcol++;} if (k <= nzcol) k = nzcol+1; continue; } do_swap = (row[0] && row[0] <= row[1]); } else { if (row[0]) do_swap = (!row[1] || row[0] <= row[1]); else if (row[1]) do_swap = 0; else { /* row[0] == row[1] == 0 */ ulong av1 = avma; z = addii(mulii(D[k-2],D[k]), sqri(lambda[k][k-1])); do_swap = (cmpii(mulsi(n1,z), mulsi(m1,sqri(D[k-1]))) < 0); avma = av1; } } if (do_swap) { hnfswap(A,B,k,lambda,D); if (k > nzcol+1) k--; } else { for (i=k-2; i>=nzcol; i--) reduce2(A,B,k,i,row,lambda,D); k++; } if (low_stack(lim, stack_lim(av,3))) { GEN a = (GEN)lambda, b = (GEN)(D-1); /* gcc -Wall */ gptr[0]=&A; gptr[1]=&a; gptr[2]=&b; gptr[3]=&B; if (DEBUGMEM) err(warnmem,"hnflll, k = %ld / %ld",k,n); gerepilemany(av,gptr,B? 4: 3); lambda = (GEN**)a; D = (GEN*)(b+1); } } for (i=nzcol; i 0) q = divnearest(lambda[k][j], D[j]); else return; if (! gcmp0(q)) { q = mynegi(q); A[k] = laddii((GEN)A[k], mulii(q,(GEN)A[j])); elt_col((GEN)B[k],(GEN)B[j],q); lambda[k][j] = addii(lambda[k][j], mulii(q,D[j])); for (i=1; i2) k--; } else { for (i=k-2; i; i--) reduce1(A,B,k,i,lambda,D); k++; } } if (signe(A[n-1])<0) { A[n-1] = (long)mynegi((GEN)A[n-1]); neg_col((GEN)B[n-1]); } tetpil = avma; p1 = cgetg(3,t_VEC); p1[1]=lcopy((GEN)A[n-1]); p1[2]=lcopy(B); return gerepile(av,tetpil,p1); } /* HNF with permutation. TODO: obsolete, replace with hnflll. */ GEN hnfperm(GEN A) { GEN U,c,l,perm,d,u,p,q,y,b; long r,t,i,j,j1,k,m,n,av=avma,av1,tetpil,lim; if (typ(A) != t_MAT) err(typeer,"hnfperm"); n = lg(A)-1; if (!n) { y = cgetg(4,t_VEC); y[1] = lgetg(1,t_MAT); y[2] = lgetg(1,t_MAT); y[3] = lgetg(1,t_VEC); return y; } m = lg(A[1])-1; c = cgetg(m+1,t_VECSMALL); for (i=1; i<=m; i++) c[i]=0; l = cgetg(n+1,t_VECSMALL); for (j=1; j<=n; j++) l[j]=0; perm = cgetg(m+1, t_VECSMALL); av1 = avma; lim = stack_lim(av1,1); U = idmat(n); A = dummycopy(A); /* 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((GEN)A[k]); ZV_neg((GEN)U[k]); p = gcoeff(A,t,k); } for (j=1; j1) err(warnmem,"hnfperm"); gerepilemany(av1,gptr,2); } } if (r < m) { for (i=1,k=r; i<=m; i++) if (c[i]==0) 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) */ tetpil=avma; y=cgetg(4,t_VEC); p=cgetg(r+1,t_MAT); u=cgetg(n+1,t_MAT); for (t=1,k=r,j=1; j<=n; j++) if (l[j]) { q=cgetg(m+1,t_COL); p[k]=(long)q; for (i=1; i<=m; i++) q[i]=lcopy(gcoeff(A,perm[m-i+1],j)); u[k+n-r]=lcopy((GEN)U[j]); k--; } else u[t++]=lcopy((GEN)U[j]); y[1]=(long)p; y[2]=(long)u; q = cgetg(m+1,t_VEC); y[3]=(long)q; for (i=1; i<=m; i++) q[m-i+1]=lstoi(perm[i]); return gerepile(av,tetpil,y); } /*==================================================================== * Forme Normale d'Hermite (Version par colonnes 31/01/94) *====================================================================*/ GEN hnfall_i(GEN A, GEN *ptB, long remove) { GEN B,c,h,x,p,a; long m,n,r,i,j,k,li,av=avma,av1,tetpil,lim; GEN *gptr[2]; if (typ(A)!=t_MAT) 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 = dummycopy(A); B = ptB? idmat(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))) { tetpil = avma; A = ZM_copy(A); gptr[0]=&A; if (B) { B = ZM_copy(B); gptr[1]=&B; } if (DEBUGMEM>1) err(warnmem,"hnfall[1], li = %ld", li); gerepilemanysp(av1,tetpil,gptr,B? 2: 1); } } x = gcoeff(A,li,j); if (signe(x)) break; h[j] = li-1; } if (j == r) continue; r--; if (j < r) /* A[j] != 0 */ { swap(A[j], A[r]); if (B) swap(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((GEN)A[r]); if (B) ZV_neg((GEN)B[r]); p = gcoeff(A,li,r); } ZM_reduce(A,B, li,r); if (low_stack(lim, stack_lim(av1,1))) { GEN *gptr[2]; gptr[0]=&A; gptr[1]=&B; if (DEBUGMEM>1) err(warnmem,"hnfall[2], li = %ld", li); gerepilemany(av1,gptr,B? 2: 1); } } 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))) { tetpil = avma; A = ZM_copy(A); gptr[0]=&A; if (B) { B = ZM_copy(B); gptr[1]=&B; } if (DEBUGMEM>1) err(warnmem,"hnfall[3], j = %ld", j); gerepilemanysp(av1,tetpil,gptr, B? 2: 1); } } if (DEBUGLEVEL>5) fprintferr("\n"); /* remove the first r columns */ if (remove) { A += r; A[0] = evaltyp(t_MAT) | evallg(n-r+1); } gptr[0] = &A; gptr[1] = &B; gerepilemany(av, gptr, B? 2: 1); if (B) *ptB = B; return A; } GEN hnfall0(GEN A, long remove) { GEN B, z = cgetg(3, t_VEC); z[1] = (long)hnfall_i(A, &B, remove); z[2] = (long)B; return z; } GEN hnfall(GEN x) {return hnfall0(x,1);} /***************************************************************/ /** **/ /** SMITH NORMAL FORM REDUCTION **/ /** **/ /***************************************************************/ static GEN col_mul(GEN x, GEN c) { 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; } static void do_zero(GEN x) { long i, lx = lg(x); for (i=1; i=9) outerr(x); n=lg(x)-1; if (!n) return trivsmith(all); if (lg(x[1]) != n+1) err(mattype1,"smithall"); for (i=1; i<=n; i++) for (j=1; j<=n; j++) if (typ(coeff(x,i,j)) != t_INT) err(talker,"non integral matrix in smithall"); av = avma; lim = stack_lim(av,1); x = dummycopy(x); mdet = detint(x); if (ishnfall(x)) { if (all) { ml=idmat(n); mr=idmat(n); } } else { if (signe(mdet)) { p1=hnfmod(x,mdet); if (all) { ml=idmat(n); mr=gauss(x,p1); } } else { if (all) { p1 = hnfall0(x,0); ml = idmat(n); mr = (GEN)p1[2]; p1 = (GEN)p1[1]; } else p1 = hnf0(x,0); } x = p1; } p1=cgetg(n+1,t_VEC); for (i=1; i<=n; i++) p1[i]=lnegi(gcoeff(x,i,i)); p2=sindexsort(p1); ys=cgetg(n+1,t_MAT); for (j=1; j<=n; j++) { p1=cgetg(n+1,t_COL); ys[j]=(long)p1; for (i=1; i<=n; i++) p1[i]=coeff(x,p2[i],p2[j]); } x = ys; if (all) { p3=cgetg(n+1,t_MAT); p4=cgetg(n+1,t_MAT); for (j=1; j<=n; j++) { p3[j]=ml[p2[j]]; p4[j]=mr[p2[j]]; } ml=p3; mr=p4; } if (signe(mdet)) { p1 = hnfmod(x,mdet); if (all) mr=gmul(mr,gauss(x,p1)); } else { if (all) { p1 = hnfall0(x,0); mr = gmul(mr,(GEN)p1[2]); p1 = (GEN)p1[1]; } else p1 = hnf0(x,0); } x=p1; mun = negi(gun); if (DEBUGLEVEL>7) {fprintferr("starting SNF loop");flusherr();} for (i=n; i>=2; i--) { if (DEBUGLEVEL>7) {fprintferr("\ni = %ld: ",i);flusherr();} for(;;) { c = 0; for (j=i-1; j>=1; j--) { p1=gcoeff(x,i,j); s1 = signe(p1); if (s1) { p2=gcoeff(x,i,i); if (!absi_cmp(p1,p2)) { s2=signe(p2); if (s1 == s2) { d=p1; u=gun; p4=gun; } else { if (s2>0) { u = gun; p4 = mun; } else { u = mun; p4 = gun; } d=(s1>0)? p1: absi(p1); } v = gzero; p3 = u; } else { d=bezout(p2,p1,&u,&v); p3=divii(p2,d); p4=divii(p1,d); } for (k=1; k<=i; k++) { b=addii(mulii(u,gcoeff(x,k,i)),mulii(v,gcoeff(x,k,j))); coeff(x,k,j)=lsubii(mulii(p3,gcoeff(x,k,j)), mulii(p4,gcoeff(x,k,i))); coeff(x,k,i)=(long)b; } if (all) update(u,v,p3,p4,(GEN*)(mr+i),(GEN*)(mr+j)); if (low_stack(lim, stack_lim(av,1))) { if (DEBUGMEM>1) err(warnmem,"[1]: smithall"); if (all) { GEN *gptr[3]; gptr[0]=&x; gptr[1]=&ml; gptr[2]=&mr; gerepilemany(av,gptr,3); } else x=gerepileupto(av, ZM_copy(x)); } } } if (DEBUGLEVEL>=8) {fprintferr("; ");flusherr();} for (j=i-1; j>=1; j--) { p1=gcoeff(x,j,i); s1 = signe(p1); if (s1) { p2=gcoeff(x,i,i); if (!absi_cmp(p1,p2)) { s2 = signe(p2); if (s1 == s2) { d=p1; u=gun; p4=gun; } else { if (s2>0) { u = gun; p4 = mun; } else { u = mun; p4 = gun; } d=(s1>0)? p1: absi(p1); } v = gzero; p3 = u; } else { d=bezout(p2,p1,&u,&v); p3=divii(p2,d); p4=divii(p1,d); } for (k=1; k<=i; k++) { b=addii(mulii(u,gcoeff(x,i,k)),mulii(v,gcoeff(x,j,k))); coeff(x,j,k)=lsubii(mulii(p3,gcoeff(x,j,k)), mulii(p4,gcoeff(x,i,k))); coeff(x,i,k)=(long)b; } if (all) update(u,v,p3,p4,(GEN*)(ml+i),(GEN*)(ml+j)); c = 1; } } if (!c) { b=gcoeff(x,i,i); fl=1; if (signe(b)) { for (k=1; k1) err(warnmem,"[2]: smithall"); if (all) { GEN *gptr[3]; gptr[0]=&x; gptr[1]=&ml; gptr[2]=&mr; gerepilemany(av,gptr,3); } else x=gerepileupto(av,ZM_copy(x)); } } } if (DEBUGLEVEL>7) {fprintferr("\n");flusherr();} if (all) { for (k=1; k<=n; k++) if (signe(gcoeff(x,k,k))<0) { mr[k]=lneg((GEN)mr[k]); coeff(x,k,k)=lnegi(gcoeff(x,k,k)); } tetpil=avma; z=cgetg(4,t_VEC); z[1]=ltrans(ml); z[2]=lcopy(mr); z[3]=lcopy(x); return gerepile(av,tetpil,z); } tetpil=avma; z=cgetg(n+1,t_VEC); j=n; for (k=n; k; k--) if (signe(gcoeff(x,k,k))) z[j--]=labsi(gcoeff(x,k,k)); for ( ; j; j--) z[j]=zero; return gerepile(av,tetpil,z); } GEN smith(GEN x) { return smithall(x,0); } GEN smith2(GEN x) { return smithall(x,1); } /* 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) err(typeer,"smithclean"); l = lg(z); if (l == 1) return cgetg(1,t_VEC); u=(GEN)z[1]; if (l != 4 || typ(u) != t_MAT) { if (typ(u) != t_INT) err(typeer,"smithclean"); for (c=1; c=2; i--) { do { c=0; for (j=i-1; j>=1; j--) { p1=gcoeff(x,i,j); if (signe(p1)) { p2=gcoeff(x,i,i); if (!signe(p2)) { p3 = gzero; p4 = gun; u = gzero; v = gun; } else { v = gdiventres(p1,p2); if (gcmp0((GEN)v[2])) { d=p2; p4=(GEN)v[1]; v=gzero; p3=gun; u=gun; } else { d=gbezout(p2,p1,&u,&v); p3=gdiv(p2,d); p4=gdiv(p1,d); } } for (k=1; k<=i; k++) { b=gadd(gmul(u,gcoeff(x,k,i)),gmul(v,gcoeff(x,k,j))); coeff(x,k,j)=lsub(gmul(p3,gcoeff(x,k,j)),gmul(p4,gcoeff(x,k,i))); coeff(x,k,i)=(long)b; } if (all) update(u,v,p3,p4,(GEN*)(mr+i),(GEN*)(mr+j)); } } for (j=i-1; j>=1; j--) { p1=gcoeff(x,j,i); if (signe(p1)) { p2 = gcoeff(x,i,i); if (!signe(p2)) { p3 = gzero; p4 = gun; u = gzero; v = gun; } else { v = gdiventres(p1,p2); if (gcmp0((GEN)v[2])) { d=p2; p4=(GEN)v[1]; v=gzero; p3=gun; u=gun; } else { d=gbezout(p2,p1,&u,&v); p3=gdiv(p2,d); p4=gdiv(p1,d); } } for (k=1; k<=i; k++) { b=gadd(gmul(u,gcoeff(x,i,k)),gmul(v,gcoeff(x,j,k))); coeff(x,j,k)=lsub(gmul(p3,gcoeff(x,j,k)),gmul(p4,gcoeff(x,i,k))); coeff(x,i,k)=(long)b; } if (all) update(u,v,p3,p4,(GEN*)(ml+i),(GEN*)(ml+j)); c = 1; } } if (!c) { b = gcoeff(x,i,i); if (signe(b)) for (k=1; k1) err(warnmem,"[5]: smithall"); if (all) { GEN *gptr[3]; gptr[0]=&x; gptr[1]=&ml; gptr[2]=&mr; gerepilemany(av,gptr,3); } else x=gerepilecopy(av,x); } } while (c); } if (all) { for (k=1; k<=n; k++) if (signe(gcoeff(x,k,k)) < 0) { mr[k]=lneg((GEN)mr[k]); coeff(x,k,k)=lneg(gcoeff(x,k,k)); } tetpil=avma; z=cgetg(4,t_VEC); z[1]=ltrans(ml); z[2]=lcopy(mr); z[3]=lcopy(x); } else { tetpil=avma; z=cgetg(n+1,t_VEC); for (j=0,k=1; k<=n; k++) if (!signe(gcoeff(x,k,k))) z[++j]=zero; for (k=1; k<=n; k++) if (signe(p1=gcoeff(x,k,k))) z[++j]=(long)gabs(p1,0); } return gerepile(av,tetpil,z); } GEN matsnf0(GEN x,long flag) { long av = avma; if (flag > 7) err(flagerr,"matsnf"); if (typ(x) == t_VEC && flag & 4) return smithclean(x); if (flag & 2) x = gsmithall(x,flag & 1); else x = smithall(x, flag & 1); if (flag & 4) x = smithclean(x); return gerepileupto(av, x); } GEN gsmith(GEN x) { return gsmithall(x,0); } GEN gsmith2(GEN x) { return gsmithall(x,1); }