/* $Id: subcyclo.c,v 1.21 2002/07/04 12:25:04 bill 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. */ #include "pari.h" extern GEN vandermondeinversemod(GEN L, GEN T, GEN den, GEN mod); extern GEN supnorm(GEN L, long prec); /*************************************************************************/ /** **/ /** Routines for handling subgroups of (Z/nZ)^* **/ /** without requiring discrete logarithms. **/ /** **/ /*************************************************************************/ /* Subgroups are [gen,ord,bits] where * gen is a vecsmall of generators * ord is theirs relative orders * bits is a bit vector of the elements, of length(n). */ /*The algorithm is similar to testpermutation*/ void znstar_partial_coset_func(long n, GEN H, void (*func)(void *data,long c) , void *data, long d, long c) { GEN gen = (GEN) H[1]; GEN ord = (GEN) H[2]; GEN cache = vecsmall_const(d,c); long i, j, card = 1; (*func)(data,c); for (i = 1; i <= d; i++) card *= ord[i]; for(i=1; i0;i--) { long p=coeff(F,i,1); long e=coeff(F,i,2); long q=n; if (DEBUGLEVEL>=4) fprintferr("SubCyclo: testing %ld^%ld\n",p,e); for ( ; e>=1; e--) { long z = 1; q /= p; for (j = 1; j < p; j++) { z += q; if (!bitvec_test((GEN) H[3],z) && cgcd(z,n)==1) break; } if ( j < p ) { if (DEBUGLEVEL>=4) fprintferr("SubCyclo: %ld not found\n",z); break; } cnd /= p; if (DEBUGLEVEL>=4) fprintferr("SubCyclo: new conductor:%ld\n",cnd); } } if (DEBUGLEVEL>=6) fprintferr("SubCyclo: conductor:%ld\n",cnd); avma=ltop; return cnd; } /* Calcule les orbites d'un sous-groupe de Z/nZ donne par un * generateur ou d'un ensemble de generateur donne par un vecteur. */ GEN znstar_cosets(long n, long phi_n, GEN H) { long k; long c = 0; long card = group_order(H); long index = phi_n/card; GEN cosets = cgetg(index+1,t_VECSMALL); gpmem_t ltop = avma; GEN bits = bitvec_alloc(n); for (k = 1; k <= index; k++) { for (c++ ; bitvec_test(bits,c) || cgcd(c,n)!=1; c++); cosets[k]=c; znstar_coset_bits_inplace(n, H, bits, c); } avma=ltop; return cosets; } /*************************************************************************/ /** **/ /** znstar/HNF interface **/ /** **/ /*************************************************************************/ /* Convert a true znstar output by znstar to a `small znstar' */ GEN znstar_small(GEN zn) { GEN Z=cgetg(4,t_VEC); Z[1]=licopy(gmael3(zn,3,1,1)); Z[2]=(long) gtovecsmall((GEN)zn[2]); Z[3]=(long) lift((GEN)zn[3]); return Z; } /* Compute generators for the subgroup of (Z/nZ)* given in HNF. */ GEN znstar_hnf_generators(GEN Z, GEN M) { long l = lg(M); GEN gen=cgetg(l, t_VECSMALL); gpmem_t ltop=avma; GEN zgen= (GEN) Z[3]; long n = itos((GEN) Z[1]); GEN m = stoi(n); long j,h; for (j = 1; j < l; j++) { gen[j] = 1; for (h = 1; h < l; h++) gen[j] = mulssmod(gen[j], itos(powmodulo((GEN) zgen[h], gmael(M,j,h),m)),n); } avma=ltop; return gen; } GEN znstar_hnf(GEN Z, GEN M) { return znstar_generate(itos((GEN)Z[1]),znstar_hnf_generators(Z,M)); } GEN znstar_hnf_elts(GEN Z, GEN H) { gpmem_t ltop=avma; GEN G=znstar_hnf(Z,H); long n=itos((GEN)Z[1]); GEN list=znstar_elts(n,G); return gerepileupto(ltop,list); } /*************************************************************************/ /** **/ /** subcyclo **/ /** **/ /*************************************************************************/ static GEN gscycloconductor(GEN g, long n, long flag) { if (flag==2) { GEN V=cgetg(3,t_VEC); V[1]=lcopy(g); V[2]=lstoi(n); return V; } return g; } static long lift_check_modulus(GEN H, long n) { long t=typ(H); long h; switch(t) { case t_INTMOD: if (cmpsi(n,(GEN)H[1])) err(talker,"wrong modulus in galoissubcyclo"); H = (GEN)H[2]; case t_INT: h=smodis(H,n); if (cgcd(h,n)!=1) err(talker,"generators must be prime to conductor in galoissubcyclo"); return h; } err(talker,"wrong type in galoissubcyclo"); return 0;/*not reached*/ } GEN subcyclo_complex_bound(gpmem_t ltop, GEN V, long prec) { GEN pol = roots_to_pol(V,0); GEN vec = gtovec(greal(pol)); GEN borne = ceil_safe(supnorm(vec,prec)); return gerepileupto(ltop,borne); } GEN subcyclo_complex_cyclic(long n, long d, long m ,long z, long g, GEN powz, long prec) { GEN V=cgetg(d+1,t_VEC); long base=1; long i,k; for (i=1;i<=d;i++,base=mulssmod(base,z,n)) { gpmem_t ltop=avma; long ex=base; GEN s=gzero; (void)new_chunk(2*prec + 3); for (k=0; kpowz; GEN *s = data->s; if (!data->count) data->ltop= avma; *s = gadd(*s,(GEN)powz[k]); data->count++; if ((data->count & 0xffUL) == 0) *s = gerepileupto(data->ltop, *s); } /* Newton sums mod le. if le==NULL, works with complex instead */ GEN subcyclo_orbits(long n, GEN H, GEN O, GEN powz, GEN le) { long i, d=lg(O); GEN V=cgetg(d,t_VEC); struct _subcyclo_orbits_s data; long lle=le?lg(le)*2+1:2*lg(powz[1])+3;/*Assume dvmdii use lx+ly space*/ data.powz = powz; for(i=1; i= 1) (void)timer2(); l=stoi(n+1);e=1; while(!isprime(l)) { l=addis(l,n); e++; } if (DEBUGLEVEL >= 4) fprintferr("Subcyclo: prime l=%Z\n",l); av=avma; if (!borne) { /*Borne utilise': Vecmax(Vec((x+o)^d)=max{binome(d,i)*o^i ;1<=i<=d} */ i=d-(1+d)/(1+o); borne=mulii(binome(stoi(d),i),gpowgs(stoi(o),i)); } if (DEBUGLEVEL >= 4) fprintferr("Subcyclo: borne=%Z\n",borne); val=logint(shifti(borne,2),l,NULL); avma=av; if (DEBUGLEVEL >= 4) fprintferr("Subcyclo: val=%ld\n",val); le=gpowgs(l,val); z=lift(gpowgs(gener(l),e)); z=padicsqrtnlift(gun,stoi(n),z,l,val); if (DEBUGLEVEL >= 1) msgtimer("padicsqrtnlift."); *ptr_val=val; *ptr_l=itos(l); return gmodulcp(z,le); } GEN subcyclo_complex_roots(long n, long real, long prec) { GEN powz, z = exp_Ir(divrs(Pi2n(1, prec), n)); /* = e_n(1) */ long i, k = (n+3)>>1; powz = cgetg(n,t_VEC); powz[1] = (long)z; for (i=2; i2) err(flagerr,"galoissubcyclo"); if ( v==-1 ) v=0; if (!sg) sg=gun; switch(typ(N)) { case t_INT: n=itos(N); if ( n<1 ) err(arither2); break; case t_VEC: if (lg(N)==7) N=bnrtozn(N,&complex); if (lg(N)==4) { Z=N; if (typ(Z[3])!=t_VEC) err(typeer,"galoissubcyclo"); if (lg(Z[3])==1) n=1; else { if (typ(gmael(Z,3,1))!= t_INTMOD) #ifdef NETHACK_MESSAGES err(talker,"You have transgressed!"); #else err(talker,"Please do not try to break PARI with ridiculously counterfeit data. Thanks!"); #endif n=itos(gmael3(Z,3,1,1)); } break; } default: /*fall through*/ err(typeer,"galoissubcyclo"); return NULL;/*Not reached*/ } if (n==1) {avma=ltop; return polx[v];} switch(typ(sg)) { case t_INTMOD: case t_INT: V=cgetg(2,t_VECSMALL); V[1]=lift_check_modulus(sg,n); break; case t_VECSMALL: V=gcopy(sg); for (i=1;i conj(z)=z^-1=z^(n-1) is in H */ if (DEBUGLEVEL >= 6) { fprintferr("Subcyclo: elements:"); for (i=1;i= 6) fprintferr("Subcyclo: complex=%ld\n",complex); if (DEBUGLEVEL >= 1) (void)timer2(); cnd = znstar_conductor(n,H); if (DEBUGLEVEL >= 1) msgtimer("znstar_conductor"); if ( flag == 1 ) { avma=ltop; return stoi(cnd); } if (n != cnd) { H = znstar_reduce_modulus(H, cnd); n = cnd; } card = group_order(H); phi_n= itos(phi(stoi(n))); if ( card==phi_n ) { avma=ltop; if (flag==3) return galoiscyclo(n,v); return gscycloconductor(cyclo(n,v),n,flag); } O = znstar_cosets(n, phi_n, H); if (DEBUGLEVEL >= 1) msgtimer("znstar_cosets"); if (DEBUGLEVEL >= 6) fprintferr("Subcyclo: orbits=%Z\n",O); if (DEBUGLEVEL >= 4) fprintferr("Subcyclo: %ld orbits with %ld elements each\n",phi_n/card,card); av=avma; powz=subcyclo_complex_roots(n,!complex,3); L=subcyclo_orbits(n,H,O,powz,NULL); B=subcyclo_complex_bound(av,L,3); zl=subcyclo_start(n,phi_n/card,card,B,&val,&l); powz=subcyclo_roots(n,zl); le=(GEN) zl[1]; L=subcyclo_orbits(n,H,O,powz,le); T=FpV_roots_to_pol(L,le,v); T=FpX_center(T,le); return gerepileupto(ltop,gscycloconductor(T,n,flag)); } GEN subcyclo(long n, long d, long v) { gpmem_t ltop=avma; long o,p,al,r,g,gd; GEN fa,G; GEN zl,L,T,le; long l,val; GEN B,powz; if (v<0) v = 0; if (d==1) return polx[v]; if (d<=0 || n<=0) err(typeer,"subcyclo"); if ((n & 3) == 2) n >>= 1; if (n == 1 || d >= n) err(talker,"degree does not divide phi(n) in subcyclo"); fa = decomp(stoi(n)); p = itos(gmael(fa,1,1)); al= itos(gmael(fa,2,1)); if (lg((GEN)fa[1]) > 2 || (p==2 && al>2)) err(talker,"non-cyclic case in polsubcyclo: use galoissubcyclo instead"); avma=ltop; r = cgcd(d,n); /* = p^(v_p(d))*/ n = r*p; o = n-r; /* = phi(n) */ if (o == d) return cyclo(n,v); if (o % d) err(talker,"degree does not divide phi(n) in subcyclo"); o /= d; if (p==2) { GEN pol = powgi(polx[v],gdeux); pol[2]=un; /* replace gzero */ return pol; /* = x^2 + 1 */ } G=gener(stoi(n)); g=itos((GEN)G[2]); gd=itos((GEN)gpowgs(G,d)[2]); avma=ltop; powz=subcyclo_complex_roots(n,(o&1)==0,3); L=subcyclo_cyclic(n,d,o,g,gd,powz,NULL); B=subcyclo_complex_bound(ltop,L,3); zl=subcyclo_start(n,d,o,B,&val,&l); le=(GEN)zl[1]; powz=subcyclo_roots(n,zl); if (DEBUGLEVEL >= 6) msgtimer("subcyclo_roots"); L=subcyclo_cyclic(n,d,o,g,gd,powz,le); if (DEBUGLEVEL >= 6) msgtimer("subcyclo_cyclic"); T=FpV_roots_to_pol(L,le,v); if (DEBUGLEVEL >= 6) msgtimer("roots_to_pol"); T=FpX_center(T,le); return gerepileupto(ltop,T); } GEN polsubcyclo(long n, long d, long v) { gpmem_t ltop=avma; GEN L, Z=znstar(stoi(n)); /*subcyclo is twice faster but Z must be cyclic*/ if (lg(Z[2]) == 2 && divise((GEN)Z[1],stoi(d))) { avma=ltop; return subcyclo(n, d, v); } L=subgrouplist((GEN) Z[2], _vec(stoi(d))); if (lg(L) == 2) return gerepileupto(ltop, galoissubcyclo(Z, (GEN) L[1], 0, v)); else { GEN V=cgetg(lg(L),t_VEC); long i; for (i=1; i< lg(V); i++) V[i] = (long) galoissubcyclo(Z, (GEN) L[i], 0, v); return gerepileupto(ltop,V); } }