/* $Id: buch1.c,v 1.149 2006/04/11 17:28:55 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. */ #include "pari.h" #include "paripriv.h" /*******************************************************************/ /* */ /* Hilbert and Ray Class field using CM (Schertz) */ /* */ /*******************************************************************/ static int isoforder2(GEN form) { GEN a = gel(form,1), b = gel(form,2), c = gel(form,3); return !signe(b) || absi_equal(a,b) || equalii(a,c); } GEN getallforms(GEN D, long *pth, GEN *ptz) { ulong d = itou(D), dover3 = d/3, t, b2, a, b, c, h; GEN z, L = cgetg((long)(sqrt(d) * log2(d)), t_VEC); b2 = b = (d&1); h = 0; z = gen_1; if (!b) /* b = 0 treated separately to avoid special cases */ { t = d >> 2; /* (b^2 - D) / 4*/ for (a=1; a*a<=t; a++) if (c = t/a, t == c*a) { z = mului(a,z); gel(L,++h) = mkvecsmall3(a,b,c); } b = 2; b2 = 4; } /* now b > 0 */ for ( ; b2 <= dover3; b += 2, b2 = b*b) { t = (b2 + d) >> 2; /* (b^2 - D) / 4*/ /* b = a */ if (c = t/b, t == c*b) { z = mului(b,z); gel(L,++h) = mkvecsmall3(b,b,c); } /* b < a < c */ for (a = b+1; a*a < t; a++) if (c = t/a, t == c*a) { z = mului(a,z); gel(L,++h) = mkvecsmall3(a, b,c); gel(L,++h) = mkvecsmall3(a,-b,c); } /* a = c */ if (a * a == t) { z = mului(a,z); gel(L,++h) = mkvecsmall3(a,b,c); } } *pth = h; *ptz = z; setlg(L,h+1); return L; } static ulong check_pq(GEN gp, GEN z, long d, GEN D) { ulong p = itou(gp); if (!umodiu(z,p) || kross(d,(long)p) <= 0 || gcmp1((GEN)redimag(primeform_u(D,p))[1])); pari_err(talker,"[quadhilbert] incorrect values in pq: %lu", p); return p; } #define MOD4(x) ((x)&3) /* find P and Q two non principal prime ideals (above p,q) such that * (pq, 2z) = 1 [coprime to all class group representatives] * cl(P) = cl(Q) if P has order 2 in Cl(K) * Try to have e = 24 / gcd(24, (p-1)(q-1)) as small as possible */ static void get_pq(GEN D, GEN z, GEN pq, ulong *ptp, ulong *ptq) { const long MAXL = 50; GEN form, wp = cgetg(MAXL,t_VECSMALL), wlf = cgetg(MAXL,t_VEC); long i, ell, p, l = 1, d = itos(D); byteptr diffell = diffptr + 2; if (pq && typ(pq)==t_VEC) { if (lg(pq) != 3) pari_err(typeer, "quadhilbert (pq)"); *ptp = check_pq(gel(pq,1),z,d,D); *ptq = check_pq(gel(pq,2),z,d,D); return; } ell = 3; while (l < MAXL) { NEXT_PRIME_VIADIFF_CHECK(ell, diffell); if (umodiu(z,ell) && kross(d,ell) > 0) { form = redimag(primeform_u(D,ell)); if (gcmp1(gel(form,1))) continue; gel(wlf,l) = form; wp[l] = ell; l++; } } setlg(wp,l); setlg(wlf,l); for (i=1; i1) (void)timer2(); if (cmpiu(D,11) <= 0) return pol_x[0]; L = getallforms(D,&h,&z); if (DEBUGLEVEL>1) msgtimer("class number = %ld",h); if (h == 1) { avma=av; return pol_x[0]; } get_pq(D, z, pq, &p, &q); e = 24 / cgcd((p%24 - 1) * (q%24 - 1), 24); if(DEBUGLEVEL>1) fprintferr("p = %lu, q = %lu, e = %ld\n",p,q,e); qfp = primeform_u(D, p); if (p == q) { u = (GEN)compimagraw(qfp, qfp)[2]; u = gmodulo(u, shifti(sqru(p),1)); } else { GEN qfq = primeform_u(D, q); GEN up = mkintmodu(itou(gel(qfp,2)), p << 1); GEN uq = mkintmodu(itou(gel(qfq,2)), q << 1); u = chinese(up,uq); } /* u modulo 2pq */ prec = 3; for(;;) { long ex, exmax = 0; pari_sp av0 = avma; GEN lead, sqd = sqrtr_abs(itor(D, prec)); P = cgetg(h+1,t_VEC); for (i=1; i<=h; i++) { GEN s = gpq(gel(L,i), p, q, e, sqd, u, prec); if (DEBUGLEVEL>3) fprintferr("%ld ", i); gel(P,i) = s; ex = gexpo(s); if (ex > 0) exmax += ex; } if (DEBUGLEVEL>1) msgtimer("roots"); /* to avoid integer overflow (1 + 0.) */ lead = (exmax < bit_accuracy(prec))? gen_1: real_1(prec); P = real_i( roots_to_pol_intern(lead,P,0,0) ); P = grndtoi(P,&exmax); if (DEBUGLEVEL>1) msgtimer("product, error bits = %ld",exmax); if (exmax <= -10) { if (pq && degpol(srgcd(P, derivpol(P)))) { avma = av; return gen_0; } break; } avma = av0; prec += (DEFAULTPREC-2) + (1 + (exmax >> TWOPOTBITS_IN_LONG)); if (DEBUGLEVEL) pari_warn(warnprec,"quadhilbertimag",prec); } return gerepileupto(av,P); } GEN quadhilbert(GEN D, GEN flag, long prec) { if (typ(D) != t_INT) { D = checkbnf(D); if (degpol(gmael(D,7,1)) != 2) pari_err(talker,"not a polynomial of degree 2 in quadhilbert"); D = gmael(D,7,3); } else if (!isfundamental(D)) pari_err(talker,"quadhilbert needs a fundamental discriminant"); return (signe(D)>0)? quadhilbertreal(D,prec) : quadhilbertimag(D,flag); } #define to_approx(nf,a) ((GEN)gmul(gmael((nf),5,1), (a))[1]) /* Z-basis for a (over C) */ static GEN get_om(GEN nf, GEN a) { return mkvec2(to_approx(nf,gel(a,2)), to_approx(nf,gel(a,1))); } /* Compute all elts in class group G = [|G|,c,g], c=cyclic factors, g=gens. * Set list[j + 1] = g1^e1...gk^ek where j is the integer * ek + ck [ e(k-1) + c(k-1) [... + c2 [e1]]...] */ static GEN getallelts(GEN bnr) { GEN nf,G,C,c,g, *list, **pows, *gk; long lc,i,j,k,no; nf = checknf(bnr); G = gel(bnr,5); no = itos(gel(G,1)); c = gel(G,2); g = gel(G,3); lc = lg(c)-1; list = (GEN*) cgetg(no+1,t_VEC); if (!lc) { list[1] = idealhermite(nf,gen_1); return (GEN)list; } pows = (GEN**)cgetg(lc+1,t_VEC); c = shallowcopy(c); settyp(c, t_VECSMALL); for (i=1; i<=lc; i++) { c[i] = k = itos(gel(c,i)); gk = (GEN*)cgetg(k, t_VEC); gk[1] = gel(g,i); for (j=2; j j only involves g(k-i)...gk */ i = 1; list[1] = 0; /* dummy */ for (j=1; j < C[1]; j++) list[j + 1] = pows[lc][j]; for ( ; j -20 || eb > -20) return NULL; if (!signe(b)) return a; return coltoalg(nf, mkcol2(a,b)); } static GEN findbezk_pol(GEN nf, GEN x) { long i, lx = lg(x); GEN y = cgetg(lx,t_POL); for (i=2; i> TWOPOTBITS_IN_LONG); if (k <= prec) k = (prec<<1)-2; /* dubious: old prec should have worked */ return k; } /* Compute data for ellphist */ static GEN ellphistinit(GEN om, long prec) { GEN res,om1b,om2b, om1 = gel(om,1), om2 = gel(om,2); if (gsigne(imag_i(gdiv(om1,om2))) < 0) { swap(om1,om2); om = mkvec2(om1,om2); } om1b = gconj(om1); om2b = gconj(om2); res = cgetg(4,t_VEC); gel(res,1) = gdivgs(elleisnum(om,2,0,prec),12); gel(res,2) = gdiv(PiI2(prec), gmul(om2, imag_i(gmul(om1b,om2)))); gel(res,3) = om2b; return res; } /* Computes log(phi^*(z,om)), using res computed by ellphistinit */ static GEN ellphist(GEN om, GEN res, GEN z, long prec) { GEN u = imag_i(gmul(z, gel(res,3))); GEN zst = gsub(gmul(u, gel(res,2)), gmul(z,gel(res,1))); return gsub(ellsigma(om,z,1,prec),gmul2n(gmul(z,zst),-1)); } /* Computes phi^*(la,om)/phi^*(1,om) where (1,om) is an oriented basis of the ideal gf*gc^{-1} */ static GEN computeth2(GEN om, GEN la, long prec) { GEN p1,p2,res = ellphistinit(om,prec); p1 = gsub(ellphist(om,res,la,prec), ellphist(om,res,gen_1,prec)); p2 = imag_i(p1); if (gexpo(real_i(p1))>20 || gexpo(p2)> bit_accuracy(min(prec,lg(p2)))-10) return NULL; return gexp(p1,prec); } /* Computes P_2(X)=polynomial in Z_K[X] closest to prod_gc(X-th2(gc)) where the product is over the ray class group bnr.*/ static GEN computeP2(GEN bnr, GEN la, long prec) { long clrayno, i, first = 1; pari_sp av=avma, av2; GEN listray,P0,P,f,lanum, nf = checknf(bnr); f = gmael3(bnr,2,1,1); la = algtobasis_i(nf,la); listray = getallelts(bnr); clrayno = lg(listray)-1; av2 = avma; PRECPB: if (!first) { if (DEBUGLEVEL) pari_warn(warnprec,"computeP2",prec); nf = gerepileupto(av2, nfnewprec(checknf(bnr),prec)); } first = 0; lanum = to_approx(nf,la); P = cgetg(clrayno+1,t_VEC); for (i=1; i<=clrayno; i++) { GEN om = get_om(nf, idealdiv(nf,f,gel(listray,i))); GEN s = computeth2(om,lanum,prec); if (!s) { prec = (prec<<1)-2; goto PRECPB; } gel(P,i) = s; } P0 = roots_to_pol(P, 0); P = findbezk_pol(nf, P0); if (!P) { prec = get_prec(P0, prec); goto PRECPB; } return gerepilecopy(av, P); } #define nexta(a) (a>0 ? -a : 1-a) static GEN do_compo(GEN x, GEN y) { long a, i, l = lg(y); GEN z; y = shallowcopy(y); /* y := t^deg(y) y(#/t) */ for (i = 2; i < l; i++) if (signe(y[i])) gel(y,i) = monomial(gel(y,i), l-i-1, MAXVARN); for (a = 0;; a = nexta(a)) { if (a) x = gsubst(x, 0, gaddsg(a, pol_x[0])); z = gsubst(subres(x,y), MAXVARN, pol_x[0]); if (issquarefree(z)) return z; } } #undef nexta static GEN galoisapplypol(GEN nf, GEN s, GEN x) { long i, lx = lg(x); GEN y = cgetg(lx,t_POL); for (i=2; i>2); if (equalui(ell,D)) /* ell = |D| */ { p2 = gcoeff(nffactor(nf,p2),1,1); return do_compo(p1,p2); } if (ell%4 == 3) ell = -ell; /* nf = K = Q(a), L = K(b) quadratic extension = Q(t) */ polLK = quadpoly(stoi(ell)); /* relative polynomial */ res = rnfequation2(nf, polLK); vx = varn(nf[1]); polL = gsubst(gel(res,1),0,pol_x[vx]); /* = charpoly(t) */ a = gsubst(lift(gel(res,2)), 0,pol_x[vx]); b = gsub(pol_x[vx], gmul(gel(res,3), a)); nfL = initalg(polL, DEFAULTPREC); p1 = gcoeff(nffactor(nfL,p1),1,1); p2 = gcoeff(nffactor(nfL,p2),1,1); p3 = do_compo(p1,p2); /* relative equation over L */ /* compute non trivial s in Gal(L / K) */ sb= gneg(gadd(b, truecoeff(polLK,1))); /* s(b) = Tr(b) - b */ s = gadd(pol_x[vx], gsub(sb, b)); /* s(t) = t + s(b) - b */ p3 = gmul(p3, galoisapplypol(nfL, s, p3)); return findquad_pol(gel(nf,1), a, p3); } /* I integral ideal in HNF. (x) = I, x small in Z ? */ static long isZ(GEN I) { GEN x = gcoeff(I,1,1); if (signe(gcoeff(I,1,2)) || !equalii(x, gcoeff(I,2,2))) return 0; return is_bigint(x)? -1: itos(x); } /* Treat special cases directly. return NULL if not special case */ static GEN treatspecialsigma(GEN nf, GEN gf) { GEN p1, p2, tryf, D = gel(nf,3); long Ds, fl, i = isZ(gf); if (i == 1) return quadhilbertimag(gel(nf,3), NULL); /* f = 1 ? */ if (equaliu(D,3)) { if (i == 4 || i == 5 || i == 7) return cyclo(i,0); if (!equaliu(gcoeff(gf,1,1),9) || !equaliu(content(gf),3)) return NULL; p1 = (GEN)nfroots(nf,cyclo(3,0))[1]; /* f = P_3^3 */ return gadd(monomial(gen_1,3,0), p1); /* x^3+j */ } if (equaliu(D,4)) { if (i == 3 || i == 5) return cyclo(i,0); if (i != 4) return NULL; p1 = (GEN)nfroots(nf,cyclo(4,0))[1]; return gadd(monomial(gen_1,2,0), p1); /* x^2+i */ } Ds = smodis(D,48); if (i) { if (i==2 && Ds%16== 8) return compocyclo(nf, 4,1); if (i==3 && Ds% 3== 1) return compocyclo(nf, 3,1); if (i==4 && Ds% 8== 1) return compocyclo(nf, 4,1); if (i==6 && Ds ==40) return compocyclo(nf,12,1); return NULL; } p1 = gcoeff(gf,1,1); /* integer > 0 */ p2 = gcoeff(gf,2,2); if (gcmp1(p2)) { fl = 0; tryf = p1; } else { if (Ds % 16 != 8 || !equaliu(Q_content(gf),2)) return NULL; fl = 1; tryf = shifti(p1,-1); } /* tryf integer > 0 */ if (cmpiu(tryf, 3) <= 0 || signe(remii(D, tryf)) || !isprime(tryf)) return NULL; i = itos(tryf); if (fl) i *= 4; return compocyclo(nf,i,2); } /* return a vector of all roots of 1 in bnf [not necessarily quadratic] */ static GEN getallrootsof1(GEN bnf) { GEN T, u, nf = checknf(bnf), tu; long i, n = itos(gmael3(bnf,8,4,1)); if (n == 2) { long N = degpol(gel(nf,1)); return mkvec2(gscalcol_i(gen_m1, N), gscalcol_i(gen_1, N)); } tu = poltobasis(nf, gmael3(bnf,8,4,2)); T = eltmul_get_table(nf, tu); u = cgetg(n+1, t_VEC); gel(u,1) = tu; for (i=2; i <= n; i++) gel(u,i) = gmul(T, gel(u,i-1)); return u; } static GEN get_lambda(GEN bnr) { GEN allf, bnf, nf, pol, f, la, P, labas, lamodf, u; long a, b, f2, i, lu, v; allf = conductor(bnr,NULL,2); bnr = gel(allf,2); f = gmael(allf,1,1); bnf= gel(bnr,1); nf = gel(bnf,7); pol= gel(nf,1); v = varn(pol); P = treatspecialsigma(nf,f); if (P) return P; f2 = 2 * itos(gcoeff(f,1,1)); u = getallrootsof1(bnf); lu = lg(u); for (i=1; i1) fprintferr("quadray: looking for [a,b] != unit mod 2f\n[a,b] = "); for (a=0; a1) fprintferr("[%ld,%ld] ",a,b); labas = poltobasis(nf, la); lamodf = colreducemodHNF(labas, f, NULL); for (i=1; i1) fprintferr("\n"); fprintferr("lambda = %Z\n",la); } return labas; } pari_err(bugparier,"get_lambda"); return NULL; } GEN quadray(GEN D, GEN f, GEN flag, long prec) { GEN bnr, y, pol, bnf; pari_sp av = avma; if (flag) { if (typ(flag) != t_VEC || lg(flag)!=3) pari_err(flagerr,"quadray"); } if (typ(D) != t_INT) { bnf = checkbnf(D); if (degpol(gmael(bnf,7,1)) != 2) pari_err(talker,"not a polynomial of degree 2 in quadray"); D = gmael(bnf,7,3); } else { if (!isfundamental(D)) pari_err(talker,"quadray needs a fundamental discriminant"); pol = quadpoly0(D, fetch_user_var("y")); bnf = bnfinit0(pol, signe(D)>0?1:0, NULL, prec); } bnr = bnrinit0(bnf,f,1); if (gcmp1(gmael(bnr,5,1))) { avma = av; return pol_x[0]; } if (signe(D) > 0) y = bnrstark(bnr,NULL,prec); else { if (!flag) flag = get_lambda(bnr); if (typ(flag) == t_POL) y = flag; /* special case */ else y = computeP2(bnr,flag,prec); } return gerepileupto(av, y); } /*******************************************************************/ /* */ /* CLASS GROUP AND REGULATOR (McCURLEY, BUCHMANN) */ /* QUADRATIC FIELDS */ /* */ /*******************************************************************/ /* For largeprime() hashtable. Note that hashed pseudoprimes are odd (unless * 2 | index), hence the low order bit is not useful. So we hash * HASHBITS bits starting at bit 1, not bit 0 */ #define HASHBITS 10 static const long HASHT = 1 << HASHBITS; static long hash(long q) { return (q & ((1 << (HASHBITS+1)) - 1)) >> 1; } #undef HASHBITS /* See buch2.c: * subFB contains split p such that \prod p > sqrt(Disc) * powsubFB contains powers of forms in subFB */ #define RANDOM_BITS 4 static const long CBUCH = (1< Cl (a > 0) */ static GEN qfr3_canon(GEN x) { GEN a = gel(x,1), c = gel(x,3); if (signe(a) < 0) { if (absi_equal(a,c)) return qfr3_rho(x,Disc,isqrtD); setsigne(a, 1); setsigne(c,-1); } return x; } static GEN qfr5_canon(GEN x) { GEN a = gel(x,1), c = gel(x,3); if (signe(a) < 0) { if (absi_equal(a,c)) return qfr5_rho(x,Disc,sqrtD,isqrtD); setsigne(a, 1); setsigne(c,-1); } return x; } static GEN QFR5_comp(GEN x,GEN y) { return qfr5_canon(qfr5_comp(x,y,Disc,sqrtD,isqrtD)); } static GEN QFR3_comp(GEN x, GEN y) { return qfr3_canon(qfr3_comp(x,y,Disc,isqrtD)); } /* compute rho^n(x) */ static GEN qrf5_rho_pow(GEN x, long n) { long i; pari_sp av = avma, lim = stack_lim(av, 1); for (i=1; i<=n; i++) { x = qfr5_rho(x,Disc,sqrtD,isqrtD); if (low_stack(lim, stack_lim(av,1))) { if(DEBUGMEM>1) pari_warn(warnmem,"qrf5_rho_pow"); x = gerepilecopy(av, x); } } return gerepilecopy(av, x); } static GEN qfr5_pf(GEN D, long p) { GEN y = primeform_u(D,p); return qfr5_canon(qfr5_red(qfr_to_qfr5(y,PRECREG), Disc, sqrtD, isqrtD)); } static GEN qfr3_pf(GEN D, long p) { GEN y = primeform_u(D,p); return qfr3_canon(qfr3_red(y, Disc, isqrtD)); } #define qfi_pf primeform_u /* Warning: ex[0] not set in general */ static GEN init_form(long *ex, GEN (*comp)(GEN,GEN)) { long i, l = lg(powsubFB); GEN F = NULL; for (i=1; i= B) pari_err(talker,"sorry, couldn't deal with this field. PLEASE REPORT"); cbach *= 2; if (cbach > B) cbach = B; if (DEBUGLEVEL) fprintferr("\n*** Bach constant: %f\n", cbach); return cbach; } #if 0 static long factorquad(GEN f, long kcz, ulong limp) { ulong p; long i, k, lo; pari_sp av; GEN x = gel(f,1); if (is_pm1(x)) { primfact[0] = 0; return 1; } av = avma; lo = 0; x = absi(x); for (i=1; ; i++) { int stop; k = Z_lvalrem_stop(x, (ulong)FB[i], &stop); if (k) { primfact[++lo] = i; exprimfact[lo] = k; } if (stop) break; if (i == kcz) { avma = av; return 0; } } avma = av; if (lgefint(x) != 3 || (p=(ulong)x[2]) > limhash) return 0; if (p != 1 && p <= limp) { if (badprim && cgcd(p, umodiu(badprim,p)) > 1) return 0; primfact[++lo] = numFB[p]; exprimfact[lo] = 1; p = 1; } primfact[0] = lo; return p; } #else /* Same, Z_lvalrem_stop unrolled. Ugly but more than 30% faster :-( */ /* Is |q| <= p ? */ static int isless_iu(GEN q, ulong p) { long l = lgefint(q); return l==2 || (l == 3 && (ulong)q[2] <= p); } static long factorquad(GEN f, long kcz, ulong limp) { ulong X; long i, lo; pari_sp av; GEN x = gel(f,1); if (is_pm1(x)) { primfact[0] = 0; return 1; } av = avma; lo = 0; for (i=1; lgefint(x) > 3; i++) { ulong p = (ulong)FB[i], r; GEN q = diviu_rem(x, p, &r); if (!r) { long k = 0; do { k++; x = q; q = diviu_rem(x, p, &r); } while (!r); primfact[++lo] = i; exprimfact[lo] = k; } if (isless_iu(q,p)) { avma = av; if (lgefint(x) == 3) { X = (ulong)x[2]; goto END; } return 0; } if (i == kcz) { avma = av; return 0; } } avma = av; X = (ulong)x[2]; for (;; i++) { /* single precision affair, split for efficiency */ ulong p = (ulong)FB[i]; ulong q = X / p, r = X % p; /* gcc makes a single div */ if (!r) { long k = 0; do { k++; X = q; q = X / p; r = X % p; } while (!r); primfact[++lo] = i; exprimfact[lo] = k; } if (q <= p) break; if (i == kcz) return 0; } END: if (X > limhash) return 0; if (X != 1 && X <= limp) { if (badprim && cgcd(X, umodiu(badprim,X)) > 1) return 0; primfact[++lo] = numFB[X]; exprimfact[lo] = 1; X = 1; } primfact[0] = lo; return X; } #endif /* Check for a "large prime relation" involving q; q may not be prime */ static long * largeprime(long q, long *ex, long np, long nrho) { const long hashv = hash(q); long *pt, i, l = lg(subFB); for (pt = hashtab[hashv]; ; pt = (long*) pt[0]) { if (!pt) { pt = (long*) gpmalloc((l+3) * sizeof(long)); *pt++ = nrho; /* nrho = pt[-3] */ *pt++ = np; /* np = pt[-2] */ *pt++ = q; /* q = pt[-1] */ pt[0] = (long)hashtab[hashv]; for (i=1; i> 1; if (r && signe(D) < 0) r = 8-r; return (r < 4); } r = (remii(D, muluu(p,p)) == gen_0); /* p^2 | D ? */ avma = av; return r; } /* create FB, numFB; set badprim. Return L(kro_D, 1) */ static GEN FBquad(GEN Disc, long n2, long n) { GEN Res = real_1(DEFAULTPREC); long i, p, s, LIM; pari_sp av; byteptr d = diffptr; numFB = cgetg(n2+1, t_VECSMALL); FB = cgetg(n2+1, t_VECSMALL); av = avma; KC = 0; i = 0; maxprime_check((ulong)n2); badprim = gen_1; for (p = 0;;) /* p <= n2 */ { NEXT_PRIME_VIADIFF(p, d); if (!KC && p > n) KC = i; if (p > n2) break; s = krois(Disc,p); Res = mulur(p, divrs(Res, p - s)); switch (s) { case -1: break; /* inert */ case 0: /* ramified */ if (is_bad(Disc, (ulong)p)) { badprim = muliu(badprim, p); break; } /* fall through */ default: /* split */ i++; numFB[p] = i; FB[i] = p; break; } } if (!KC) return NULL; KC2 = i; setlg(FB, KC2+1); if (DEBUGLEVEL) { msgtimer("factor base"); if (DEBUGLEVEL>7) fprintferr("FB = %Z\n", FB); } LIM = (expi(Disc) < 16)? 100: 1000; while (p < LIM) { s = krois(Disc,p); Res = mulur(p, divrs(Res, p - s)); NEXT_PRIME_VIADIFF(p, d); } if (badprim != gen_1) gerepileall(av, 2, &Res, &badprim); else { badprim = NULL; Res = gerepileuptoleaf(av, Res); } return Res; } /* create vperm, return subFB */ static GEN subFBquad(GEN D, double PROD, long KC) { long i, j, minSFB, lgsub = 1, ino = 1, lv = KC+1; double prod = 1.; pari_sp av; GEN no; minSFB = (expi(D) > 15)? 3: 2; vperm = cgetg(lv, t_VECSMALL); av = avma; no = cgetg(lv, t_VECSMALL); for (j = 1; j < lv; j++) { ulong p = FB[j]; if (!umodiu(D, p)) no[ino++] = j; /* ramified */ else { vperm[lgsub++] = j; prod *= p; if (lgsub > minSFB && prod > PROD) break; } } if (j == lv) return NULL; i = lgsub; for (j = 1; j < ino;i++,j++) vperm[i] = no[j]; for ( ; i < lv; i++) vperm[i] = i; if (DEBUGLEVEL) msgtimer("subFBquad (%ld elt.)", lgsub-1); no = gclone(vecslice(vperm, 1, lgsub-1)); avma = av; return no; } /* assume n >= 1, x[i][j] = subFB[i]^j, for j = 1..n */ static GEN powsubFBquad(long n) { pari_sp av = avma; long i,j, l = lg(subFB); GEN F, y, x = cgetg(l, t_VEC); if (PRECREG) /* real */ { for (i=1; i p) e = -e; col[k] -= e; } } static void add_fact(GEN col, GEN F) { GEN b = gel(F,2); long i; for (i=1; i<=primfact[0]; i++) { ulong k = primfact[i], p = FB[k]; long e = exprimfact[i]; if (umodiu(b, p<<1) > p) e = -e; col[k] += e; } } static GEN get_clgp(GEN Disc, GEN W, GEN *ptD, long prec) { GEN res, *init, u1, D = smithrel(W,NULL,&u1), Z = prec? real_0(prec): NULL; long i, j, l = lg(W), c = lg(D); if (DEBUGLEVEL) msgtimer("smith/class group"); res=cgetg(c,t_VEC); init = (GEN*)cgetg(l,t_VEC); for (i=1; i trivial relation */ if (umodiu(Disc, FB[i])) continue; col = const_vecsmall(KC, 0); col[i] = 2; j++; gel(mat,j) = col; gel(C,j) = gen_0; } return j; } static void dbg_all(char *phase, long s, long n) { fprintferr("\nTime %s rel [#rel/#test = %ld/%ld]: %ld\n", phase,s,n,timer2()); } void wr_rel(GEN col) { long i, l = lg(col); fprintferr("\nrel = "); for (i=1; i= need) break; avma = av; form = qfi_random(ex); form = compimag(form, qfi_pf(Disc, FB[current])); nbtest++; fpc = factorquad(form,KC,LIMC); if (!fpc) { if (DEBUGLEVEL>1) fprintferr("."); continue; } if (fpc > 1) { long *fpd = largeprime(fpc,ex,current,0); ulong b1, b2, p; GEN form2; if (!fpd) { if (DEBUGLEVEL>1) fprintferr("."); continue; } form2 = compimag(qfi_factorback(fpd), qfi_pf(Disc, FB[fpd[-2]])); p = fpc << 1; b1 = umodiu(gel(form2,2), p); b2 = umodiu(gel(form,2), p); if (b1 != b2 && b1+b2 != p) continue; col = gel(mat,++s); add_fact(col, form); (void)factorquad(form2,KC,LIMC); if (b1==b2) { for (i=1; i KC) current = 1; } if (DEBUGLEVEL) dbg_all("random", s, nbtest); *pc = current; } static int imag_be_honest() { long p, fpc, s = KC, nbtest = 0; GEN F, ex = cgetg(lg(subFB), t_VECSMALL); pari_sp av = avma; while (s 40) return 0; avma = av; } return 1; } /* Real Quadratic fields */ static void real_relations(long need, long *pc, long lim, ulong LIMC, GEN mat, GEN C) { long lgsub = lg(subFB), current = *pc, nbtest = 0, s = 0; long i, fpc, endcycle, rhoacc, rho; /* in a 2nd phase, don't include FB[current] but run along the cyle * ==> get more units */ int first = (current == 0); pari_sp av, av1, limstack; GEN d, col, form, form0, form1, ex = cgetg(lgsub, t_VECSMALL); if (!current) current = 1; if (lim > need) lim = need; av = avma; limstack = stack_lim(av,1); for(;;) { if (s >= need) break; if (first && s >= lim) { first = 0; if (DEBUGLEVEL) dbg_all("initial", s, nbtest); } avma = av; form = qfr3_random(ex); if (!first) form = QFR3_comp(form, qfr3_pf(Disc, FB[current])); av1 = avma; form0 = form; form1 = NULL; endcycle = rhoacc = 0; rho = -1; CYCLE: if (endcycle || rho > 5000) continue; if (low_stack(limstack, stack_lim(av,1))) { if(DEBUGMEM>1) pari_warn(warnmem,"real_relations"); gerepileall(av1, form1? 2: 1, &form, &form1); } if (rho < 0) rho = 0; /* first time in */ else { form = qfr3_rho(form, Disc, isqrtD); rho++; rhoacc++; if (first) endcycle = (absi_equal(gel(form,1),gel(form0,1)) && equalii(gel(form,2),gel(form0,2))); else { if (absi_equal(gel(form,1), gel(form,3))) /* a = -c */ { if (absi_equal(gel(form,1),gel(form0,1)) && equalii(gel(form,2),gel(form0,2))) continue; form = qfr3_rho(form, Disc, isqrtD); rho++; } else { setsigne(form[1],1); setsigne(form[3],-1); } if (equalii(gel(form,1),gel(form0,1)) && equalii(gel(form,2),gel(form0,2))) continue; } } nbtest++; fpc = factorquad(form,KC,LIMC); if (!fpc) { if (DEBUGLEVEL>1) fprintferr("."); goto CYCLE; } if (fpc > 1) { /* look for Large Prime relation */ long *fpd = largeprime(fpc,ex,first? 0: current,rhoacc); ulong b1, b2, p; GEN form2; if (!fpd) { if (DEBUGLEVEL>1) fprintferr("."); goto CYCLE; } if (!form1) { form1 = qfr5_factorback(ex); if (!first) form1 = QFR5_comp(form1, qfr5_pf(Disc, FB[current])); } form1 = qrf5_rho_pow(form1, rho); rho = 0; form2 = qfr5_factorback(fpd); if (fpd[-2]) form2 = QFR5_comp(form2, qfr5_pf(Disc, FB[fpd[-2]])); form2 = qrf5_rho_pow(form2, fpd[-3]); if (!absi_equal(gel(form2,1),gel(form2,3))) { setsigne(form2[1], 1); setsigne(form2[3],-1); } p = fpc << 1; b1 = umodiu(gel(form2,2), p); b2 = umodiu(gel(form1,2), p); if (b1 != b2 && b1+b2 != p) goto CYCLE; col = gel(mat,++s); add_fact(col, form1); (void)factorquad(form2,KC,LIMC); if (b1==b2) { for (i=1; i= lim) continue; goto CYCLE; } else { col[current]--; if (++current > KC) current = 1; } } if (DEBUGLEVEL) dbg_all("random", s, nbtest); *pc = current; } static int real_be_honest() { long p, fpc, s = KC, nbtest = 0; GEN F,F0, ex = cgetg(lg(subFB), t_VECSMALL); pari_sp av = avma; while (s 40) return 0; F = qfr3_canon(qfr3_rho(F, Disc, isqrtD)); if (equalii(gel(F,1),gel(F0,1)) && equalii(gel(F,2),gel(F0,2))) break; } avma = av; } return 1; } static GEN gcdreal(GEN a,GEN b) { long e; GEN k1,r; if (!signe(a)) return mpabs(b); if (!signe(b)) return mpabs(a); if (typ(a)==t_INT) { if (typ(b)==t_INT) return gcdii(a,b); a = itor(a, lg(b)); } else if (typ(b)==t_INT) { b = itor(b, lg(a)); } if (expo(a)<-5) return absr(b); if (expo(b)<-5) return absr(a); a=absr(a); b=absr(b); while (expo(b) >= -5 && signe(b)) { k1 = gcvtoi(divrr(a,b),&e); if (e > 0) return NULL; r=subrr(a,mulir(k1,b)); a=b; b=r; } return absr(a); } static int get_R(GEN C, long sreg, GEN z, GEN *ptR) { GEN R = gen_1; double c; long i; if (PRECREG) { R = mpabs(gel(C,1)); for (i=2; i<=sreg; i++) { R = gcdreal(gel(C,i), R); if (!R) return fupb_PRECI; } if (gexpo(R) <= -3) { if (DEBUGLEVEL) fprintferr("regulator is zero.\n"); return fupb_RELAT; } if (DEBUGLEVEL) fprintferr("#### Tentative regulator: %Z\n",R); } c = gtodouble(gmul(z, R)); if (c < 0.8 || c > 1.3) return fupb_RELAT; *ptR = R; return fupb_NONE; } static int quad_be_honest() { int r; if (KC2 <= KC) return 1; if (DEBUGLEVEL) fprintferr("be honest for primes from %ld to %ld\n", FB[KC+1],FB[KC2]); r = PRECREG? real_be_honest(): imag_be_honest(); if (DEBUGLEVEL) { fprintferr("\n"); msgtimer("be honest"); } return r; } GEN buchquad(GEN D, double cbach, double cbach2, long RELSUP, long prec) { pari_sp av0 = avma, av, av2; long i, s, current, triv, nrelsup, nreldep, need, nsubFB; ulong LIMC, LIMC2, cp; GEN h, W, cyc, res, gen, dep, mat, C, extraC, B, R, resc, Res, z; double drc, lim, LOGD, LOGD2; check_quaddisc(D, &s, /*junk*/&i, "buchquad"); Disc = D; if (s < 0) { if (cmpiu(Disc,4) <= 0) { GEN z = cgetg(6,t_VEC); gel(z,1) = gel(z,4) = gel(z,5) = gen_1; gel(z,2) = gel(z,3) = cgetg(1,t_VEC); return z; } PRECREG = 0; } else { PRECREG = max(prec+1, MEDDEFAULTPREC + 2*(expi(Disc)>>TWOPOTBITS_IN_LONG)); } if (DEBUGLEVEL) (void)timer2(); primfact = new_chunk(100); exprimfact = new_chunk(100); hashtab = (long**) new_chunk(HASHT); for (i=0; i 6.) { if (cbach2 < cbach) cbach2 = cbach; cbach = 6.; } if (cbach <= 0.) pari_err(talker,"Bach constant <= 0 in buchquad"); av = avma; cbach /= 2; powsubFB = subFB = NULL; /* LIMC = Max(cbach*(log D)^2, exp(sqrt(log D loglog D) / 8)) */ START: avma = av; cbach = check_bach(cbach,6.); if (subFB) gunclone(subFB); if (powsubFB) gunclone(powsubFB); clearhash(hashtab); nreldep = nrelsup = 0; LIMC = (ulong)(cbach*LOGD2); if (LIMC < cp) { LIMC = cp; cbach = (double)LIMC / LOGD2; } LIMC2 = (ulong)(max(cbach,cbach2)*LOGD2); if (LIMC2 < LIMC) LIMC2 = LIMC; if (PRECREG) { sqrtD = sqrtr(itor(Disc,PRECREG)); isqrtD = truncr(sqrtD); } Res = FBquad(Disc, LIMC2, LIMC); if (!Res) goto START; subFB = subFBquad(Disc, lim + 0.5, KC); if (!subFB) goto START; nsubFB = lg(subFB) - 1; powsubFB = powsubFBquad(CBUCH+1); limhash = (LIMC & HIGHMASK)? (HIGHBIT>>1): LIMC*LIMC; need = KC + RELSUP - 2; current = 0; W = NULL; s = nsubFB + RELSUP; av2 = avma; MORE: if ((nreldep & 3) == 1 || (nrelsup & 7) == 1) { if (DEBUGLEVEL) fprintferr("*** Changing sub factor base\n"); gunclone(subFB); gunclone(powsubFB); subFB = gclone(vecslice(vperm, 1, nsubFB)); powsubFB = powsubFBquad(CBUCH+1); clearhash(hashtab); } need += 2; mat = cgetg(need+1, t_MAT); extraC = cgetg(need+1, t_VEC); if (!W) { /* first time */ C = extraC; triv = trivial_relations(mat, KC, C, Disc); if (DEBUGLEVEL) fprintferr("KC = %ld, need %ld relations\n", KC, need); } else { triv = 0; if (DEBUGLEVEL) fprintferr("...need %ld more relations\n", need); } if (PRECREG) { for (i = triv+1; i<=need; i++) { gel(mat,i) = const_vecsmall(KC, 0); gel(extraC,i) = cgetr(PRECREG); } real_relations(need - triv, ¤t, s,LIMC,mat + triv,extraC + triv); } else { for (i = triv+1; i<=need; i++) { gel(mat,i) = const_vecsmall(KC, 0); gel(extraC,i) = gen_0; } imag_relations(need - triv, ¤t, s,LIMC,mat + triv); } if (!W) W = hnfspec_i((long**)mat,vperm,&dep,&B,&C,nsubFB); else W = hnfadd_i(W,vperm,&dep,&B,&C, mat,extraC); gerepileall(av2, 4, &W,&C,&B,&dep); need = lg(dep)>1? lg(dep[1])-1: lg(B[1])-1; if (need) { if (++nreldep > 15 && cbach < 1) goto START; goto MORE; } h = dethnf_i(W); if (DEBUGLEVEL) fprintferr("\n#### Tentative class number: %Z\n", h); z = mulrr(Res, resc); /* ~ hR if enough relations, a multiple otherwise */ switch(get_R(C, (lg(C)-1) - (lg(B)-1) - (lg(W)-1), divir(h,z), &R)) { case fupb_PRECI: PRECREG = (PRECREG<<1)-2; cbach /= 2; goto START; case fupb_RELAT: if (++nrelsup <= 7 || cbach > 1) { need = min(KC, nrelsup); if (cbach > 1 && nsubFB < 3 && lg(vperm) > 3) nsubFB++; goto MORE; } goto START; } /* DONE */ if (!quad_be_honest()) goto START; clearhash(hashtab); gen = get_clgp(Disc,W,&cyc,PRECREG); gunclone(subFB); gunclone(powsubFB); res = cgetg(5,t_VEC); gel(res,1) = h; gel(res,2) = cyc; gel(res,3) = gen; gel(res,4) = R; return gerepilecopy(av0,res); } GEN buchimag(GEN D, GEN c, GEN c2, GEN REL) { return buchquad(D,gtodouble(c),gtodouble(c2),itos(REL), 0); } GEN buchreal(GEN D, GEN flag, GEN c, GEN c2, GEN REL, long prec) { if (signe(flag)) pari_err(impl,"narrow class group"); return buchquad(D,gtodouble(c),gtodouble(c2),itos(REL), prec); } GEN quadclassunit0(GEN x, long flag, GEN data, long prec) { long lx, RELSUP; double cbach, cbach2; if (!data) lx=1; else { lx = lg(data); if (typ(data)!=t_VEC || lx > 7) pari_err(talker,"incorrect parameters in quadclassunit"); if (lx > 4) lx = 4; } cbach = cbach2 = 0.2; /* was 0.1, but slower on average for 20 digits disc */ RELSUP = 5; switch(lx) { case 4: RELSUP = itos(gel(data,3)); case 3: cbach2 = gtodouble(gel(data,2)); case 2: cbach = gtodouble(gel(data,1)); } if (flag) pari_err(impl,"narrow class group"); return buchquad(x,cbach,cbach2,RELSUP,prec); }