Testing latest pari + WASM + node.js... and it works?! Wow.
License: GPL3
ubuntu2004
/* Copyright (C) 2000 The PARI group.12This file is part of the PARI/GP package.34PARI/GP is free software; you can redistribute it and/or modify it under the5terms of the GNU General Public License as published by the Free Software6Foundation; either version 2 of the License, or (at your option) any later7version. It is distributed in the hope that it will be useful, but WITHOUT8ANY WARRANTY WHATSOEVER.910Check the License for details. You should have received a copy of it, along11with the package; see the file 'COPYING'. If not, write to the Free Software12Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */13#include "pari.h"14#include "paripriv.h"1516#define DEBUGLEVEL DEBUGLEVEL_alg1718#define dbg_printf(lvl) if (DEBUGLEVEL >= (lvl) + 3) err_printf1920/********************************************************************/21/** **/22/** ASSOCIATIVE ALGEBRAS, CENTRAL SIMPLE ALGEBRAS **/23/** contributed by Aurel Page (2014) **/24/** **/25/********************************************************************/26static GEN alg_subalg(GEN al, GEN basis);27static GEN alg_maximal_primes(GEN al, GEN P);28static GEN algnatmultable(GEN al, long D);29static GEN _tablemul_ej(GEN mt, GEN x, long j);30static GEN _tablemul_ej_Fp(GEN mt, GEN x, long j, GEN p);31static GEN _tablemul_ej_Fl(GEN mt, GEN x, long j, ulong p);32static ulong algtracei(GEN mt, ulong p, ulong expo, ulong modu);33static GEN alg_pmaximal(GEN al, GEN p);34static GEN alg_maximal(GEN al);35static GEN algtracematrix(GEN al);36static GEN algtableinit_i(GEN mt0, GEN p);37static GEN algbasisrightmultable(GEN al, GEN x);38static GEN algabstrace(GEN al, GEN x);39static GEN algbasismul(GEN al, GEN x, GEN y);40static GEN algbasismultable(GEN al, GEN x);41static GEN algbasismultable_Flm(GEN mt, GEN x, ulong m);4243static int44checkalg_i(GEN al)45{46GEN mt, rnf;47if (typ(al) != t_VEC || lg(al) != 12) return 0;48mt = alg_get_multable(al);49if (typ(mt) != t_VEC || lg(mt) == 1 || typ(gel(mt,1)) != t_MAT) return 0;50rnf = alg_get_splittingfield(al);51if (isintzero(rnf) || !gequal0(alg_get_char(al))) return 1;52if (typ(gel(al,2)) != t_VEC || lg(gel(al,2)) == 1) return 0;53/* not checkrnf_i: beware placeholder from alg_csa_table */54return typ(rnf)==t_VEC && lg(rnf)==13;55}56void57checkalg(GEN al)58{ if (!checkalg_i(al)) pari_err_TYPE("checkalg [please apply alginit()]",al); }5960static int61checklat_i(GEN al, GEN lat)62{63long N,i,j;64GEN m,t,c;65if (typ(lat)!=t_VEC || lg(lat) != 3) return 0;66t = gel(lat,2);67if (typ(t) != t_INT && typ(t) != t_FRAC) return 0;68if (gsigne(t)<=0) return 0;69m = gel(lat,1);70if (typ(m) != t_MAT) return 0;71N = alg_get_absdim(al);72if (lg(m)-1 != N || lg(gel(m,1))-1 != N) return 0;73for (i=1; i<=N; i++)74for (j=1; j<=N; j++) {75c = gcoeff(m,i,j);76if (typ(c) != t_INT) return 0;77if (j<i && signe(gcoeff(m,i,j))) return 0;78if (i==j && !signe(gcoeff(m,i,j))) return 0;79}80return 1;81}82void checklat(GEN al, GEN lat)83{ if (!checklat_i(al,lat)) pari_err_TYPE("checklat [please apply alglathnf()]", lat); }8485/** ACCESSORS **/86long87alg_type(GEN al)88{89if (isintzero(alg_get_splittingfield(al)) || !gequal0(alg_get_char(al))) return al_TABLE;90switch(typ(gmael(al,2,1))) {91case t_MAT: return al_CSA;92case t_INT:93case t_FRAC:94case t_POL:95case t_POLMOD: return al_CYCLIC;96default: return al_NULL;97}98return -1; /*LCOV_EXCL_LINE*/99}100long101algtype(GEN al)102{ return checkalg_i(al)? alg_type(al): al_NULL; }103104/* absdim == dim for al_TABLE. */105long106alg_get_dim(GEN al)107{108long d;109switch(alg_type(al)) {110case al_TABLE: return lg(alg_get_multable(al))-1;111case al_CSA: return lg(alg_get_relmultable(al))-1;112case al_CYCLIC: d = alg_get_degree(al); return d*d;113default: pari_err_TYPE("alg_get_dim", al);114}115return -1; /*LCOV_EXCL_LINE*/116}117118long119alg_get_absdim(GEN al)120{121switch(alg_type(al)) {122case al_TABLE: return lg(alg_get_multable(al))-1;123case al_CSA: return alg_get_dim(al)*nf_get_degree(alg_get_center(al));124case al_CYCLIC:125return rnf_get_absdegree(alg_get_splittingfield(al))*alg_get_degree(al);126default: pari_err_TYPE("alg_get_absdim", al);127}128return -1;/*LCOV_EXCL_LINE*/129}130131long132algdim(GEN al, long abs)133{134checkalg(al);135if (abs) return alg_get_absdim(al);136return alg_get_dim(al);137}138139/* only cyclic */140GEN141alg_get_auts(GEN al)142{143if (alg_type(al) != al_CYCLIC)144pari_err_TYPE("alg_get_auts [noncyclic algebra]", al);145return gel(al,2);146}147GEN148alg_get_aut(GEN al)149{150if (alg_type(al) != al_CYCLIC)151pari_err_TYPE("alg_get_aut [noncyclic algebra]", al);152return gel(alg_get_auts(al),1);153}154GEN155algaut(GEN al) { checkalg(al); return alg_get_aut(al); }156GEN157alg_get_b(GEN al)158{159if (alg_type(al) != al_CYCLIC)160pari_err_TYPE("alg_get_b [noncyclic algebra]", al);161return gel(al,3);162}163GEN164algb(GEN al) { checkalg(al); return alg_get_b(al); }165166/* only CSA */167GEN168alg_get_relmultable(GEN al)169{170if (alg_type(al) != al_CSA)171pari_err_TYPE("alg_get_relmultable [algebra not given via mult. table]", al);172return gel(al,2);173}174GEN175algrelmultable(GEN al) { checkalg(al); return alg_get_relmultable(al); }176GEN177alg_get_splittingdata(GEN al)178{179if (alg_type(al) != al_CSA)180pari_err_TYPE("alg_get_splittingdata [algebra not given via mult. table]",al);181return gel(al,3);182}183GEN184algsplittingdata(GEN al) { checkalg(al); return alg_get_splittingdata(al); }185GEN186alg_get_splittingbasis(GEN al)187{188if (alg_type(al) != al_CSA)189pari_err_TYPE("alg_get_splittingbasis [algebra not given via mult. table]",al);190return gmael(al,3,2);191}192GEN193alg_get_splittingbasisinv(GEN al)194{195if (alg_type(al) != al_CSA)196pari_err_TYPE("alg_get_splittingbasisinv [algebra not given via mult. table]",al);197return gmael(al,3,3);198}199200/* only cyclic and CSA */201GEN202alg_get_splittingfield(GEN al) { return gel(al,1); }203GEN204algsplittingfield(GEN al)205{206long ta;207checkalg(al);208ta = alg_type(al);209if (ta != al_CYCLIC && ta != al_CSA)210pari_err_TYPE("alg_get_splittingfield [use alginit]",al);211return alg_get_splittingfield(al);212}213long214alg_get_degree(GEN al)215{216long ta;217ta = alg_type(al);218if (ta != al_CYCLIC && ta != al_CSA)219pari_err_TYPE("alg_get_degree [use alginit]",al);220return rnf_get_degree(alg_get_splittingfield(al));221}222long223algdegree(GEN al)224{225checkalg(al);226return alg_get_degree(al);227}228229GEN230alg_get_center(GEN al)231{232long ta;233ta = alg_type(al);234if (ta != al_CSA && ta != al_CYCLIC)235pari_err_TYPE("alg_get_center [use alginit]",al);236return rnf_get_nf(alg_get_splittingfield(al));237}238GEN239alg_get_splitpol(GEN al)240{241long ta = alg_type(al);242if (ta != al_CYCLIC && ta != al_CSA)243pari_err_TYPE("alg_get_splitpol [use alginit]",al);244return rnf_get_pol(alg_get_splittingfield(al));245}246GEN247alg_get_abssplitting(GEN al)248{249long ta = alg_type(al), prec;250if (ta != al_CYCLIC && ta != al_CSA)251pari_err_TYPE("alg_get_abssplitting [use alginit]",al);252prec = nf_get_prec(alg_get_center(al));253return rnf_build_nfabs(alg_get_splittingfield(al), prec);254}255GEN256alg_get_hasse_i(GEN al)257{258long ta = alg_type(al);259if (ta != al_CYCLIC && ta != al_CSA)260pari_err_TYPE("alg_get_hasse_i [use alginit]",al);261if (ta == al_CSA) pari_err_IMPL("computation of Hasse invariants over table CSA");262return gel(al,4);263}264GEN265alghassei(GEN al) { checkalg(al); return alg_get_hasse_i(al); }266GEN267alg_get_hasse_f(GEN al)268{269long ta = alg_type(al);270if (ta != al_CYCLIC && ta != al_CSA)271pari_err_TYPE("alg_get_hasse_f [use alginit]",al);272if (ta == al_CSA) pari_err_IMPL("computation of Hasse invariants over table CSA");273return gel(al,5);274}275GEN276alghassef(GEN al) { checkalg(al); return alg_get_hasse_f(al); }277278/* all types */279GEN280alg_get_basis(GEN al) { return gel(al,7); }281GEN282algbasis(GEN al) { checkalg(al); return alg_get_basis(al); }283GEN284alg_get_invbasis(GEN al) { return gel(al,8); }285GEN286alginvbasis(GEN al) { checkalg(al); return alg_get_invbasis(al); }287GEN288alg_get_multable(GEN al) { return gel(al,9); }289GEN290algmultable(GEN al) { checkalg(al); return alg_get_multable(al); }291GEN292alg_get_char(GEN al) { return gel(al,10); }293GEN294algchar(GEN al) { checkalg(al); return alg_get_char(al); }295GEN296alg_get_tracebasis(GEN al) { return gel(al,11); }297298/* lattices */299GEN300alglat_get_primbasis(GEN lat) { return gel(lat,1); }301GEN302alglat_get_scalar(GEN lat) { return gel(lat,2); }303304/** ADDITIONAL **/305306/* no garbage collection */307static GEN308backtrackfacto(GEN y0, long n, GEN red, GEN pl, GEN nf, GEN data, int (*test)(GEN,GEN), GEN* fa, GEN N, GEN I)309{310long b, i;311ulong lim = 1UL << 17;312long *v = new_chunk(n+1);313pari_sp av = avma;314for (b = 0;; b += (2*b)/(3*n) + 1)315{316GEN ny, y1, y2;317set_avma(av);318for (i = 1; i <= n; i++) v[i] = -b;319v[n]--;320for(;;)321{322i = n;323while (i > 0)324{ if (v[i] == b) v[i--] = -b; else { v[i]++; break; } }325if (i==0) break;326327y1 = y0;328for (i = 1; i <= n; i++) y1 = nfadd(nf, y1, ZC_z_mul(gel(red,i), v[i]));329if (!nfchecksigns(nf, y1, pl)) continue;330331ny = absi_shallow(nfnorm(nf, y1));332if (!signe(ny)) continue;333ny = diviiexact(ny, gcdii(ny, N));334if (!Z_issmooth(ny, lim)) continue;335336y2 = idealdivexact(nf, y1, idealadd(nf,y1,I));337*fa = idealfactor(nf, y2);338if (!data || test(data,*fa)) return y1;339}340}341}342343/* if data == NULL, the test is skipped */344/* in the test, the factorization does not contain the known factors */345static GEN346factoredextchinesetest(GEN nf, GEN x, GEN y, GEN pl, GEN* fa, GEN data, int (*test)(GEN,GEN))347{348pari_sp av = avma;349long n,i;350GEN x1, y0, y1, red, N, I, P = gel(x,1), E = gel(x,2);351n = nf_get_degree(nf);352x = idealchineseinit(nf, mkvec2(x,pl));353x1 = gel(x,1);354red = lg(x1) == 1? matid(n): gel(x1,1);355y0 = idealchinese(nf, x, y);356357E = shallowcopy(E);358if (!gequal0(y0))359for (i=1; i<lg(E); i++)360{361long v = nfval(nf,y0,gel(P,i));362if (cmpsi(v, gel(E,i)) < 0) gel(E,i) = stoi(v);363}364/* N and I : known factors */365I = factorbackprime(nf, P, E);366N = idealnorm(nf,I);367368y1 = backtrackfacto(y0, n, red, pl, nf, data, test, fa, N, I);369370/* restore known factors */371for (i=1; i<lg(E); i++) gel(E,i) = stoi(nfval(nf,y1,gel(P,i)));372*fa = famat_reduce(famat_mul_shallow(*fa, mkmat2(P, E)));373374gerepileall(av, 2, &y1, fa);375return y1;376}377378static GEN379factoredextchinese(GEN nf, GEN x, GEN y, GEN pl, GEN* fa)380{ return factoredextchinesetest(nf,x,y,pl,fa,NULL,NULL); }381382/** OPERATIONS ON ASSOCIATIVE ALGEBRAS algebras.c **/383384/*385Convention:386(K/F,sigma,b) = sum_{i=0..n-1} u^i*K387t*u = u*sigma(t)388389Natural basis:3901<=i<=d*n^2391b_i = u^((i-1)/(dn))*ZKabs.((i-1)%(dn)+1)392393Integral basis:394Basis of some order.395396al:3971- rnf of the cyclic splitting field of degree n over the center nf of degree d3982- VEC of aut^i 1<=i<=n3993- b in nf4004- infinite hasse invariants (mod n) : VECSMALL of size r1, values only 0 or n/2 (if integral)4015- finite hasse invariants (mod n) : VEC[VEC of primes, VECSMALL of hasse inv mod n]4026- nf of the splitting field (absolute)4037* dn^2*dn^2 matrix expressing the integral basis in terms of the natural basis4048* dn^2*dn^2 matrix expressing the natural basis in terms of the integral basis4059* VEC of dn^2 matrices giving the dn^2*dn^2 left multiplication tables of the integral basis40610* characteristic of the base field (used only for algebras given by a multiplication table)40711* trace of basis elements408409If al is given by a multiplication table (al_TABLE), only the * fields are present.410*/411412/* assumes same center and same variable */413/* currently only works for coprime degrees */414GEN415algtensor(GEN al1, GEN al2, long maxord) {416pari_sp av = avma;417long v, k, d1, d2;418GEN nf, P1, P2, aut1, aut2, b1, b2, C, rnf, aut, b, x1, x2, al;419420checkalg(al1);421checkalg(al2);422if (alg_type(al1) != al_CYCLIC || alg_type(al2) != al_CYCLIC)423pari_err_IMPL("tensor of noncyclic algebras"); /* TODO: do it. */424425nf=alg_get_center(al1);426if (!gequal(alg_get_center(al2),nf))427pari_err_OP("tensor product [not the same center]", al1, al2);428429P1=alg_get_splitpol(al1); aut1=alg_get_aut(al1); b1=alg_get_b(al1);430P2=alg_get_splitpol(al2); aut2=alg_get_aut(al2); b2=alg_get_b(al2);431v=varn(P1);432433d1=alg_get_degree(al1);434d2=alg_get_degree(al2);435if (ugcd(d1,d2) != 1)436pari_err_IMPL("tensor of cylic algebras of noncoprime degrees"); /* TODO */437438if (d1==1) return gcopy(al2);439if (d2==1) return gcopy(al1);440441C = nfcompositum(nf, P1, P2, 3);442rnf = rnfinit(nf,gel(C,1));443x1 = gel(C,2);444x2 = gel(C,3);445k = itos(gel(C,4));446aut = gadd(gsubst(aut2,v,x2),gmulsg(k,gsubst(aut1,v,x1)));447b = nfmul(nf,nfpow_u(nf,b1,d2),nfpow_u(nf,b2,d1));448al = alg_cyclic(rnf,aut,b,maxord);449return gerepilecopy(av,al);450}451452/* M an n x d Flm of rank d, n >= d. Initialize Mx = y solver */453static GEN454Flm_invimage_init(GEN M, ulong p)455{456GEN v = Flm_indexrank(M, p), perm = gel(v,1);457GEN MM = rowpermute(M, perm); /* square invertible */458return mkvec2(Flm_inv(MM,p), perm);459}460/* assume Mx = y has a solution, v = Flm_invimage_init(M,p); return x */461static GEN462Flm_invimage_pre(GEN v, GEN y, ulong p)463{464GEN inv = gel(v,1), perm = gel(v,2);465return Flm_Flc_mul(inv, vecsmallpermute(y, perm), p);466}467468GEN469algradical(GEN al)470{471pari_sp av = avma;472GEN I, x, traces, K, MT, P, mt;473long l,i,ni, n;474ulong modu, expo, p;475checkalg(al);476P = alg_get_char(al);477mt = alg_get_multable(al);478n = alg_get_absdim(al);479dbg_printf(1)("algradical: char=%Ps, dim=%d\n", P, n);480traces = algtracematrix(al);481if (!signe(P))482{483dbg_printf(2)(" char 0, computing kernel...\n");484K = ker(traces);485dbg_printf(2)(" ...done.\n");486ni = lg(K)-1; if (!ni) return gc_const(av, gen_0);487return gerepileupto(av, K);488}489dbg_printf(2)(" char>0, computing kernel...\n");490K = FpM_ker(traces, P);491dbg_printf(2)(" ...done.\n");492ni = lg(K)-1; if (!ni) return gc_const(av, gen_0);493if (abscmpiu(P,n)>0) return gerepileupto(av, K);494495/* tough case, p <= n. Ronyai's algorithm */496p = P[2]; l = 1;497expo = p; modu = p*p;498dbg_printf(2)(" char>0, hard case.\n");499while (modu<=(ulong)n) { l++; modu *= p; }500MT = ZMV_to_FlmV(mt, modu);501I = ZM_to_Flm(K,p); /* I_0 */502for (i=1; i<=l; i++) {/*compute I_i, expo = p^i, modu = p^(l+1) > n*/503long j, lig,col;504GEN v = cgetg(ni+1, t_VECSMALL);505GEN invI = Flm_invimage_init(I, p);506dbg_printf(2)(" computing I_%d:\n", i);507traces = cgetg(ni+1,t_MAT);508for (j = 1; j <= ni; j++)509{510GEN M = algbasismultable_Flm(MT, gel(I,j), modu);511uel(v,j) = algtracei(M, p,expo,modu);512}513for (col=1; col<=ni; col++)514{515GEN t = cgetg(n+1,t_VECSMALL); gel(traces,col) = t;516x = gel(I, col); /*col-th basis vector of I_{i-1}*/517for (lig=1; lig<=n; lig++)518{519GEN y = _tablemul_ej_Fl(MT,x,lig,p);520GEN z = Flm_invimage_pre(invI, y, p);521uel(t,lig) = Flv_dotproduct(v, z, p);522}523}524dbg_printf(2)(" computing kernel...\n");525K = Flm_ker(traces, p);526dbg_printf(2)(" ...done.\n");527ni = lg(K)-1; if (!ni) return gc_const(av, gen_0);528I = Flm_mul(I,K,p);529expo *= p;530}531return Flm_to_ZM(I);532}533534/* compute the multiplication table of the element x, where mt is a535* multiplication table in an arbitrary ring */536static GEN537Rgmultable(GEN mt, GEN x)538{539long i, l = lg(x);540GEN z = NULL;541for (i = 1; i < l; i++)542{543GEN c = gel(x,i);544if (!gequal0(c))545{546GEN M = RgM_Rg_mul(gel(mt,i),c);547z = z? RgM_add(z, M): M;548}549}550return z;551}552553static GEN554change_Rgmultable(GEN mt, GEN P, GEN Pi)555{556GEN mt2;557long lmt = lg(mt), i;558mt2 = cgetg(lmt,t_VEC);559for (i=1;i<lmt;i++) {560GEN mti = Rgmultable(mt,gel(P,i));561gel(mt2,i) = RgM_mul(Pi, RgM_mul(mti,P));562}563return mt2;564}565566static GEN567alg_quotient0(GEN al, GEN S, GEN Si, long nq, GEN p, long maps)568{569GEN mt = cgetg(nq+1,t_VEC), P, Pi, d;570long i;571dbg_printf(3)(" alg_quotient0: char=%Ps, dim=%d, dim I=%d\n", p, alg_get_absdim(al), lg(S)-1);572for (i=1; i<=nq; i++) {573GEN mti = algbasismultable(al,gel(S,i));574if (signe(p)) gel(mt,i) = FpM_mul(Si, FpM_mul(mti,S,p), p);575else gel(mt,i) = RgM_mul(Si, RgM_mul(mti,S));576}577if (!signe(p) && !isint1(Q_denom(mt))) {578dbg_printf(3)(" bad case: denominator=%Ps\n", Q_denom(mt));579P = Q_remove_denom(Si,&d);580P = ZM_hnf(P);581P = RgM_Rg_div(P,d);582Pi = RgM_inv(P);583mt = change_Rgmultable(mt,P,Pi);584Si = RgM_mul(P,Si);585S = RgM_mul(S,Pi);586}587al = algtableinit_i(mt,p);588if (maps) al = mkvec3(al,Si,S); /* algebra, proj, lift */589return al;590}591592/* quotient of an algebra by a nontrivial two-sided ideal */593GEN594alg_quotient(GEN al, GEN I, long maps)595{596pari_sp av = avma;597GEN p, IS, ISi, S, Si;598long n, ni;599600checkalg(al);601p = alg_get_char(al);602n = alg_get_absdim(al);603ni = lg(I)-1;604605/* force first vector of complement to be the identity */606IS = shallowconcat(I, gcoeff(alg_get_multable(al),1,1));607if (signe(p)) {608IS = FpM_suppl(IS,p);609ISi = FpM_inv(IS,p);610}611else {612IS = suppl(IS);613ISi = RgM_inv(IS);614}615S = vecslice(IS, ni+1, n);616Si = rowslice(ISi, ni+1, n);617return gerepilecopy(av, alg_quotient0(al, S, Si, n-ni, p, maps));618}619620static GEN621image_keep_first(GEN m, GEN p) /* assume first column is nonzero or m==0, no GC */622{623GEN ir, icol, irow, M, c, x;624long i;625if (gequal0(gel(m,1))) return zeromat(nbrows(m),0);626627if (signe(p)) ir = FpM_indexrank(m,p);628else ir = indexrank(m);629630icol = gel(ir,2);631if (icol[1]==1) return extract0(m,icol,NULL);632633irow = gel(ir,1);634M = extract0(m, irow, icol);635c = extract0(gel(m,1), irow, NULL);636if (signe(p)) x = FpM_FpC_invimage(M,c,p);637else x = inverseimage(M,c); /* TODO modulo a small prime */638639for (i=1; i<lg(x); i++)640{641if (!gequal0(gel(x,i)))642{643icol[i] = 1;644vecsmall_sort(icol);645return extract0(m,icol,NULL);646}647}648649return NULL; /* LCOV_EXCL_LINE */650}651652/* z[1],...z[nz] central elements such that z[1]A + z[2]A + ... + z[nz]A = A653* is a direct sum. idempotents ==> first basis element is identity */654GEN655alg_centralproj(GEN al, GEN z, long maps)656{657pari_sp av = avma;658GEN S, U, Ui, alq, p;659long i, iu, lz = lg(z);660661checkalg(al);662if (typ(z) != t_VEC) pari_err_TYPE("alcentralproj",z);663p = alg_get_char(al);664dbg_printf(3)(" alg_centralproj: char=%Ps, dim=%d, #z=%d\n", p, alg_get_absdim(al), lz-1);665S = cgetg(lz,t_VEC); /* S[i] = Im(z_i) */666for (i=1; i<lz; i++)667{668GEN mti = algbasismultable(al, gel(z,i));669gel(S,i) = image_keep_first(mti,p);670}671U = shallowconcat1(S); /* U = [Im(z_1)|Im(z_2)|...|Im(z_nz)], n x n */672if (lg(U)-1 < alg_get_absdim(al)) pari_err_TYPE("alcentralproj [z[i]'s not surjective]",z);673if (signe(p)) Ui = FpM_inv(U,p);674else Ui = RgM_inv(U);675if (!Ui) pari_err_BUG("alcentralproj"); /*LCOV_EXCL_LINE*/676677alq = cgetg(lz,t_VEC);678for (iu=0,i=1; i<lz; i++)679{680long nq = lg(gel(S,i))-1, ju = iu + nq;681GEN Si = rowslice(Ui, iu+1, ju);682gel(alq, i) = alg_quotient0(al,gel(S,i),Si,nq,p,maps);683iu = ju;684}685return gerepilecopy(av, alq);686}687688/* al is an al_TABLE */689static GEN690algtablecenter(GEN al)691{692pari_sp av = avma;693long n, i, j, k, ic;694GEN C, cij, mt, p;695696n = alg_get_absdim(al);697mt = alg_get_multable(al);698p = alg_get_char(al);699C = cgetg(n+1,t_MAT);700for (j=1; j<=n; j++)701{702gel(C,j) = cgetg(n*n-n+1,t_COL);703ic = 1;704for (i=2; i<=n; i++) {705if (signe(p)) cij = FpC_sub(gmael(mt,i,j),gmael(mt,j,i),p);706else cij = RgC_sub(gmael(mt,i,j),gmael(mt,j,i));707for (k=1; k<=n; k++, ic++) gcoeff(C,ic,j) = gel(cij, k);708}709}710if (signe(p)) return gerepileupto(av, FpM_ker(C,p));711else return gerepileupto(av, ker(C));712}713714GEN715algcenter(GEN al)716{717checkalg(al);718if (alg_type(al)==al_TABLE) return algtablecenter(al);719return alg_get_center(al);720}721722/* Only in positive characteristic. Assumes that al is semisimple. */723GEN724algprimesubalg(GEN al)725{726pari_sp av = avma;727GEN p, Z, F, K;728long nz, i;729checkalg(al);730p = alg_get_char(al);731if (!signe(p)) pari_err_DOMAIN("algprimesubalg","characteristic","=",gen_0,p);732733Z = algtablecenter(al);734nz = lg(Z)-1;735if (nz==1) return Z;736737F = cgetg(nz+1, t_MAT);738for (i=1; i<=nz; i++) {739GEN zi = gel(Z,i);740gel(F,i) = FpC_sub(algpow(al,zi,p),zi,p);741}742K = FpM_ker(F,p);743return gerepileupto(av, FpM_mul(Z,K,p));744}745746static GEN747out_decompose(GEN t, GEN Z, GEN P, GEN p)748{749GEN ali = gel(t,1), projm = gel(t,2), liftm = gel(t,3), pZ;750if (signe(p)) pZ = FpM_image(FpM_mul(projm,Z,p),p);751else pZ = image(RgM_mul(projm,Z));752return mkvec5(ali, projm, liftm, pZ, P);753}754/* fa factorization of charpol(x) */755static GEN756alg_decompose_from_facto(GEN al, GEN x, GEN fa, GEN Z, long mini)757{758long k = lgcols(fa)-1, k2 = mini? 1: k/2;759GEN v1 = rowslice(fa,1,k2);760GEN v2 = rowslice(fa,k2+1,k);761GEN alq, P, Q, p = alg_get_char(al);762dbg_printf(3)(" alg_decompose_from_facto\n");763if (signe(p)) {764P = FpXV_factorback(gel(v1,1), gel(v1,2), p, 0);765Q = FpXV_factorback(gel(v2,1), gel(v2,2), p, 0);766P = FpX_mul(P, FpXQ_inv(P,Q,p), p);767}768else {769P = factorback(v1);770Q = factorback(v2);771P = RgX_mul(P, RgXQ_inv(P,Q));772}773P = algpoleval(al, P, x);774if (signe(p)) Q = FpC_sub(col_ei(lg(P)-1,1), P, p);775else Q = gsub(gen_1, P);776if (gequal0(P) || gequal0(Q)) return NULL;777alq = alg_centralproj(al, mkvec2(P,Q), 1);778779P = out_decompose(gel(alq,1), Z, P, p); if (mini) return P;780Q = out_decompose(gel(alq,2), Z, Q, p);781return mkvec2(P,Q);782}783784static GEN785random_pm1(long n)786{787GEN z = cgetg(n+1,t_VECSMALL);788long i;789for (i = 1; i <= n; i++) z[i] = random_bits(5)%3 - 1;790return z;791}792793static GEN alg_decompose(GEN al, GEN Z, long mini, GEN* pt_primelt);794/* Try to split al using x's charpoly. Return gen_0 if simple, NULL if failure.795* And a splitting otherwise796* If pt_primelt!=NULL, compute a primitive element of the center when simple */797static GEN798try_fact(GEN al, GEN x, GEN zx, GEN Z, GEN Zal, long mini, GEN* pt_primelt)799{800GEN z, dec0, dec1, cp = algcharpoly(Zal,zx,0,1), fa, p = alg_get_char(al);801long nfa, e;802dbg_printf(3)(" try_fact: zx=%Ps\n", zx);803if (signe(p)) fa = FpX_factor(cp,p);804else fa = factor(cp);805dbg_printf(3)(" charpoly=%Ps\n", fa);806nfa = nbrows(fa);807if (nfa == 1) {808if (signe(p)) e = gel(fa,2)[1];809else e = itos(gcoeff(fa,1,2));810if (e == 1) {811if (pt_primelt != NULL) *pt_primelt = mkvec2(x, cp);812return gen_0;813}814else return NULL;815}816dec0 = alg_decompose_from_facto(al, x, fa, Z, mini);817if (!dec0) return NULL;818if (!mini) return dec0;819dec1 = alg_decompose(gel(dec0,1), gel(dec0,4), 1, pt_primelt);820z = gel(dec0,5);821if (!isintzero(dec1)) {822if (signe(p)) z = FpM_FpC_mul(gel(dec0,3),dec1,p);823else z = RgM_RgC_mul(gel(dec0,3),dec1);824}825return z;826}827static GEN828randcol(long n, GEN b)829{830GEN N = addiu(shifti(b,1), 1);831long i;832GEN res = cgetg(n+1,t_COL);833for (i=1; i<=n; i++)834{835pari_sp av = avma;836gel(res,i) = gerepileuptoint(av, subii(randomi(N),b));837}838return res;839}840/* Return gen_0 if already simple. mini: only returns a central idempotent841* corresponding to one simple factor842* if pt_primelt!=NULL, sets it to a primitive element of the center when simple */843static GEN844alg_decompose(GEN al, GEN Z, long mini, GEN* pt_primelt)845{846pari_sp av;847GEN Zal, x, zx, rand, dec0, B, p;848long i, nz = lg(Z)-1;849850if (nz == 1) {851if (pt_primelt != 0) *pt_primelt = mkvec2(zerocol(alg_get_dim(al)), pol_x(0));852return gen_0;853}854p = alg_get_char(al);855dbg_printf(2)(" alg_decompose: char=%Ps, dim=%d, dim Z=%d\n", p, alg_get_absdim(al), nz);856Zal = alg_subalg(al,Z);857Z = gel(Zal,2);858Zal = gel(Zal,1);859av = avma;860861rand = random_pm1(nz);862zx = zc_to_ZC(rand);863if (signe(p)) {864zx = FpC_red(zx,p);865x = ZM_zc_mul(Z,rand);866x = FpC_red(x,p);867}868else x = RgM_zc_mul(Z,rand);869dec0 = try_fact(al,x,zx,Z,Zal,mini,pt_primelt);870if (dec0) return dec0;871set_avma(av);872873for (i=2; i<=nz; i++)874{875dec0 = try_fact(al,gel(Z,i),col_ei(nz,i),Z,Zal,mini,pt_primelt);876if (dec0) return dec0;877set_avma(av);878}879B = int2n(10);880for (;;)881{882GEN x = randcol(nz,B), zx = ZM_ZC_mul(Z,x);883dec0 = try_fact(al,x,zx,Z,Zal,mini,pt_primelt);884if (dec0) return dec0;885set_avma(av);886}887}888889static GEN890alg_decompose_total(GEN al, GEN Z, long maps)891{892GEN dec, sc, p;893long i;894895dec = alg_decompose(al, Z, 0, NULL);896if (isintzero(dec))897{898if (maps) {899long n = alg_get_absdim(al);900al = mkvec3(al, matid(n), matid(n));901}902return mkvec(al);903}904p = alg_get_char(al); if (!signe(p)) p = NULL;905sc = cgetg(lg(dec), t_VEC);906for (i=1; i<lg(sc); i++) {907GEN D = gel(dec,i), a = gel(D,1), Za = gel(D,4);908GEN S = alg_decompose_total(a, Za, maps);909gel(sc,i) = S;910if (maps)911{912GEN projm = gel(D,2), liftm = gel(D,3);913long j, lS = lg(S);914for (j=1; j<lS; j++)915{916GEN Sj = gel(S,j), p2 = gel(Sj,2), l2 = gel(Sj,3);917if (p) p2 = FpM_mul(p2, projm, p);918else p2 = RgM_mul(p2, projm);919if (p) l2 = FpM_mul(liftm, l2, p);920else l2 = RgM_mul(liftm, l2);921gel(Sj,2) = p2;922gel(Sj,3) = l2;923}924}925}926return shallowconcat1(sc);927}928929static GEN930alg_subalg(GEN al, GEN basis)931{932GEN invbasis, mt, p = alg_get_char(al);933long i, j, n = lg(basis)-1;934935if (!signe(p)) p = NULL;936basis = shallowmatconcat(mkvec2(col_ei(n,1), basis));937if (p)938{939basis = image_keep_first(basis,p);940invbasis = FpM_inv(basis,p);941}942else943{ /* FIXME use an integral variant of image_keep_first */944basis = QM_ImQ_hnf(basis);945invbasis = RgM_inv(basis);946}947mt = cgetg(n+1,t_VEC);948gel(mt,1) = matid(n);949for (i = 2; i <= n; i++)950{951GEN mtx = cgetg(n+1,t_MAT), x = gel(basis,i);952gel(mtx,1) = col_ei(n,i);953for (j = 2; j <= n; j++)954{955GEN xy = algmul(al, x, gel(basis,j));956if (p) gel(mtx,j) = FpM_FpC_mul(invbasis, xy, p);957else gel(mtx,j) = RgM_RgC_mul(invbasis, xy);958}959gel(mt,i) = mtx;960}961return mkvec2(algtableinit_i(mt,p), basis);962}963964GEN965algsubalg(GEN al, GEN basis)966{967pari_sp av = avma;968GEN p;969checkalg(al);970if (typ(basis) != t_MAT) pari_err_TYPE("algsubalg",basis);971p = alg_get_char(al);972if (signe(p)) basis = RgM_to_FpM(basis,p);973return gerepilecopy(av, alg_subalg(al,basis));974}975976static int977cmp_algebra(GEN x, GEN y)978{979long d;980d = gel(x,1)[1] - gel(y,1)[1]; if (d) return d < 0? -1: 1;981d = gel(x,1)[2] - gel(y,1)[2]; if (d) return d < 0? -1: 1;982return cmp_universal(gel(x,2), gel(y,2));983}984985GEN986algsimpledec_ss(GEN al, long maps)987{988pari_sp av = avma;989GEN Z, p, r, res, perm;990long i, l, n;991checkalg(al);992p = alg_get_char(al);993dbg_printf(1)("algsimpledec_ss: char=%Ps, dim=%d\n", p, alg_get_absdim(al));994if (signe(p)) Z = algprimesubalg(al);995else Z = algtablecenter(al);996997if (lg(Z) == 2) {/* dim Z = 1 */998n = alg_get_absdim(al);999set_avma(av);1000if (!maps) return mkveccopy(al);1001retmkvec(mkvec3(gcopy(al), matid(n), matid(n)));1002}1003res = alg_decompose_total(al, Z, maps);1004l = lg(res); r = cgetg(l, t_VEC);1005for (i = 1; i < l; i++)1006{1007GEN A = maps? gmael(res,i,1): gel(res,i);1008gel(r,i) = mkvec2(mkvecsmall2(alg_get_dim(A), lg(algtablecenter(A))),1009alg_get_multable(A));1010}1011perm = gen_indexsort(r, (void*)cmp_algebra, &cmp_nodata);1012return gerepilecopy(av, vecpermute(res, perm));1013}10141015GEN1016algsimpledec(GEN al, long maps)1017{1018pari_sp av = avma;1019int ss;1020GEN rad, dec, res, proj=NULL, lift=NULL;1021rad = algradical(al);1022ss = gequal0(rad);1023if (!ss)1024{1025al = alg_quotient(al, rad, maps);1026if (maps) {1027proj = gel(al,2);1028lift = gel(al,3);1029al = gel(al,1);1030}1031}1032dec = algsimpledec_ss(al, maps);1033if (!ss && maps) /* update maps */1034{1035GEN p = alg_get_char(al);1036long i;1037for (i=1; i<lg(dec); i++)1038{1039if (signe(p))1040{1041gmael(dec,i,2) = FpM_mul(gmael(dec,i,2), proj, p);1042gmael(dec,i,3) = FpM_mul(lift, gmael(dec,i,3), p);1043}1044else1045{1046gmael(dec,i,2) = RgM_mul(gmael(dec,i,2), proj);1047gmael(dec,i,3) = RgM_mul(lift, gmael(dec,i,3));1048}1049}1050}1051res = mkvec2(rad, dec);1052return gerepilecopy(av,res);1053}10541055static GEN alg_idempotent(GEN al, long n, long d);1056static GEN1057try_split(GEN al, GEN x, long n, long d)1058{1059GEN cp, p = alg_get_char(al), fa, e, pol, exp, P, Q, U, u, mx, mte, ire;1060long nfa, i, smalldim = alg_get_absdim(al)+1, dim, smalli = 0;1061cp = algcharpoly(al,x,0,1);1062fa = FpX_factor(cp,p);1063nfa = nbrows(fa);1064if (nfa == 1) return NULL;1065pol = gel(fa,1);1066exp = gel(fa,2);10671068/* charpoly is always a d-th power */1069for (i=1; i<lg(exp); i++) {1070if (exp[i]%d) pari_err(e_MISC, "the algebra must be simple (try_split 1)");1071exp[i] /= d;1072}1073cp = FpXV_factorback(gel(fa,1), gel(fa,2), p, 0);10741075/* find smallest Fp-dimension of a characteristic space */1076for (i=1; i<lg(pol); i++) {1077dim = degree(gel(pol,i))*exp[i];1078if (dim < smalldim) {1079smalldim = dim;1080smalli = i;1081}1082}1083i = smalli;1084if (smalldim != n) return NULL;1085/* We could also compute e*al*e and try again with this smaller algebra */1086/* Fq-rank 1 = Fp-rank n idempotent: success */10871088/* construct idempotent */1089mx = algbasismultable(al,x);1090P = gel(pol,i);1091P = FpX_powu(P, exp[i], p);1092Q = FpX_div(cp, P, p);1093e = algpoleval(al, Q, mkvec2(x,mx));1094U = FpXQ_inv(Q, P, p);1095u = algpoleval(al, U, mkvec2(x,mx));1096e = algbasismul(al, e, u);1097mte = algbasisrightmultable(al,e);1098ire = FpM_indexrank(mte,p);1099if (lg(gel(ire,1))-1 != smalldim*d) pari_err(e_MISC, "the algebra must be simple (try_split 2)");11001101return mkvec3(e,mte,ire);1102}11031104/*1105* Given a simple algebra al of dimension d^2 over its center of degree n,1106* find an idempotent e in al with rank n (which is minimal).1107*/1108static GEN1109alg_idempotent(GEN al, long n, long d)1110{1111pari_sp av = avma;1112long i, N = alg_get_absdim(al);1113GEN e, p = alg_get_char(al), x;1114for(i=2; i<=N; i++) {1115x = col_ei(N,i);1116e = try_split(al, x, n, d);1117if (e) return e;1118set_avma(av);1119}1120for(;;) {1121x = random_FpC(N,p);1122e = try_split(al, x, n, d);1123if (e) return e;1124set_avma(av);1125}1126}11271128static GEN1129try_descend(GEN M, GEN B, GEN p, long m, long n, long d)1130{1131GEN B2 = cgetg(m+1,t_MAT), b;1132long i, j, k=0;1133for (i=1; i<=d; i++)1134{1135k++;1136b = gel(B,i);1137gel(B2,k) = b;1138for (j=1; j<n; j++)1139{1140k++;1141b = FpM_FpC_mul(M,b,p);1142gel(B2,k) = b;1143}1144}1145if (!signe(FpM_det(B2,p))) return NULL;1146return FpM_inv(B2,p);1147}11481149/* Given an m*m matrix M with irreducible charpoly over F of degree n,1150* let K = F(M), which is a field, and write m=d*n.1151* Compute the d-dimensional K-vector space structure on V=F^m induced by M.1152* Return [B,C] where:1153* - B is m*d matrix over F giving a K-basis b_1,...,b_d of V1154* - C is d*m matrix over F[x] expressing the canonical F-basis of V on the b_i1155* Currently F = Fp TODO extend this. */1156static GEN1157descend_i(GEN M, long n, GEN p)1158{1159GEN B, C;1160long m,d,i;1161pari_sp av;1162m = lg(M)-1;1163d = m/n;1164B = cgetg(d+1,t_MAT);1165av = avma;11661167/* try a subset of the canonical basis */1168for (i=1; i<=d; i++)1169gel(B,i) = col_ei(m,n*(i-1)+1);1170C = try_descend(M,B,p,m,n,d);1171if (C) return mkvec2(B,C);1172set_avma(av);11731174/* try smallish elements */1175for (i=1; i<=d; i++)1176gel(B,i) = FpC_red(zc_to_ZC(random_pm1(m)),p);1177C = try_descend(M,B,p,m,n,d);1178if (C) return mkvec2(B,C);1179set_avma(av);11801181/* try random elements */1182for (;;)1183{1184for (i=1; i<=d; i++)1185gel(B,i) = random_FpC(m,p);1186C = try_descend(M,B,p,m,n,d);1187if (C) return mkvec2(B,C);1188set_avma(av);1189}1190}1191static GEN1192RgC_contract(GEN C, long n, long v) /* n>1 */1193{1194GEN C2, P;1195long m, d, i, j;1196m = lg(C)-1;1197d = m/n;1198C2 = cgetg(d+1,t_COL);1199for (i=1; i<=d; i++)1200{1201P = pol_xn(n-1,v);1202for (j=1; j<=n; j++)1203gel(P,j+1) = gel(C,n*(i-1)+j);1204P = normalizepol(P);1205gel(C2,i) = P;1206}1207return C2;1208}1209static GEN1210RgM_contract(GEN A, long n, long v) /* n>1 */1211{1212GEN A2 = cgetg(lg(A),t_MAT);1213long i;1214for (i=1; i<lg(A2); i++)1215gel(A2,i) = RgC_contract(gel(A,i),n,v);1216return A2;1217}1218static GEN1219descend(GEN M, long n, GEN p, long v)1220{1221GEN res = descend_i(M,n,p);1222gel(res,2) = RgM_contract(gel(res,2),n,v);1223return res;1224}12251226/* isomorphism of Fp-vector spaces M_d(F_p^n) -> (F_p)^(d^2*n) */1227static GEN1228Fq_mat2col(GEN M, long d, long n)1229{1230long N = d*d*n, i, j, k;1231GEN C = cgetg(N+1, t_COL);1232for (i=1; i<=d; i++)1233for (j=1; j<=d; j++)1234for (k=0; k<n; k++)1235gel(C,n*(d*(i-1)+j-1)+k+1) = polcoef_i(gcoeff(M,i,j),k,-1);1236return C;1237}12381239static GEN1240alg_finite_csa_split(GEN al, long v)1241{1242GEN Z, e, mte, ire, primelt, b, T, M, proje, lifte, extre, p, B, C, mt, mx, map, mapi, T2, ro;1243long n, d, N = alg_get_absdim(al), i;1244p = alg_get_char(al);1245/* compute the center */1246Z = algcenter(al);1247/* TODO option to give the center as input instead of computing it */1248n = lg(Z)-1;12491250/* compute a minimal rank idempotent e */1251if (n==N) {1252d = 1;1253e = col_ei(N,1);1254mte = matid(N);1255ire = mkvec2(identity_perm(n),identity_perm(n));1256}1257else {1258d = usqrt(N/n);1259if (d*d*n != N) pari_err(e_MISC, "the algebra must be simple (alg_finite_csa_split 1)");1260e = alg_idempotent(al,n,d);1261mte = gel(e,2);1262ire = gel(e,3);1263e = gel(e,1);1264}12651266/* identify the center */1267if (n==1)1268{1269T = pol_x(v);1270primelt = gen_0;1271}1272else1273{1274b = alg_decompose(al, Z, 1, &primelt);1275if (!gequal0(b)) pari_err(e_MISC, "the algebra must be simple (alg_finite_csa_split 2)");1276T = gel(primelt,2);1277primelt = gel(primelt,1);1278setvarn(T,v);1279}12801281/* use the ffinit polynomial */1282if (n>1)1283{1284T2 = init_Fq(p,n,v);1285setvarn(T,fetch_var_higher());1286ro = FpXQX_roots(T2,T,p);1287ro = gel(ro,1);1288primelt = algpoleval(al,ro,primelt);1289T = T2;1290}12911292/* descend al*e to a vector space over the center */1293/* lifte: al*e -> al ; proje: al*e -> al */1294lifte = shallowextract(mte,gel(ire,2));1295extre = shallowmatextract(mte,gel(ire,1),gel(ire,2));1296extre = FpM_inv(extre,p);1297proje = rowpermute(mte,gel(ire,1));1298proje = FpM_mul(extre,proje,p);1299if (n==1)1300{1301B = lifte;1302C = proje;1303}1304else1305{1306M = algbasismultable(al,primelt);1307M = FpM_mul(M,lifte,p);1308M = FpM_mul(proje,M,p);1309B = descend(M,n,p,v);1310C = gel(B,2);1311B = gel(B,1);1312B = FpM_mul(lifte,B,p);1313C = FqM_mul(C,proje,T,p);1314}13151316/* compute the isomorphism */1317mt = alg_get_multable(al);1318map = cgetg(N+1,t_VEC);1319M = cgetg(N+1,t_MAT);1320for (i=1; i<=N; i++)1321{1322mx = gel(mt,i);1323mx = FpM_mul(mx,B,p);1324mx = FqM_mul(C,mx,T,p);1325gel(map,i) = mx;1326gel(M,i) = Fq_mat2col(mx,d,n);1327}1328mapi = FpM_inv(M,p);1329if (!mapi) pari_err(e_MISC, "the algebra must be simple (alg_finite_csa_split 3)");1330return mkvec3(T,map,mapi);1331}13321333GEN1334algsplit(GEN al, long v)1335{1336pari_sp av = avma;1337GEN res, T, map, mapi, ff, p;1338long i,j,k,li,lj;1339checkalg(al);1340p = alg_get_char(al);1341if (gequal0(p))1342pari_err_IMPL("splitting a characteristic 0 algebra over its center");1343res = alg_finite_csa_split(al, v);1344T = gel(res,1);1345map = gel(res,2);1346mapi = gel(res,3);1347ff = Tp_to_FF(T,p);1348for (i=1; i<lg(map); i++)1349{1350li = lg(gel(map,i));1351for (j=1; j<li; j++)1352{1353lj = lg(gmael(map,i,j));1354for (k=1; k<lj; k++)1355gmael3(map,i,j,k) = Fq_to_FF(gmael3(map,i,j,k),ff);1356}1357}13581359return gerepilecopy(av, mkvec2(map,mapi));1360}13611362/* multiplication table sanity checks */1363static GEN1364check_mt_noid(GEN mt, GEN p)1365{1366long i, l;1367GEN MT = cgetg_copy(mt, &l);1368if (typ(MT) != t_VEC || l == 1) return NULL;1369for (i = 1; i < l; i++)1370{1371GEN M = gel(mt,i);1372if (typ(M) != t_MAT || lg(M) != l || lgcols(M) != l) return NULL;1373if (p) M = RgM_to_FpM(M,p);1374gel(MT,i) = M;1375}1376return MT;1377}1378static GEN1379check_mt(GEN mt, GEN p)1380{1381long i;1382GEN MT;1383MT = check_mt_noid(mt, p);1384if (!MT || !ZM_isidentity(gel(MT,1))) return NULL;1385for (i=2; i<lg(MT); i++)1386if (ZC_is_ei(gmael(MT,i,1)) != i) return NULL;1387return MT;1388}13891390static GEN1391check_relmt(GEN nf, GEN mt)1392{1393long i, l = lg(mt), j, k;1394GEN MT = gcopy(mt), a, b, d;1395if (typ(MT) != t_VEC || l == 1) return NULL;1396for (i = 1; i < l; i++)1397{1398GEN M = gel(MT,i);1399if (typ(M) != t_MAT || lg(M) != l || lgcols(M) != l) return NULL;1400for (k = 1; k < l; k++)1401for (j = 1; j < l; j++)1402{1403a = gcoeff(M,j,k);1404if (typ(a)==t_INT) continue;1405b = algtobasis(nf,a);1406d = Q_denom(b);1407if (!isint1(d))1408pari_err_DOMAIN("alg_csa_table", "denominator(mt)", "!=", gen_1, mt);1409gcoeff(M,j,k) = lift(basistoalg(nf,b));1410}1411if (i > 1 && RgC_is_ei(gel(M,1)) != i) return NULL; /* i = 1 checked at end */1412gel(MT,i) = M;1413}1414if (!RgM_isidentity(gel(MT,1))) return NULL;1415return MT;1416}14171418int1419algisassociative(GEN mt0, GEN p)1420{1421pari_sp av = avma;1422long i, j, k, n;1423GEN M, mt;14241425if (checkalg_i(mt0)) { p = alg_get_char(mt0); mt0 = alg_get_multable(mt0); }1426if (typ(p) != t_INT) pari_err_TYPE("algisassociative",p);1427mt = check_mt_noid(mt0, isintzero(p)? NULL: p);1428if (!mt) pari_err_TYPE("algisassociative (mult. table)", mt0);1429if (!ZM_isidentity(gel(mt,1))) return gc_bool(av,0);1430n = lg(mt)-1;1431M = cgetg(n+1,t_MAT);1432for (j=1; j<=n; j++) gel(M,j) = cgetg(n+1,t_COL);1433for (i=1; i<=n; i++)1434{1435GEN mi = gel(mt,i);1436for (j=1; j<=n; j++) gcoeff(M,i,j) = gel(mi,j); /* ei.ej */1437}1438for (i=2; i<=n; i++) {1439GEN mi = gel(mt,i);1440for (j=2; j<=n; j++) {1441for (k=2; k<=n; k++) {1442GEN x, y;1443if (signe(p)) {1444x = _tablemul_ej_Fp(mt,gcoeff(M,i,j),k,p);1445y = FpM_FpC_mul(mi,gcoeff(M,j,k),p);1446}1447else {1448x = _tablemul_ej(mt,gcoeff(M,i,j),k);1449y = RgM_RgC_mul(mi,gcoeff(M,j,k));1450}1451/* not cmp_universal: must not fail on 0 == Mod(0,2) for instance */1452if (!gequal(x,y)) return gc_bool(av,0);1453}1454}1455}1456return gc_bool(av,1);1457}14581459int1460algiscommutative(GEN al) /* assumes e_1 = 1 */1461{1462long i,j,k,N,sp;1463GEN mt,a,b,p;1464checkalg(al);1465if (alg_type(al) != al_TABLE) return alg_get_degree(al)==1;1466N = alg_get_absdim(al);1467mt = alg_get_multable(al);1468p = alg_get_char(al);1469sp = signe(p);1470for (i=2; i<=N; i++)1471for (j=2; j<=N; j++)1472for (k=1; k<=N; k++) {1473a = gcoeff(gel(mt,i),k,j);1474b = gcoeff(gel(mt,j),k,i);1475if (sp) {1476if (cmpii(Fp_red(a,p), Fp_red(b,p))) return 0;1477}1478else if (gcmp(a,b)) return 0;1479}1480return 1;1481}14821483int1484algissemisimple(GEN al)1485{1486pari_sp av = avma;1487GEN rad;1488checkalg(al);1489if (alg_type(al) != al_TABLE) return 1;1490rad = algradical(al);1491set_avma(av);1492return gequal0(rad);1493}14941495/* ss : known to be semisimple */1496int1497algissimple(GEN al, long ss)1498{1499pari_sp av = avma;1500GEN Z, dec, p;1501checkalg(al);1502if (alg_type(al) != al_TABLE) return 1;1503if (!ss && !algissemisimple(al)) return 0;15041505p = alg_get_char(al);1506if (signe(p)) Z = algprimesubalg(al);1507else Z = algtablecenter(al);15081509if (lg(Z) == 2) {/* dim Z = 1 */1510set_avma(av);1511return 1;1512}1513dec = alg_decompose(al, Z, 1, NULL);1514set_avma(av);1515return gequal0(dec);1516}15171518static long1519is_place_emb(GEN nf, GEN pl)1520{1521long r, r1, r2;1522if (typ(pl) != t_INT) pari_err_TYPE("is_place_emb", pl);1523if (signe(pl)<=0) pari_err_DOMAIN("is_place_emb", "pl", "<=", gen_0, pl);1524nf_get_sign(nf,&r1,&r2); r = r1+r2;1525if (cmpiu(pl,r)>0) pari_err_DOMAIN("is_place_emb", "pl", ">", utoi(r), pl);1526return itou(pl);1527}15281529static long1530alghasse_emb(GEN al, long emb)1531{1532GEN nf = alg_get_center(al);1533long r1 = nf_get_r1(nf);1534return (emb <= r1)? alg_get_hasse_i(al)[emb]: 0;1535}15361537static long1538alghasse_pr(GEN al, GEN pr)1539{1540GEN hf = alg_get_hasse_f(al);1541long i = tablesearch(gel(hf,1), pr, &cmp_prime_ideal);1542return i? gel(hf,2)[i]: 0;1543}15441545static long1546alghasse_0(GEN al, GEN pl)1547{1548GEN pr, nf;1549if (alg_type(al)== al_CSA)1550pari_err_IMPL("computation of Hasse invariants over table CSA");1551if ((pr = get_prid(pl))) return alghasse_pr(al, pr);1552nf = alg_get_center(al);1553return alghasse_emb(al, is_place_emb(nf, pl));1554}1555GEN1556alghasse(GEN al, GEN pl)1557{1558long h;1559checkalg(al);1560if (alg_type(al) == al_TABLE) pari_err_TYPE("alghasse [use alginit]",al);1561h = alghasse_0(al,pl);1562return sstoQ(h, alg_get_degree(al));1563}15641565/* h >= 0, d >= 0 */1566static long1567indexfromhasse(long h, long d) { return d/ugcd(h,d); }15681569long1570algindex(GEN al, GEN pl)1571{1572long d, res, i, l;1573GEN hi, hf;15741575checkalg(al);1576if (alg_type(al) == al_TABLE) pari_err_TYPE("algindex [use alginit]",al);1577d = alg_get_degree(al);1578if (pl) return indexfromhasse(alghasse_0(al,pl), d);15791580/* else : global index */1581res = 1;1582hi = alg_get_hasse_i(al); l = lg(hi);1583for (i=1; i<l && res!=d; i++) res = ulcm(res, indexfromhasse(hi[i],d));1584hf = gel(alg_get_hasse_f(al), 2); l = lg(hf);1585for (i=1; i<l && res!=d; i++) res = ulcm(res, indexfromhasse(hf[i],d));1586return res;1587}15881589int1590algisdivision(GEN al, GEN pl)1591{1592checkalg(al);1593if (alg_type(al) == al_TABLE) {1594if (!algissimple(al,0)) return 0;1595if (algiscommutative(al)) return 1;1596pari_err_IMPL("algisdivision for table algebras");1597}1598return algindex(al,pl) == alg_get_degree(al);1599}16001601int1602algissplit(GEN al, GEN pl)1603{1604checkalg(al);1605if (alg_type(al) == al_TABLE) pari_err_TYPE("algissplit [use alginit]", al);1606return algindex(al,pl) == 1;1607}16081609int1610algisramified(GEN al, GEN pl)1611{1612checkalg(al);1613if (alg_type(al) == al_TABLE) pari_err_TYPE("algisramified [use alginit]", al);1614return algindex(al,pl) != 1;1615}16161617GEN1618algramifiedplaces(GEN al)1619{1620pari_sp av = avma;1621GEN ram, hf, hi, Lpr;1622long r1, count, i;1623checkalg(al);1624if (alg_type(al) == al_TABLE) pari_err_TYPE("algramifiedplaces [use alginit]", al);1625r1 = nf_get_r1(alg_get_center(al));1626hi = alg_get_hasse_i(al);1627hf = alg_get_hasse_f(al);1628Lpr = gel(hf,1);1629hf = gel(hf,2);1630ram = cgetg(r1+lg(Lpr), t_VEC);1631count = 0;1632for (i=1; i<=r1; i++)1633if (hi[i]) {1634count++;1635gel(ram,count) = stoi(i);1636}1637for (i=1; i<lg(Lpr); i++)1638if (hf[i]) {1639count++;1640gel(ram,count) = gel(Lpr,i);1641}1642setlg(ram, count+1);1643return gerepilecopy(av, ram);1644}16451646/** OPERATIONS ON ELEMENTS operations.c **/16471648static long1649alg_model0(GEN al, GEN x)1650{1651long t, N = alg_get_absdim(al), lx = lg(x), d, n, D, i;1652if (typ(x) == t_MAT) return al_MATRIX;1653if (typ(x) != t_COL) return al_INVALID;1654if (N == 1) {1655if (lx != 2) return al_INVALID;1656switch(typ(gel(x,1)))1657{1658case t_INT: case t_FRAC: return al_TRIVIAL; /* cannot distinguish basis and alg from size */1659case t_POL: case t_POLMOD: return al_ALGEBRAIC;1660default: return al_INVALID;1661}1662}16631664switch(alg_type(al)) {1665case al_TABLE:1666if (lx != N+1) return al_INVALID;1667return al_BASIS;1668case al_CYCLIC:1669d = alg_get_degree(al);1670if (lx == N+1) return al_BASIS;1671if (lx == d+1) return al_ALGEBRAIC;1672return al_INVALID;1673case al_CSA:1674D = alg_get_dim(al);1675n = nf_get_degree(alg_get_center(al));1676if (n == 1) {1677if (lx != D+1) return al_INVALID;1678for (i=1; i<=D; i++) {1679t = typ(gel(x,i));1680if (t == t_POL || t == t_POLMOD) return al_ALGEBRAIC;1681/* TODO t_COL for coefficients in basis form ? */1682}1683return al_BASIS;1684}1685else {1686if (lx == N+1) return al_BASIS;1687if (lx == D+1) return al_ALGEBRAIC;1688return al_INVALID;1689}1690}1691return al_INVALID; /* LCOV_EXCL_LINE */1692}16931694static void1695checkalgx(GEN x, long model)1696{1697long t, i;1698switch(model) {1699case al_BASIS:1700for (i=1; i<lg(x); i++) {1701t = typ(gel(x,i));1702if (t != t_INT && t != t_FRAC)1703pari_err_TYPE("checkalgx", gel(x,i));1704}1705return;1706case al_TRIVIAL:1707case al_ALGEBRAIC:1708for (i=1; i<lg(x); i++) {1709t = typ(gel(x,i));1710if (t != t_INT && t != t_FRAC && t != t_POL && t != t_POLMOD)1711/* TODO t_COL ? */1712pari_err_TYPE("checkalgx", gel(x,i));1713}1714return;1715}1716}17171718long1719alg_model(GEN al, GEN x)1720{1721long res = alg_model0(al, x);1722if (res == al_INVALID) pari_err_TYPE("alg_model", x);1723checkalgx(x, res); return res;1724}17251726static GEN1727alC_add_i(GEN al, GEN x, GEN y, long lx)1728{1729GEN A = cgetg(lx, t_COL);1730long i;1731for (i=1; i<lx; i++) gel(A,i) = algadd(al, gel(x,i), gel(y,i));1732return A;1733}1734static GEN1735alM_add(GEN al, GEN x, GEN y)1736{1737long lx = lg(x), l, j;1738GEN z;1739if (lg(y) != lx) pari_err_DIM("alM_add (rows)");1740if (lx == 1) return cgetg(1, t_MAT);1741z = cgetg(lx, t_MAT); l = lgcols(x);1742if (lgcols(y) != l) pari_err_DIM("alM_add (columns)");1743for (j = 1; j < lx; j++) gel(z,j) = alC_add_i(al, gel(x,j), gel(y,j), l);1744return z;1745}1746GEN1747algadd(GEN al, GEN x, GEN y)1748{1749pari_sp av = avma;1750long tx, ty;1751GEN p;1752checkalg(al);1753tx = alg_model(al,x);1754ty = alg_model(al,y);1755p = alg_get_char(al);1756if (signe(p)) return FpC_add(x,y,p);1757if (tx==ty) {1758if (tx!=al_MATRIX) return gadd(x,y);1759return gerepilecopy(av, alM_add(al,x,y));1760}1761if (tx==al_ALGEBRAIC) x = algalgtobasis(al,x);1762if (ty==al_ALGEBRAIC) y = algalgtobasis(al,y);1763return gerepileupto(av, gadd(x,y));1764}17651766GEN1767algneg(GEN al, GEN x) { checkalg(al); (void)alg_model(al,x); return gneg(x); }17681769static GEN1770alC_sub_i(GEN al, GEN x, GEN y, long lx)1771{1772long i;1773GEN A = cgetg(lx, t_COL);1774for (i=1; i<lx; i++) gel(A,i) = algsub(al, gel(x,i), gel(y,i));1775return A;1776}1777static GEN1778alM_sub(GEN al, GEN x, GEN y)1779{1780long lx = lg(x), l, j;1781GEN z;1782if (lg(y) != lx) pari_err_DIM("alM_sub (rows)");1783if (lx == 1) return cgetg(1, t_MAT);1784z = cgetg(lx, t_MAT); l = lgcols(x);1785if (lgcols(y) != l) pari_err_DIM("alM_sub (columns)");1786for (j = 1; j < lx; j++) gel(z,j) = alC_sub_i(al, gel(x,j), gel(y,j), l);1787return z;1788}1789GEN1790algsub(GEN al, GEN x, GEN y)1791{1792long tx, ty;1793pari_sp av = avma;1794GEN p;1795checkalg(al);1796tx = alg_model(al,x);1797ty = alg_model(al,y);1798p = alg_get_char(al);1799if (signe(p)) return FpC_sub(x,y,p);1800if (tx==ty) {1801if (tx != al_MATRIX) return gsub(x,y);1802return gerepilecopy(av, alM_sub(al,x,y));1803}1804if (tx==al_ALGEBRAIC) x = algalgtobasis(al,x);1805if (ty==al_ALGEBRAIC) y = algalgtobasis(al,y);1806return gerepileupto(av, gsub(x,y));1807}18081809static GEN1810algalgmul_cyc(GEN al, GEN x, GEN y)1811{1812pari_sp av = avma;1813long n = alg_get_degree(al), i, k;1814GEN xalg, yalg, res, rnf, auts, sum, b, prod, autx;1815rnf = alg_get_splittingfield(al);1816auts = alg_get_auts(al);1817b = alg_get_b(al);18181819xalg = cgetg(n+1, t_COL);1820for (i=0; i<n; i++)1821gel(xalg,i+1) = lift_shallow(rnfbasistoalg(rnf,gel(x,i+1)));18221823yalg = cgetg(n+1, t_COL);1824for (i=0; i<n; i++) gel(yalg,i+1) = rnfbasistoalg(rnf,gel(y,i+1));18251826res = cgetg(n+1,t_COL);1827for (k=0; k<n; k++) {1828gel(res,k+1) = gmul(gel(xalg,k+1),gel(yalg,1));1829for (i=1; i<=k; i++) {1830autx = poleval(gel(xalg,k-i+1),gel(auts,i));1831prod = gmul(autx,gel(yalg,i+1));1832gel(res,k+1) = gadd(gel(res,k+1), prod);1833}18341835sum = gen_0;1836for (; i<n; i++) {1837autx = poleval(gel(xalg,k+n-i+1),gel(auts,i));1838prod = gmul(autx,gel(yalg,i+1));1839sum = gadd(sum,prod);1840}1841sum = gmul(b,sum);18421843gel(res,k+1) = gadd(gel(res,k+1),sum);1844}18451846return gerepilecopy(av, res);1847}18481849static GEN1850_tablemul(GEN mt, GEN x, GEN y)1851{1852pari_sp av = avma;1853long D = lg(mt)-1, i;1854GEN res = NULL;1855for (i=1; i<=D; i++) {1856GEN c = gel(x,i);1857if (!gequal0(c)) {1858GEN My = RgM_RgC_mul(gel(mt,i),y);1859GEN t = RgC_Rg_mul(My,c);1860res = res? RgC_add(res,t): t;1861}1862}1863if (!res) { set_avma(av); return zerocol(D); }1864return gerepileupto(av, res);1865}18661867static GEN1868_tablemul_Fp(GEN mt, GEN x, GEN y, GEN p)1869{1870pari_sp av = avma;1871long D = lg(mt)-1, i;1872GEN res = NULL;1873for (i=1; i<=D; i++) {1874GEN c = gel(x,i);1875if (signe(c)) {1876GEN My = FpM_FpC_mul(gel(mt,i),y,p);1877GEN t = FpC_Fp_mul(My,c,p);1878res = res? FpC_add(res,t,p): t;1879}1880}1881if (!res) { set_avma(av); return zerocol(D); }1882return gerepileupto(av, res);1883}18841885/* x*ej */1886static GEN1887_tablemul_ej(GEN mt, GEN x, long j)1888{1889pari_sp av = avma;1890long D = lg(mt)-1, i;1891GEN res = NULL;1892for (i=1; i<=D; i++) {1893GEN c = gel(x,i);1894if (!gequal0(c)) {1895GEN My = gel(gel(mt,i),j);1896GEN t = RgC_Rg_mul(My,c);1897res = res? RgC_add(res,t): t;1898}1899}1900if (!res) { set_avma(av); return zerocol(D); }1901return gerepileupto(av, res);1902}1903static GEN1904_tablemul_ej_Fp(GEN mt, GEN x, long j, GEN p)1905{1906pari_sp av = avma;1907long D = lg(mt)-1, i;1908GEN res = NULL;1909for (i=1; i<=D; i++) {1910GEN c = gel(x,i);1911if (!gequal0(c)) {1912GEN My = gel(gel(mt,i),j);1913GEN t = FpC_Fp_mul(My,c,p);1914res = res? FpC_add(res,t,p): t;1915}1916}1917if (!res) { set_avma(av); return zerocol(D); }1918return gerepileupto(av, res);1919}19201921static GEN1922_tablemul_ej_Fl(GEN mt, GEN x, long j, ulong p)1923{1924pari_sp av = avma;1925long D = lg(mt)-1, i;1926GEN res = NULL;1927for (i=1; i<=D; i++) {1928ulong c = x[i];1929if (c) {1930GEN My = gel(gel(mt,i),j);1931GEN t = Flv_Fl_mul(My,c, p);1932res = res? Flv_add(res,t, p): t;1933}1934}1935if (!res) { set_avma(av); return zero_Flv(D); }1936return gerepileupto(av, res);1937}19381939static GEN1940algalgmul_csa(GEN al, GEN x, GEN y)1941{1942GEN z, nf = alg_get_center(al);1943long i;1944z = _tablemul(alg_get_relmultable(al), x, y);1945for (i=1; i<lg(z); i++)1946gel(z,i) = basistoalg(nf,gel(z,i));1947return z;1948}19491950/* assumes x and y in algebraic form */1951static GEN1952algalgmul(GEN al, GEN x, GEN y)1953{1954switch(alg_type(al))1955{1956case al_CYCLIC: return algalgmul_cyc(al, x, y);1957case al_CSA: return algalgmul_csa(al, x, y);1958}1959return NULL; /*LCOV_EXCL_LINE*/1960}19611962static GEN1963algbasismul(GEN al, GEN x, GEN y)1964{1965GEN mt = alg_get_multable(al), p = alg_get_char(al);1966if (signe(p)) return _tablemul_Fp(mt, x, y, p);1967return _tablemul(mt, x, y);1968}19691970/* x[i,]*y. Assume lg(x) > 1 and 0 < i < lgcols(x) */1971static GEN1972alMrow_alC_mul_i(GEN al, GEN x, GEN y, long i, long lx)1973{1974pari_sp av = avma;1975GEN c = algmul(al,gcoeff(x,i,1),gel(y,1)), ZERO;1976long k;1977ZERO = zerocol(alg_get_absdim(al));1978for (k = 2; k < lx; k++)1979{1980GEN t = algmul(al, gcoeff(x,i,k), gel(y,k));1981if (!gequal(t,ZERO)) c = algadd(al, c, t);1982}1983return gerepilecopy(av, c);1984}1985/* return x * y, 1 < lx = lg(x), l = lgcols(x) */1986static GEN1987alM_alC_mul_i(GEN al, GEN x, GEN y, long lx, long l)1988{1989GEN z = cgetg(l,t_COL);1990long i;1991for (i=1; i<l; i++) gel(z,i) = alMrow_alC_mul_i(al,x,y,i,lx);1992return z;1993}1994static GEN1995alM_mul(GEN al, GEN x, GEN y)1996{1997long j, l, lx=lg(x), ly=lg(y);1998GEN z;1999if (ly==1) return cgetg(1,t_MAT);2000if (lx != lgcols(y)) pari_err_DIM("alM_mul");2001if (lx==1) return zeromat(0, ly-1);2002l = lgcols(x); z = cgetg(ly,t_MAT);2003for (j=1; j<ly; j++) gel(z,j) = alM_alC_mul_i(al,x,gel(y,j),lx,l);2004return z;2005}20062007GEN2008algmul(GEN al, GEN x, GEN y)2009{2010pari_sp av = avma;2011long tx, ty;2012checkalg(al);2013tx = alg_model(al,x);2014ty = alg_model(al,y);2015if (tx==al_MATRIX) {2016if (ty==al_MATRIX) return alM_mul(al,x,y);2017pari_err_TYPE("algmul", y);2018}2019if (signe(alg_get_char(al))) return algbasismul(al,x,y);2020if (tx==al_TRIVIAL) retmkcol(gmul(gel(x,1),gel(y,1)));2021if (tx==al_ALGEBRAIC && ty==al_ALGEBRAIC) return algalgmul(al,x,y);2022if (tx==al_ALGEBRAIC) x = algalgtobasis(al,x);2023if (ty==al_ALGEBRAIC) y = algalgtobasis(al,y);2024return gerepileupto(av,algbasismul(al,x,y));2025}20262027GEN2028algsqr(GEN al, GEN x)2029{2030pari_sp av = avma;2031long tx;2032checkalg(al);2033tx = alg_model(al,x);2034if (tx==al_MATRIX) return gerepilecopy(av,alM_mul(al,x,x));2035if (signe(alg_get_char(al))) return algbasismul(al,x,x);2036if (tx==al_TRIVIAL) retmkcol(gsqr(gel(x,1)));2037if (tx==al_ALGEBRAIC) return algalgmul(al,x,x);2038return gerepileupto(av,algbasismul(al,x,x));2039}20402041static GEN2042algmtK2Z_cyc(GEN al, GEN m)2043{2044pari_sp av = avma;2045GEN nf = alg_get_abssplitting(al), res, mt, rnf = alg_get_splittingfield(al), c, dc;2046long n = alg_get_degree(al), N = nf_get_degree(nf), Nn, i, j, i1, j1;2047Nn = N*n;2048res = zeromatcopy(Nn,Nn);2049for (i=0; i<n; i++)2050for (j=0; j<n; j++) {2051c = gcoeff(m,i+1,j+1);2052if (!gequal0(c)) {2053c = rnfeltreltoabs(rnf,c);2054c = algtobasis(nf,c);2055c = Q_remove_denom(c,&dc);2056mt = zk_multable(nf,c);2057if (dc) mt = ZM_Z_div(mt,dc);2058for (i1=1; i1<=N; i1++)2059for (j1=1; j1<=N; j1++)2060gcoeff(res,i*N+i1,j*N+j1) = gcoeff(mt,i1,j1);2061}2062}2063return gerepilecopy(av,res);2064}20652066static GEN2067algmtK2Z_csa(GEN al, GEN m)2068{2069pari_sp av = avma;2070GEN nf = alg_get_center(al), res, mt, c, dc;2071long d2 = alg_get_dim(al), n = nf_get_degree(nf), D, i, j, i1, j1;2072D = d2*n;2073res = zeromatcopy(D,D);2074for (i=0; i<d2; i++)2075for (j=0; j<d2; j++) {2076c = gcoeff(m,i+1,j+1);2077if (!gequal0(c)) {2078c = algtobasis(nf,c);2079c = Q_remove_denom(c,&dc);2080mt = zk_multable(nf,c);2081if (dc) mt = ZM_Z_div(mt,dc);2082for (i1=1; i1<=n; i1++)2083for (j1=1; j1<=n; j1++)2084gcoeff(res,i*n+i1,j*n+j1) = gcoeff(mt,i1,j1);2085}2086}2087return gerepilecopy(av,res);2088}20892090/* assumes al is a CSA or CYCLIC */2091static GEN2092algmtK2Z(GEN al, GEN m)2093{2094switch(alg_type(al))2095{2096case al_CYCLIC: return algmtK2Z_cyc(al, m);2097case al_CSA: return algmtK2Z_csa(al, m);2098}2099return NULL; /*LCOV_EXCL_LINE*/2100}21012102/* left multiplication table, as a vector space of dimension n over the splitting field (by right multiplication) */2103static GEN2104algalgmultable_cyc(GEN al, GEN x)2105{2106pari_sp av = avma;2107long n = alg_get_degree(al), i, j;2108GEN res, rnf, auts, b, pol;2109rnf = alg_get_splittingfield(al);2110auts = alg_get_auts(al);2111b = alg_get_b(al);2112pol = rnf_get_pol(rnf);21132114res = zeromatcopy(n,n);2115for (i=0; i<n; i++)2116gcoeff(res,i+1,1) = lift_shallow(rnfbasistoalg(rnf,gel(x,i+1)));21172118for (i=0; i<n; i++) {2119for (j=1; j<=i; j++)2120gcoeff(res,i+1,j+1) = gmodulo(poleval(gcoeff(res,i-j+1,1),gel(auts,j)),pol);2121for (; j<n; j++)2122gcoeff(res,i+1,j+1) = gmodulo(gmul(b,poleval(gcoeff(res,n+i-j+1,1),gel(auts,j))), pol);2123}21242125for (i=0; i<n; i++)2126gcoeff(res,i+1,1) = gmodulo(gcoeff(res,i+1,1),pol);21272128return gerepilecopy(av, res);2129}21302131static GEN2132elementmultable(GEN mt, GEN x)2133{2134pari_sp av = avma;2135long D = lg(mt)-1, i;2136GEN z = NULL;2137for (i=1; i<=D; i++)2138{2139GEN c = gel(x,i);2140if (!gequal0(c))2141{2142GEN M = RgM_Rg_mul(gel(mt,i),c);2143z = z? RgM_add(z, M): M;2144}2145}2146if (!z) { set_avma(av); return zeromatcopy(D,D); }2147return gerepileupto(av, z);2148}2149/* mt a t_VEC of Flm modulo m */2150static GEN2151algbasismultable_Flm(GEN mt, GEN x, ulong m)2152{2153pari_sp av = avma;2154long D = lg(gel(mt,1))-1, i;2155GEN z = NULL;2156for (i=1; i<=D; i++)2157{2158ulong c = x[i];2159if (c)2160{2161GEN M = Flm_Fl_mul(gel(mt,i),c, m);2162z = z? Flm_add(z, M, m): M;2163}2164}2165if (!z) { set_avma(av); return zero_Flm(D,D); }2166return gerepileupto(av, z);2167}2168static GEN2169elementabsmultable_Z(GEN mt, GEN x)2170{2171long i, l = lg(x);2172GEN z = NULL;2173for (i = 1; i < l; i++)2174{2175GEN c = gel(x,i);2176if (signe(c))2177{2178GEN M = ZM_Z_mul(gel(mt,i),c);2179z = z? ZM_add(z, M): M;2180}2181}2182return z;2183}2184static GEN2185elementabsmultable(GEN mt, GEN x)2186{2187GEN d, z = elementabsmultable_Z(mt, Q_remove_denom(x,&d));2188return (z && d)? ZM_Z_div(z, d): z;2189}2190static GEN2191elementabsmultable_Fp(GEN mt, GEN x, GEN p)2192{2193GEN z = elementabsmultable_Z(mt, x);2194return z? FpM_red(z, p): z;2195}2196static GEN2197algbasismultable(GEN al, GEN x)2198{2199pari_sp av = avma;2200GEN z, p = alg_get_char(al), mt = alg_get_multable(al);2201z = signe(p)? elementabsmultable_Fp(mt, x, p): elementabsmultable(mt, x);2202if (!z)2203{2204long D = lg(mt)-1;2205set_avma(av); return zeromat(D,D);2206}2207return gerepileupto(av, z);2208}22092210static GEN2211algalgmultable_csa(GEN al, GEN x)2212{2213GEN nf = alg_get_center(al), m;2214long i,j;2215m = elementmultable(alg_get_relmultable(al), x);2216for (i=1; i<lg(m); i++)2217for(j=1; j<lg(m); j++)2218gcoeff(m,i,j) = basistoalg(nf,gcoeff(m,i,j));2219return m;2220}22212222/* assumes x in algebraic form */2223static GEN2224algalgmultable(GEN al, GEN x)2225{2226switch(alg_type(al))2227{2228case al_CYCLIC: return algalgmultable_cyc(al, x);2229case al_CSA: return algalgmultable_csa(al, x);2230}2231return NULL; /*LCOV_EXCL_LINE*/2232}22332234/* on the natural basis */2235/* assumes x in algebraic form */2236static GEN2237algZmultable(GEN al, GEN x) {2238pari_sp av = avma;2239GEN res = NULL, x0;2240long tx = alg_model(al,x);2241switch(tx) {2242case al_TRIVIAL:2243x0 = gel(x,1);2244if (typ(x0)==t_POLMOD) x0 = gel(x0,2);2245if (typ(x0)==t_POL) x0 = constant_coeff(x0);2246res = mkmatcopy(mkcol(x0));2247break;2248case al_ALGEBRAIC: res = algmtK2Z(al,algalgmultable(al,x)); break;2249}2250return gerepileupto(av,res);2251}22522253/* x integral */2254static GEN2255algbasisrightmultable(GEN al, GEN x)2256{2257long N = alg_get_absdim(al), i,j,k;2258GEN res = zeromatcopy(N,N), c, mt = alg_get_multable(al), p = alg_get_char(al);2259if (gequal0(p)) p = NULL;2260for (i=1; i<=N; i++) {2261c = gel(x,i);2262if (!gequal0(c)) {2263for (j=1; j<=N; j++)2264for(k=1; k<=N; k++) {2265if (p) gcoeff(res,k,j) = Fp_add(gcoeff(res,k,j), Fp_mul(c, gcoeff(gel(mt,j),k,i), p), p);2266else gcoeff(res,k,j) = addii(gcoeff(res,k,j), mulii(c, gcoeff(gel(mt,j),k,i)));2267}2268}2269}2270return res;2271}22722273/* basis for matrices : 1, E_{i,j} for (i,j)!=(1,1) */2274/* index : ijk = ((i-1)*N+j-1)*n + k */2275/* square matrices only, coefficients in basis form, shallow function */2276static GEN2277algmat2basis(GEN al, GEN M)2278{2279long n = alg_get_absdim(al), N = lg(M)-1, i, j, k, ij, ijk;2280GEN res, x;2281res = zerocol(N*N*n);2282for (i=1; i<=N; i++) {2283for (j=1, ij=(i-1)*N+1; j<=N; j++, ij++) {2284x = gcoeff(M,i,j);2285for (k=1, ijk=(ij-1)*n+1; k<=n; k++, ijk++) {2286gel(res, ijk) = gel(x, k);2287if (i>1 && i==j) gel(res, ijk) = gsub(gel(res,ijk), gel(res,k));2288}2289}2290}22912292return res;2293}22942295static GEN2296algbasis2mat(GEN al, GEN M, long N)2297{2298long n = alg_get_absdim(al), i, j, k, ij, ijk;2299GEN res, x;2300res = zeromatcopy(N,N);2301for (i=1; i<=N; i++)2302for (j=1; j<=N; j++)2303gcoeff(res,i,j) = zerocol(n);23042305for (i=1; i<=N; i++) {2306for (j=1, ij=(i-1)*N+1; j<=N; j++, ij++) {2307x = gcoeff(res,i,j);2308for (k=1, ijk=(ij-1)*n+1; k<=n; k++, ijk++) {2309gel(x,k) = gel(M,ijk);2310if (i>1 && i==j) gel(x,k) = gadd(gel(x,k), gel(M,k));2311}2312}2313}23142315return res;2316}23172318static GEN2319algmatbasis_ei(GEN al, long ijk, long N)2320{2321long n = alg_get_absdim(al), i, j, k, ij;2322GEN res;23232324res = zeromatcopy(N,N);2325for (i=1; i<=N; i++)2326for (j=1; j<=N; j++)2327gcoeff(res,i,j) = zerocol(n);23282329k = ijk%n;2330if (k==0) k=n;2331ij = (ijk-k)/n+1;23322333if (ij==1) {2334for (i=1; i<=N; i++)2335gcoeff(res,i,i) = col_ei(n,k);2336return res;2337}23382339j = ij%N;2340if (j==0) j=N;2341i = (ij-j)/N+1;23422343gcoeff(res,i,j) = col_ei(n,k);2344return res;2345}23462347/* FIXME lazy implementation! */2348static GEN2349algleftmultable_mat(GEN al, GEN M)2350{2351long N = lg(M)-1, n = alg_get_absdim(al), D = N*N*n, j;2352GEN res, x, Mx;2353if (N == 0) return cgetg(1, t_MAT);2354if (N != nbrows(M)) pari_err_DIM("algleftmultable_mat (nonsquare)");2355res = cgetg(D+1, t_MAT);2356for (j=1; j<=D; j++) {2357x = algmatbasis_ei(al, j, N);2358Mx = algmul(al, M, x);2359gel(res, j) = algmat2basis(al, Mx);2360}2361return res;2362}23632364/* left multiplication table on integral basis */2365static GEN2366algleftmultable(GEN al, GEN x)2367{2368pari_sp av = avma;2369long tx;2370GEN res;23712372checkalg(al);2373tx = alg_model(al,x);2374switch(tx) {2375case al_TRIVIAL : res = mkmatcopy(mkcol(gel(x,1))); break;2376case al_ALGEBRAIC : x = algalgtobasis(al,x);2377case al_BASIS : res = algbasismultable(al,x); break;2378case al_MATRIX : res = algleftmultable_mat(al,x); break;2379default : return NULL; /* LCOV_EXCL_LINE */2380}2381return gerepileupto(av,res);2382}23832384static GEN2385algbasissplittingmatrix_csa(GEN al, GEN x)2386{2387long d = alg_get_degree(al), i, j;2388GEN rnf = alg_get_splittingfield(al), splba = alg_get_splittingbasis(al), splbainv = alg_get_splittingbasisinv(al), M;2389M = algbasismultable(al,x);2390M = RgM_mul(M, splba); /* TODO best order ? big matrix /Q vs small matrix /nf */2391M = RgM_mul(splbainv, M);2392for (i=1; i<=d; i++)2393for (j=1; j<=d; j++)2394gcoeff(M,i,j) = rnfeltabstorel(rnf, gcoeff(M,i,j));2395return M;2396}23972398GEN2399algtomatrix(GEN al, GEN x, long abs)2400{2401pari_sp av = avma;2402GEN res = NULL;2403long ta, tx, i, j;2404checkalg(al);2405ta = alg_type(al);2406if (abs || ta==al_TABLE) return algleftmultable(al,x);2407tx = alg_model(al,x);2408if (tx==al_MATRIX) {2409if (lg(x) == 1) return cgetg(1, t_MAT);2410res = zeromatcopy(nbrows(x),lg(x)-1);2411for (j=1; j<lg(x); j++)2412for (i=1; i<lgcols(x); i++)2413gcoeff(res,i,j) = algtomatrix(al,gcoeff(x,i,j),0);2414res = shallowmatconcat(res);2415}2416else switch(alg_type(al))2417{2418case al_CYCLIC:2419if (tx==al_BASIS) x = algbasistoalg(al,x);2420res = algalgmultable(al,x);2421break;2422case al_CSA:2423if (tx==al_ALGEBRAIC) x = algalgtobasis(al,x);2424res = algbasissplittingmatrix_csa(al,x);2425break;2426default:2427pari_err_DOMAIN("algtomatrix", "alg_type(al)", "=", stoi(alg_type(al)), stoi(alg_type(al)));2428}2429return gerepilecopy(av,res);2430}24312432/* x^(-1)*y, NULL if no solution */2433static GEN2434algdivl_i(GEN al, GEN x, GEN y, long tx, long ty) {2435pari_sp av = avma;2436GEN res, p = alg_get_char(al), mtx;2437if (tx != ty) {2438if (tx==al_ALGEBRAIC) { x = algalgtobasis(al,x); tx=al_BASIS; }2439if (ty==al_ALGEBRAIC) { y = algalgtobasis(al,y); ty=al_BASIS; }2440}2441if (ty == al_MATRIX)2442{2443if (alg_type(al) != al_TABLE) y = algalgtobasis(al,y);2444y = algmat2basis(al,y);2445}2446if (signe(p)) res = FpM_FpC_invimage(algbasismultable(al,x),y,p);2447else2448{2449if (ty==al_ALGEBRAIC) mtx = algalgmultable(al,x);2450else mtx = algleftmultable(al,x);2451res = inverseimage(mtx,y);2452}2453if (!res || lg(res)==1) return gc_NULL(av);2454if (tx == al_MATRIX) {2455res = algbasis2mat(al, res, lg(x)-1);2456return gerepilecopy(av,res);2457}2458return gerepileupto(av,res);2459}2460static GEN2461algdivl_i2(GEN al, GEN x, GEN y)2462{2463long tx, ty;2464checkalg(al);2465tx = alg_model(al,x);2466ty = alg_model(al,y);2467if (tx == al_MATRIX) {2468if (ty != al_MATRIX) pari_err_TYPE2("\\", x, y);2469if (lg(y) == 1) return cgetg(1, t_MAT);2470if (lg(x) == 1) return NULL;2471if (lgcols(x) != lgcols(y)) pari_err_DIM("algdivl");2472if (lg(x) != lgcols(x) || lg(y) != lgcols(y))2473pari_err_DIM("algdivl (nonsquare)");2474}2475return algdivl_i(al,x,y,tx,ty);2476}24772478GEN algdivl(GEN al, GEN x, GEN y)2479{2480GEN z;2481z = algdivl_i2(al,x,y);2482if (!z) pari_err_INV("algdivl", x);2483return z;2484}24852486int2487algisdivl(GEN al, GEN x, GEN y, GEN* ptz)2488{2489pari_sp av = avma;2490GEN z = algdivl_i2(al,x,y);2491if (!z) return gc_bool(av,0);2492if (ptz != NULL) *ptz = z;2493return 1;2494}24952496static GEN2497alginv_i(GEN al, GEN x)2498{2499pari_sp av = avma;2500GEN res = NULL, p = alg_get_char(al);2501long tx = alg_model(al,x), n;2502switch(tx) {2503case al_TRIVIAL :2504if (signe(p)) { res = mkcol(Fp_inv(gel(x,1),p)); break; }2505else { res = mkcol(ginv(gel(x,1))); break; }2506case al_ALGEBRAIC :2507switch(alg_type(al)) {2508case al_CYCLIC: n = alg_get_degree(al); break;2509case al_CSA: n = alg_get_dim(al); break;2510default: return NULL; /* LCOV_EXCL_LINE */2511}2512res = algdivl_i(al, x, col_ei(n,1), tx, al_ALGEBRAIC); break;2513case al_BASIS : res = algdivl_i(al, x, col_ei(alg_get_absdim(al),1), tx,2514al_BASIS); break;2515case al_MATRIX :2516n = lg(x)-1;2517if (n==0) return cgetg(1, t_MAT);2518if (n != nbrows(x)) pari_err_DIM("alginv_i (nonsquare)");2519res = algdivl_i(al, x, col_ei(n*n*alg_get_absdim(al),1), tx, al_BASIS);2520/* cheat on type because wrong dimension */2521}2522if (!res) return gc_NULL(av);2523return gerepilecopy(av,res);2524}2525GEN2526alginv(GEN al, GEN x)2527{2528GEN z;2529checkalg(al);2530z = alginv_i(al,x);2531if (!z) pari_err_INV("alginv", x);2532return z;2533}25342535int2536algisinv(GEN al, GEN x, GEN* ptix)2537{2538pari_sp av = avma;2539GEN ix;2540checkalg(al);2541ix = alginv_i(al,x);2542if (!ix) return gc_bool(av,0);2543if (ptix != NULL) *ptix = ix;2544return 1;2545}25462547/* x*y^(-1) */2548GEN2549algdivr(GEN al, GEN x, GEN y) { return algmul(al, x, alginv(al, y)); }25502551static GEN _mul(void* data, GEN x, GEN y) { return algmul((GEN)data,x,y); }2552static GEN _sqr(void* data, GEN x) { return algsqr((GEN)data,x); }25532554static GEN2555algmatid(GEN al, long N)2556{2557long n = alg_get_absdim(al), i, j;2558GEN res, one, zero;25592560res = zeromatcopy(N,N);2561one = col_ei(n,1);2562zero = zerocol(n);2563for (i=1; i<=N; i++)2564for (j=1; j<=N; j++)2565gcoeff(res,i,j) = i==j ? one : zero;2566return res;2567}25682569GEN2570algpow(GEN al, GEN x, GEN n)2571{2572pari_sp av = avma;2573GEN res;2574checkalg(al);2575switch(signe(n)) {2576case 0:2577if (alg_model(al,x) == al_MATRIX)2578res = algmatid(al,lg(x)-1);2579else2580res = col_ei(alg_get_absdim(al),1);2581return res;2582case 1:2583res = gen_pow_i(x, n, (void*)al, _sqr, _mul); break;2584default: /* -1 */2585res = gen_pow_i(alginv(al,x), gneg(n), (void*)al, _sqr, _mul);2586}2587return gerepilecopy(av,res);2588}25892590static GEN2591algredcharpoly_i(GEN al, GEN x, long v)2592{2593GEN rnf = alg_get_splittingfield(al);2594GEN cp = charpoly(algtomatrix(al,x,0),v);2595long i, m = lg(cp);2596for (i=2; i<m; i++) gel(cp,i) = rnfeltdown(rnf, gel(cp,i));2597return cp;2598}25992600/* assumes al is CSA or CYCLIC */2601static GEN2602algredcharpoly(GEN al, GEN x, long v)2603{2604pari_sp av = avma;2605long w = gvar(rnf_get_pol(alg_get_center(al)));2606if (varncmp(v,w)>=0) pari_err_PRIORITY("algredcharpoly",pol_x(v),">=",w);2607switch(alg_type(al))2608{2609case al_CYCLIC:2610case al_CSA:2611return gerepileupto(av, algredcharpoly_i(al, x, v));2612}2613return NULL; /*LCOV_EXCL_LINE*/2614}26152616static GEN2617algbasischarpoly(GEN al, GEN x, long v)2618{2619pari_sp av = avma;2620GEN p = alg_get_char(al), mx;2621if (alg_model(al,x) == al_MATRIX) mx = algleftmultable_mat(al,x);2622else mx = algbasismultable(al,x);2623if (signe(p)) {2624GEN res = FpM_charpoly(mx,p);2625setvarn(res,v);2626return gerepileupto(av, res);2627}2628return gerepileupto(av, charpoly(mx,v));2629}26302631GEN2632algcharpoly(GEN al, GEN x, long v, long abs)2633{2634checkalg(al);2635if (v<0) v=0;26362637/* gneg(x[1]) left on stack */2638if (alg_model(al,x) == al_TRIVIAL) {2639GEN p = alg_get_char(al);2640if (signe(p)) return deg1pol(gen_1,Fp_neg(gel(x,1),p),v);2641return deg1pol(gen_1,gneg(gel(x,1)),v);2642}26432644switch(alg_type(al)) {2645case al_CYCLIC: case al_CSA:2646if (abs)2647{2648if (alg_model(al,x)==al_ALGEBRAIC) x = algalgtobasis(al,x);2649}2650else return algredcharpoly(al,x,v);2651case al_TABLE: return algbasischarpoly(al,x,v);2652default : return NULL; /* LCOV_EXCL_LINE */2653}2654}26552656/* assumes x in basis form */2657static GEN2658algabstrace(GEN al, GEN x)2659{2660pari_sp av = avma;2661GEN res = NULL, p = alg_get_char(al);2662if (signe(p)) return FpV_dotproduct(x, alg_get_tracebasis(al), p);2663switch(alg_model(al,x)) {2664case al_TRIVIAL: return gcopy(gel(x,1)); break;2665case al_BASIS: res = RgV_dotproduct(x, alg_get_tracebasis(al)); break;2666}2667return gerepileupto(av,res);2668}26692670static GEN2671algredtrace(GEN al, GEN x)2672{2673pari_sp av = avma;2674GEN res = NULL;2675switch(alg_model(al,x)) {2676case al_TRIVIAL: return gcopy(gel(x,1)); break;2677case al_BASIS: return algredtrace(al, algbasistoalg(al,x));2678/* TODO precompute too? */2679case al_ALGEBRAIC:2680switch(alg_type(al))2681{2682case al_CYCLIC:2683res = rnfelttrace(alg_get_splittingfield(al),gel(x,1));2684break;2685case al_CSA:2686res = gtrace(algalgmultable_csa(al,x));2687res = gdiv(res, stoi(alg_get_degree(al)));2688break;2689default: return NULL; /* LCOV_EXCL_LINE */2690}2691}2692return gerepileupto(av,res);2693}26942695static GEN2696algtrace_mat(GEN al, GEN M, long abs) {2697pari_sp av = avma;2698long N = lg(M)-1, i;2699GEN res, p = alg_get_char(al);2700if (N == 0) return gen_0;2701if (N != nbrows(M)) pari_err_DIM("algtrace_mat (nonsquare)");27022703if (!signe(p)) p = NULL;2704res = algtrace(al, gcoeff(M,1,1), abs);2705for (i=2; i<=N; i++) {2706if (p) res = Fp_add(res, algtrace(al,gcoeff(M,i,i),abs), p);2707else res = gadd(res, algtrace(al,gcoeff(M,i,i),abs));2708}2709if (abs || alg_type(al) == al_TABLE) res = gmulgs(res, N); /* absolute trace */2710return gerepileupto(av, res);2711}27122713GEN2714algtrace(GEN al, GEN x, long abs)2715{2716checkalg(al);2717if (alg_model(al,x) == al_MATRIX) return algtrace_mat(al,x,abs);2718switch(alg_type(al)) {2719case al_CYCLIC: case al_CSA:2720if (!abs) return algredtrace(al,x);2721if (alg_model(al,x)==al_ALGEBRAIC) x = algalgtobasis(al,x);2722case al_TABLE: return algabstrace(al,x);2723default : return NULL; /* LCOV_EXCL_LINE */2724}2725}27262727static GEN2728ZM_trace(GEN x)2729{2730long i, lx = lg(x);2731GEN t;2732if (lx < 3) return lx == 1? gen_0: gcopy(gcoeff(x,1,1));2733t = gcoeff(x,1,1);2734for (i = 2; i < lx; i++) t = addii(t, gcoeff(x,i,i));2735return t;2736}2737static GEN2738FpM_trace(GEN x, GEN p)2739{2740long i, lx = lg(x);2741GEN t;2742if (lx < 3) return lx == 1? gen_0: gcopy(gcoeff(x,1,1));2743t = gcoeff(x,1,1);2744for (i = 2; i < lx; i++) t = Fp_add(t, gcoeff(x,i,i), p);2745return t;2746}27472748static GEN2749algtracebasis(GEN al)2750{2751pari_sp av = avma;2752GEN mt = alg_get_multable(al), p = alg_get_char(al);2753long i, l = lg(mt);2754GEN v = cgetg(l, t_VEC);2755if (signe(p)) for (i=1; i < l; i++) gel(v,i) = FpM_trace(gel(mt,i), p);2756else for (i=1; i < l; i++) gel(v,i) = ZM_trace(gel(mt,i));2757return gerepileupto(av,v);2758}27592760/* Assume: i > 0, expo := p^i <= absdim, x contained in I_{i-1} given by mult2761* table modulo modu=p^(i+1). Return Tr(x^(p^i)) mod modu */2762static ulong2763algtracei(GEN mt, ulong p, ulong expo, ulong modu)2764{2765pari_sp av = avma;2766long j, l = lg(mt);2767ulong tr = 0;2768mt = Flm_powu(mt,expo,modu);2769for (j=1; j<l; j++) tr += ucoeff(mt,j,j);2770return gc_ulong(av, (tr/expo) % p);2771}27722773GEN2774algnorm(GEN al, GEN x, long abs)2775{2776pari_sp av = avma;2777long tx;2778GEN p, rnf, res, mx;2779checkalg(al);2780p = alg_get_char(al);2781tx = alg_model(al,x);2782if (signe(p)) {2783if (tx == al_MATRIX) mx = algleftmultable_mat(al,x);2784else mx = algbasismultable(al,x);2785return gerepileupto(av, FpM_det(mx,p));2786}2787if (tx == al_TRIVIAL) return gcopy(gel(x,1));27882789switch(alg_type(al)) {2790case al_CYCLIC: case al_CSA:2791if (abs)2792{2793if (alg_model(al,x)==al_ALGEBRAIC) x = algalgtobasis(al,x);2794}2795else2796{2797rnf = alg_get_splittingfield(al);2798res = rnfeltdown(rnf, det(algtomatrix(al,x,0)));2799break;2800}2801case al_TABLE:2802if (tx == al_MATRIX) mx = algleftmultable_mat(al,x);2803else mx = algbasismultable(al,x);2804res = det(mx);2805break;2806default: return NULL; /* LCOV_EXCL_LINE */2807}2808return gerepileupto(av, res);2809}28102811static GEN2812algalgtonat_cyc(GEN al, GEN x)2813{2814pari_sp av = avma;2815GEN nf = alg_get_abssplitting(al), rnf = alg_get_splittingfield(al), res, c;2816long n = alg_get_degree(al), N = nf_get_degree(nf), i, i1;2817res = zerocol(N*n);2818for (i=0; i<n; i++) {2819c = gel(x,i+1);2820c = rnfeltreltoabs(rnf,c);2821if (!gequal0(c)) {2822c = algtobasis(nf,c);2823for (i1=1; i1<=N; i1++) gel(res,i*N+i1) = gel(c,i1);2824}2825}2826return gerepilecopy(av, res);2827}28282829static GEN2830algalgtonat_csa(GEN al, GEN x)2831{2832pari_sp av = avma;2833GEN nf = alg_get_center(al), res, c;2834long d2 = alg_get_dim(al), n = nf_get_degree(nf), i, i1;2835res = zerocol(d2*n);2836for (i=0; i<d2; i++) {2837c = gel(x,i+1);2838if (!gequal0(c)) {2839c = algtobasis(nf,c);2840for (i1=1; i1<=n; i1++) gel(res,i*n+i1) = gel(c,i1);2841}2842}2843return gerepilecopy(av, res);2844}28452846/* assumes al CSA or CYCLIC */2847static GEN2848algalgtonat(GEN al, GEN x)2849{2850switch(alg_type(al))2851{2852case al_CYCLIC: return algalgtonat_cyc(al, x);2853case al_CSA: return algalgtonat_csa(al, x);2854}2855return NULL; /*LCOV_EXCL_LINE*/2856}28572858static GEN2859algnattoalg_cyc(GEN al, GEN x)2860{2861pari_sp av = avma;2862GEN nf = alg_get_abssplitting(al), rnf = alg_get_splittingfield(al), res, c;2863long n = alg_get_degree(al), N = nf_get_degree(nf), i, i1;2864res = zerocol(n);2865c = zerocol(N);2866for (i=0; i<n; i++) {2867for (i1=1; i1<=N; i1++) gel(c,i1) = gel(x,i*N+i1);2868gel(res,i+1) = rnfeltabstorel(rnf,basistoalg(nf,c));2869}2870return gerepilecopy(av, res);2871}28722873static GEN2874algnattoalg_csa(GEN al, GEN x)2875{2876pari_sp av = avma;2877GEN nf = alg_get_center(al), res, c;2878long d2 = alg_get_dim(al), n = nf_get_degree(nf), i, i1;2879res = zerocol(d2);2880c = zerocol(n);2881for (i=0; i<d2; i++) {2882for (i1=1; i1<=n; i1++) gel(c,i1) = gel(x,i*n+i1);2883gel(res,i+1) = basistoalg(nf,c);2884}2885return gerepilecopy(av, res);2886}28872888/* assumes al CSA or CYCLIC */2889static GEN2890algnattoalg(GEN al, GEN x)2891{2892switch(alg_type(al))2893{2894case al_CYCLIC: return algnattoalg_cyc(al, x);2895case al_CSA: return algnattoalg_csa(al, x);2896}2897return NULL; /*LCOV_EXCL_LINE*/2898}28992900static GEN2901algalgtobasis_mat(GEN al, GEN x) /* componentwise */2902{2903pari_sp av = avma;2904long lx, lxj, i, j;2905GEN res;2906lx = lg(x);2907res = cgetg(lx, t_MAT);2908for (j=1; j<lx; j++) {2909lxj = lg(gel(x,j));2910gel(res,j) = cgetg(lxj, t_COL);2911for (i=1; i<lxj; i++)2912gcoeff(res,i,j) = algalgtobasis(al,gcoeff(x,i,j));2913}2914return gerepilecopy(av,res);2915}2916GEN2917algalgtobasis(GEN al, GEN x)2918{2919pari_sp av;2920long tx;2921checkalg(al);2922if (alg_type(al) == al_TABLE) pari_err_TYPE("algalgtobasis [use alginit]", al);2923tx = alg_model(al,x);2924if (tx==al_BASIS) return gcopy(x);2925if (tx==al_MATRIX) return algalgtobasis_mat(al,x);2926av = avma;2927x = algalgtonat(al,x);2928x = RgM_RgC_mul(alg_get_invbasis(al),x);2929return gerepileupto(av, x);2930}29312932static GEN2933algbasistoalg_mat(GEN al, GEN x) /* componentwise */2934{2935long j, lx = lg(x);2936GEN res = cgetg(lx, t_MAT);2937for (j=1; j<lx; j++) {2938long i, lxj = lg(gel(x,j));2939gel(res,j) = cgetg(lxj, t_COL);2940for (i=1; i<lxj; i++) gcoeff(res,i,j) = algbasistoalg(al,gcoeff(x,i,j));2941}2942return res;2943}2944GEN2945algbasistoalg(GEN al, GEN x)2946{2947pari_sp av;2948long tx;2949checkalg(al);2950if (alg_type(al) == al_TABLE) pari_err_TYPE("algbasistoalg [use alginit]", al);2951tx = alg_model(al,x);2952if (tx==al_ALGEBRAIC) return gcopy(x);2953if (tx==al_MATRIX) return algbasistoalg_mat(al,x);2954av = avma;2955x = RgM_RgC_mul(alg_get_basis(al),x);2956x = algnattoalg(al,x);2957return gerepileupto(av, x);2958}29592960GEN2961algrandom(GEN al, GEN b)2962{2963GEN res, p, N;2964long i, n;2965if (typ(b) != t_INT) pari_err_TYPE("algrandom",b);2966if (signe(b)<0) pari_err_DOMAIN("algrandom", "b", "<", gen_0, b);2967checkalg(al);2968n = alg_get_absdim(al);2969N = addiu(shifti(b,1), 1); /* left on stack */2970p = alg_get_char(al); if (!signe(p)) p = NULL;2971res = cgetg(n+1,t_COL);2972for (i=1; i<= n; i++)2973{2974pari_sp av = avma;2975GEN t = subii(randomi(N),b);2976if (p) t = modii(t, p);2977gel(res,i) = gerepileuptoint(av, t);2978}2979return res;2980}29812982/* Assumes pol has coefficients in the same ring as the COL x; x either2983* in basis or algebraic form or [x,mx] where mx is the mult. table of x.2984TODO more general version: pol with coeffs in center and x in basis form */2985GEN2986algpoleval(GEN al, GEN pol, GEN x)2987{2988pari_sp av = avma;2989GEN p, mx = NULL, res;2990long i;2991checkalg(al);2992p = alg_get_char(al);2993if (typ(pol) != t_POL) pari_err_TYPE("algpoleval", pol);2994if (typ(x) == t_VEC)2995{2996if (lg(x) != 3) pari_err_TYPE("algpoleval [vector must be of length 2]", x);2997mx = gel(x,2);2998x = gel(x,1);2999if (typ(mx)!=t_MAT || !gequal(x,gel(mx,1)))3000pari_err_TYPE("algpoleval [mx must be the multiplication table of x]", mx);3001}3002else3003{3004switch(alg_model(al,x))3005{3006case al_ALGEBRAIC: mx = algalgmultable(al,x); break;3007case al_BASIS: if (!RgX_is_QX(pol))3008pari_err_IMPL("algpoleval with x in basis form and pol not in Q[x]");3009case al_TRIVIAL: mx = algbasismultable(al,x); break;3010default: pari_err_TYPE("algpoleval", x);3011}3012}3013res = zerocol(lg(mx)-1);3014if (signe(p)) {3015for (i=lg(pol)-1; i>1; i--)3016{3017gel(res,1) = Fp_add(gel(res,1), gel(pol,i), p);3018if (i>2) res = FpM_FpC_mul(mx, res, p);3019}3020}3021else {3022for (i=lg(pol)-1; i>1; i--)3023{3024gel(res,1) = gadd(gel(res,1), gel(pol,i));3025if (i>2) res = RgM_RgC_mul(mx, res);3026}3027}3028return gerepileupto(av, res);3029}30303031/** GRUNWALD-WANG **/3032/*3033Song Wang's PhD thesis (pdf pages)3034p.25 definition of chi_b. K^Ker(chi_b) = K(b^(1/m))3035p.26 bound on the conductor (also Cohen adv. GTM 193 p.166)3036p.21 & p.34 description special case, also on wikipedia:3037http://en.wikipedia.org/wiki/Grunwald%E2%80%93Wang_theorem#Special_fields3038p.77 Kummer case3039*/30403041/* n > 0. Is n = 2^k ? */3042static int3043uispow2(ulong n) { return !(n &(n-1)); }30443045static GEN3046get_phi0(GEN bnr, GEN Lpr, GEN Ld, GEN pl, long *pr, long *pn)3047{3048const long NTRY = 10; /* FIXME: magic constant */3049const long n = (lg(Ld)==1)? 2: vecsmall_max(Ld);3050GEN S = bnr_get_cyc(bnr);3051GEN Sst, G, globGmod, loc, X, Rglob, Rloc, H, U, Lconj;3052long i, j, r, nbfrob, nbloc, nz, t;30533054*pn = n;3055*pr = r = lg(S)-1;3056if (!r) return NULL;3057Lconj = NULL;3058nbloc = nbfrob = lg(Lpr)-1;3059if (uispow2(n))3060{3061long l = lg(pl), k = 1;3062GEN real = cgetg(l, t_VECSMALL);3063for (i=1; i<l; i++)3064if (pl[i]==-1) real[k++] = i;3065if (k > 1)3066{3067GEN nf = bnr_get_nf(bnr), I = bid_get_fact(bnr_get_bid(bnr));3068GEN v, y, C = idealchineseinit(bnr, I);3069long r1 = nf_get_r1(nf), n = nbrows(I);3070nbloc += k-1;3071Lconj = cgetg(k, t_VEC);3072v = const_vecsmall(r1,1);3073y = const_vec(n, gen_1);3074for (i = 1; i < k; i++)3075{3076v[i] = -1; gel(Lconj,i) = idealchinese(nf,mkvec2(C,v),y);3077v[i] = 1;3078}3079}3080}30813082/* compute Z/n-dual */3083Sst = cgetg(r+1, t_VECSMALL);3084for (i=1; i<=r; i++) Sst[i] = ugcdiu(gel(S,i), n);3085if (Sst[1] != n) return NULL;30863087globGmod = cgetg(r+1,t_MAT);3088G = cgetg(r+1,t_VECSMALL);3089for (i=1; i<=r; i++)3090{3091G[i] = n / Sst[i]; /* pairing between S and Sst */3092gel(globGmod,i) = cgetg(nbloc+1,t_VECSMALL);3093}30943095/* compute images of Frobenius elements (and complex conjugation) */3096loc = cgetg(nbloc+1,t_VECSMALL);3097for (i=1; i<=nbloc; i++) {3098long L;3099if (i<=nbfrob)3100{3101X = gel(Lpr,i);3102L = Ld[i];3103}3104else3105{ /* X = 1 (mod f), sigma_i(x) < 0, positive at all other real places */3106X = gel(Lconj,i-nbfrob);3107L = 2;3108}3109X = ZV_to_Flv(isprincipalray(bnr,X), n);3110for (nz=0,j=1; j<=r; j++)3111{3112ulong c = (X[j] * G[j]) % L;3113ucoeff(globGmod,i,j) = c;3114if (c) nz = 1;3115}3116if (!nz) return NULL;3117loc[i] = L;3118}31193120/* try some random elements in the dual */3121Rglob = cgetg(r+1,t_VECSMALL);3122for (t=0; t<NTRY; t++) {3123for (j=1; j<=r; j++) Rglob[j] = random_Fl(Sst[j]);3124Rloc = zm_zc_mul(globGmod,Rglob);3125for (i=1; i<=nbloc; i++)3126if (Rloc[i] % loc[i] == 0) break;3127if (i > nbloc)3128return zv_to_ZV(Rglob);3129}31303131/* try to realize some random elements of the product of the local duals */3132H = ZM_hnfall_i(shallowconcat(zm_to_ZM(globGmod),3133diagonal_shallow(zv_to_ZV(loc))), &U, 2);3134/* H,U nbloc x nbloc */3135Rloc = cgetg(nbloc+1,t_COL);3136for (t=0; t<NTRY; t++) {3137/* nonzero random coordinate */ /* TODO add special case ? */3138for (i=1; i<=nbloc; i++) gel(Rloc,i) = stoi(1 + random_Fl(loc[i]-1));3139Rglob = hnf_invimage(H, Rloc);3140if (Rglob)3141{3142Rglob = ZM_ZC_mul(U,Rglob);3143return vecslice(Rglob,1,r);3144}3145}3146return NULL;3147}31483149static GEN3150bnrgwsearch(GEN bnr, GEN Lpr, GEN Ld, GEN pl)3151{3152pari_sp av = avma;3153long n, r;3154GEN phi0 = get_phi0(bnr,Lpr,Ld,pl, &r,&n), gn, v, H,U;3155if (!phi0) return gc_const(av, gen_0);3156gn = stoi(n);3157/* compute kernel of phi0 */3158v = ZV_extgcd(vec_append(phi0, gn));3159U = vecslice(gel(v,2), 1,r);3160H = ZM_hnfmodid(rowslice(U, 1,r), gn);3161return gerepileupto(av, H);3162}31633164GEN3165bnfgwgeneric(GEN bnf, GEN Lpr, GEN Ld, GEN pl, long var)3166{3167pari_sp av = avma;3168const long n = (lg(Ld)==1)? 2: vecsmall_max(Ld);3169forprime_t S;3170GEN bnr = NULL, ideal = gen_1, nf, dec, H = gen_0, finf, pol;3171ulong ell, p;3172long deg, i, degell;3173(void)uisprimepower(n, &ell);3174nf = bnf_get_nf(bnf);3175deg = nf_get_degree(nf);3176degell = ugcd(deg,ell-1);3177finf = cgetg(lg(pl),t_VEC);3178for (i=1; i<lg(pl); i++) gel(finf,i) = pl[i]==-1 ? gen_1 : gen_0;31793180u_forprime_init(&S, 2, ULONG_MAX);3181while ((p = u_forprime_next(&S))) {3182if (Fl_powu(p % ell, degell, ell) != 1) continue; /* ell | p^deg-1 ? */3183dec = idealprimedec(nf, utoipos(p));3184for (i=1; i<lg(dec); i++) {3185GEN pp = gel(dec,i);3186if (RgV_isin(Lpr,pp)) continue;3187/* TODO also accept the prime ideals at which there is a condition3188* (use local Artin)? */3189if (smodis(idealnorm(nf,pp),ell) != 1) continue; /* ell | N(pp)-1 ? */3190ideal = idealmul(bnf,ideal,pp);3191/* TODO: give factorization ? */3192bnr = Buchray(bnf, mkvec2(ideal,finf), nf_INIT);3193H = bnrgwsearch(bnr,Lpr,Ld,pl);3194if (H != gen_0)3195{3196pol = rnfkummer(bnr,H,nf_get_prec(nf));3197setvarn(pol, var);3198return gerepileupto(av,pol);3199}3200}3201}3202pari_err_BUG("bnfgwgeneric (no suitable p)"); /*LCOV_EXCL_LINE*/3203return NULL;/*LCOV_EXCL_LINE*/3204}32053206/* no garbage collection */3207static GEN3208localextdeg(GEN nf, GEN pr, GEN cnd, long d, long ell, long n)3209{3210long g = n/d;3211GEN res, modpr, ppr = pr, T, p, gen, k;3212if (d==1) return gen_1;3213if (equalsi(ell,pr_get_p(pr))) { /* ell == p */3214res = nfadd(nf, gen_1, pr_get_gen(pr));3215res = nfpowmodideal(nf, res, stoi(g), cnd);3216}3217else { /* ell != p */3218k = powis(stoi(ell),Z_lval(subiu(pr_norm(pr),1),ell));3219k = divis(k,g);3220modpr = nf_to_Fq_init(nf, &ppr, &T, &p);3221(void)Fq_sqrtn(gen_1,k,T,p,&gen);3222res = Fq_to_nf(gen, modpr);3223}3224return res;3225}32263227/* Ld[i] must be nontrivial powers of the same prime ell */3228/* pl : -1 at real places at which the extention must ramify, 0 elsewhere */3229GEN3230nfgwkummer(GEN nf, GEN Lpr, GEN Ld, GEN pl, long var)3231{3232const long n = (lg(Ld)==1)? 2: vecsmall_max(Ld);3233pari_sp av = avma;3234ulong ell;3235long i, v;3236GEN cnd, y, x, pol;3237v = uisprimepower(n, &ell);3238cnd = zeromatcopy(lg(Lpr)-1,2);32393240y = vec_ei(lg(Lpr)-1,1);3241for (i=1; i<lg(Lpr); i++) {3242GEN pr = gel(Lpr,i), p = pr_get_p(pr), E;3243long e = pr_get_e(pr);3244gcoeff(cnd,i,1) = pr;32453246if (!absequalui(ell,p))3247E = gen_1;3248else3249E = addui(1 + v*e, divsi(e,subiu(p,1)));3250gcoeff(cnd,i,2) = E;3251gel(y,i) = localextdeg(nf, pr, idealpow(nf,pr,E), Ld[i], ell, n);3252}32533254/* TODO use a factoredextchinese to ease computations afterwards ? */3255x = idealchinese(nf, mkvec2(cnd,pl), y);3256x = basistoalg(nf,x);3257pol = gsub(gpowgs(pol_x(var),n),x);32583259return gerepileupto(av,pol);3260}32613262static GEN3263get_vecsmall(GEN v)3264{3265switch(typ(v))3266{3267case t_VECSMALL: return v;3268case t_VEC: if (RgV_is_ZV(v)) return ZV_to_zv(v);3269}3270pari_err_TYPE("nfgrunwaldwang",v);3271return NULL;/*LCOV_EXCL_LINE*/3272}3273GEN3274nfgrunwaldwang(GEN nf0, GEN Lpr, GEN Ld, GEN pl, long var)3275{3276ulong n, ell, ell2;3277pari_sp av = avma;3278GEN nf, bnf;3279long t, w, i, vnf;32803281if (var < 0) var = 0;3282nf = get_nf(nf0,&t);3283if (!nf) pari_err_TYPE("nfgrunwaldwang",nf0);3284vnf = nf_get_varn(nf);3285if (varncmp(var, vnf) >= 0)3286pari_err_PRIORITY("nfgrunwaldwang", pol_x(var), ">=", vnf);3287if (typ(Lpr) != t_VEC) pari_err_TYPE("nfgrunwaldwang",Lpr);3288if (lg(Lpr) != lg(Ld)) pari_err_DIM("nfgrunwaldwang [#Lpr != #Ld]");3289if (nf_get_degree(nf)==1) Lpr = shallowcopy(Lpr);3290for (i=1; i<lg(Lpr); i++) {3291GEN pr = gel(Lpr,i);3292if (nf_get_degree(nf)==1 && typ(pr)==t_INT)3293gel(Lpr,i) = gel(idealprimedec(nf,pr), 1);3294else checkprid(pr);3295}3296if (lg(pl)-1 != nf_get_r1(nf))3297pari_err_DOMAIN("nfgrunwaldwang [pl should have r1 components]", "#pl",3298"!=", stoi(nf_get_r1(nf)), stoi(lg(pl)-1));32993300Ld = get_vecsmall(Ld);3301pl = get_vecsmall(pl);3302bnf = get_bnf(nf0,&t);3303n = (lg(Ld)==1)? 2: vecsmall_max(Ld);33043305if (!uisprimepower(n, &ell))3306pari_err_IMPL("nfgrunwaldwang for non prime-power local degrees (a)");3307for (i=1; i<lg(Ld); i++)3308if (Ld[i]!=1 && (!uisprimepower(Ld[i],&ell2) || ell2!=ell))3309pari_err_IMPL("nfgrunwaldwang for non prime-power local degrees (b)");3310for (i=1; i<lg(pl); i++)3311if (pl[i]==-1 && ell%2)3312pari_err_IMPL("nfgrunwaldwang for non prime-power local degrees (c)");33133314w = bnf? bnf_get_tuN(bnf): itos(gel(nfrootsof1(nf),1));33153316/* TODO choice between kummer and generic ? Let user choose between speed3317* and size */3318if (w%n==0 && lg(Ld)>1)3319return gerepileupto(av,nfgwkummer(nf,Lpr,Ld,pl,var));3320if (ell==n) {3321if (!bnf) bnf = Buchall(nf, nf_FORCE, 0);3322return gerepileupto(av,bnfgwgeneric(bnf,Lpr,Ld,pl,var));3323}3324pari_err_IMPL("nfgrunwaldwang for nonprime degree");3325return NULL; /*LCOV_EXCL_LINE*/3326}33273328/** HASSE INVARIANTS **/33293330/* TODO long -> ulong + uel */3331static GEN3332hasseconvert(GEN H, long n)3333{3334GEN h, c;3335long i, l;3336switch(typ(H)) {3337case t_VEC:3338l = lg(H); h = cgetg(l,t_VECSMALL);3339if (l == 1) return h;3340c = gel(H,1);3341if (typ(c) == t_VEC && l == 3)3342return mkvec2(gel(H,1),hasseconvert(gel(H,2),n));3343for (i=1; i<l; i++)3344{3345c = gel(H,i);3346switch(typ(c)) {3347case t_INT: break;3348case t_INTMOD:3349c = gel(c,2); break;3350case t_FRAC :3351c = gmulgs(c,n);3352if (typ(c) == t_INT) break;3353pari_err_DOMAIN("hasseconvert [degree should be a denominator of the invariant]", "denom(h)", "ndiv", stoi(n), Q_denom(gel(H,i)));3354default : pari_err_TYPE("Hasse invariant", c);3355}3356h[i] = smodis(c,n);3357}3358return h;3359case t_VECSMALL: return H;3360}3361pari_err_TYPE("Hasse invariant", H);3362return NULL;/*LCOV_EXCL_LINE*/3363}33643365/* assume f >= 2 */3366static long3367cyclicrelfrob0(GEN nf, GEN aut, GEN pr, GEN q, long f, long g)3368{3369GEN T, p, a, b, modpr = nf_to_Fq_init(nf,&pr,&T,&p);3370long s;33713372a = pol_x(nf_get_varn(nf));3373b = galoisapply(nf, aut, modpr_genFq(modpr));3374b = nf_to_Fq(nf, b, modpr);3375for (s = 0; !ZX_equal(a, b); s++) a = Fq_pow(a, q, T, p);3376return g * Fl_inv(s, f); /* < n */3377}33783379static long3380cyclicrelfrob(GEN rnf, GEN auts, GEN pr)3381{3382pari_sp av = avma;3383long f,g,frob, n = rnf_get_degree(rnf);3384GEN P = rnfidealprimedec(rnf, pr);33853386if (pr_get_e(gel(P,1)) > pr_get_e(pr))3387pari_err_DOMAIN("cyclicrelfrob","e(PR/pr)",">",gen_1,pr);3388g = lg(P) - 1;3389f = n / g;33903391if (f <= 2) frob = g % n;3392else {3393GEN nf2, PR = gel(P,1);3394GEN autabs = rnfeltreltoabs(rnf,gel(auts,g));3395nf2 = obj_check(rnf,rnf_NFABS);3396autabs = nfadd(nf2, autabs, gmul(rnf_get_k(rnf), rnf_get_alpha(rnf)));3397frob = cyclicrelfrob0(nf2, autabs, PR, pr_norm(pr), f, g);3398}3399return gc_long(av, frob);3400}34013402static long3403localhasse(GEN rnf, GEN cnd, GEN pl, GEN auts, GEN b, long k)3404{3405pari_sp av = avma;3406long v, m, h, lfa, frob, n, i;3407GEN previous, y, pr, nf, q, fa;3408nf = rnf_get_nf(rnf);3409n = rnf_get_degree(rnf);3410pr = gcoeff(cnd,k,1);3411v = nfval(nf, b, pr);3412m = lg(cnd)>1 ? nbrows(cnd) : 0;34133414/* add the valuation of b to the conductor... */3415previous = gcoeff(cnd,k,2);3416gcoeff(cnd,k,2) = addis(previous, v);34173418y = const_vec(m, gen_1);3419gel(y,k) = b;3420/* find a factored element y congruent to b mod pr^(vpr(b)+vpr(cnd)) and to 1 mod the conductor. */3421y = factoredextchinese(nf, cnd, y, pl, &fa);3422h = 0;3423lfa = nbrows(fa);3424/* sum of all Hasse invariants of (rnf/nf,aut,y) is 0, Hasse invariants at q!=pr are easy, Hasse invariant at pr is the same as for al=(rnf/nf,aut,b). */3425for (i=1; i<=lfa; i++) {3426q = gcoeff(fa,i,1);3427if (cmp_prime_ideal(pr,q)) {3428frob = cyclicrelfrob(rnf, auts, q);3429frob = Fl_mul(frob,umodiu(gcoeff(fa,i,2),n),n);3430h = Fl_add(h,frob,n);3431}3432}3433/* ...then restore it. */3434gcoeff(cnd,k,2) = previous;3435return gc_long(av, Fl_neg(h,n));3436}34373438static GEN3439allauts(GEN rnf, GEN aut)3440{3441long n = rnf_get_degree(rnf), i;3442GEN pol = rnf_get_pol(rnf), vaut;3443if (n==1) n=2;3444vaut = cgetg(n,t_VEC);3445aut = lift_shallow(rnfbasistoalg(rnf,aut));3446gel(vaut,1) = aut;3447for (i=1; i<n-1; i++)3448gel(vaut,i+1) = RgX_rem(poleval(gel(vaut,i), aut), pol);3449return vaut;3450}34513452static GEN3453clean_factor(GEN fa)3454{3455GEN P2,E2, P = gel(fa,1), E = gel(fa,2);3456long l = lg(P), i, j = 1;3457P2 = cgetg(l, t_COL);3458E2 = cgetg(l, t_COL);3459for (i = 1;i < l; i++)3460if (signe(gel(E,i))) {3461gel(P2,j) = gel(P,i);3462gel(E2,j) = gel(E,i); j++;3463}3464setlg(P2,j);3465setlg(E2,j); return mkmat2(P2,E2);3466}34673468/* shallow concat x[1],...x[nx],y[1], ... y[ny], returning a t_COL. To be3469* used when we do not know whether x,y are t_VEC or t_COL */3470static GEN3471colconcat(GEN x, GEN y)3472{3473long i, lx = lg(x), ly = lg(y);3474GEN z=cgetg(lx+ly-1, t_COL);3475for (i=1; i<lx; i++) z[i] = x[i];3476for (i=1; i<ly; i++) z[lx+i-1]= y[i];3477return z;3478}34793480/* return v(x) at all primes in listpr, replace x by cofactor */3481static GEN3482nfmakecoprime(GEN nf, GEN *px, GEN listpr)3483{3484long j, l = lg(listpr);3485GEN x1, x = *px, L = cgetg(l, t_COL);34863487if (typ(x) != t_MAT)3488{ /* scalar, divide at the end (fast valuation) */3489x1 = NULL;3490for (j=1; j<l; j++)3491{3492GEN pr = gel(listpr,j), e;3493long v = nfval(nf, x, pr);3494e = stoi(v); gel(L,j) = e;3495if (v) x1 = x1? idealmulpowprime(nf, x1, pr, e)3496: idealpow(nf, pr, e);3497}3498if (x1) x = idealdivexact(nf, idealhnf(nf,x), x1);3499}3500else3501{ /* HNF, divide as we proceed (reduce size) */3502for (j=1; j<l; j++)3503{3504GEN pr = gel(listpr,j);3505long v = idealval(nf, x, pr);3506gel(L,j) = stoi(v);3507if (v) x = idealmulpowprime(nf, x, pr, stoi(-v));3508}3509}3510*px = x; return L;3511}35123513/* Caveat: factorizations are not sorted wrt cmp_prime_ideal: Lpr comes first */3514static GEN3515computecnd(GEN rnf, GEN Lpr)3516{3517GEN id, nf, fa, Le, P,E;3518long n = rnf_get_degree(rnf);35193520nf = rnf_get_nf(rnf);3521id = rnf_get_idealdisc(rnf);3522Le = nfmakecoprime(nf, &id, Lpr);3523fa = idealfactor(nf, id); /* part of D_{L/K} coprime with Lpr */3524P = colconcat(Lpr,gel(fa,1));3525E = colconcat(Le, gel(fa,2));3526fa = mkmat2(P, gdiventgs(E, eulerphiu(n)));3527return mkvec2(fa, clean_factor(fa));3528}35293530/* h >= 0 */3531static void3532nextgen(GEN gene, long h, GEN* gens, GEN* hgens, long* ngens, long* curgcd) {3533long nextgcd = ugcd(h,*curgcd);3534if (nextgcd == *curgcd) return;3535(*ngens)++;3536gel(*gens,*ngens) = gene;3537gel(*hgens,*ngens) = utoi(h);3538*curgcd = nextgcd;3539return;3540}35413542static int3543dividesmod(long d, long h, long n) { return !(h%cgcd(d,n)); }35443545/* ramified prime with nontrivial Hasse invariant */3546static GEN3547localcomplete(GEN rnf, GEN pl, GEN cnd, GEN auts, long j, long n, long h, long* v)3548{3549GEN nf, gens, hgens, pr, modpr, T, p, sol, U, D, b, gene, randg, pu;3550long ngens, i, d, np, k, d1, d2, hg, dnf, vcnd, curgcd;3551nf = rnf_get_nf(rnf);3552pr = gcoeff(cnd,j,1);3553np = umodiu(pr_norm(pr), n);3554dnf = nf_get_degree(nf);3555vcnd = itos(gcoeff(cnd,j,2));3556ngens = 13+dnf;3557gens = zerovec(ngens);3558hgens = zerovec(ngens);3559*v = 0;3560curgcd = 0;3561ngens = 0;35623563if (!uisprime(n)) {3564gene = pr_get_gen(pr);3565hg = localhasse(rnf, cnd, pl, auts, gene, j);3566nextgen(gene, hg, &gens, &hgens, &ngens, &curgcd);3567}35683569if (ugcd(np,n) != 1) { /* GCD(Np,n) != 1 */3570pu = idealprincipalunits(nf,pr,vcnd);3571pu = abgrp_get_gen(pu);3572for (i=1; i<lg(pu) && !dividesmod(curgcd,h,n); i++) {3573gene = gel(pu,i);3574hg = localhasse(rnf, cnd, pl, auts, gene, j);3575nextgen(gene, hg, &gens, &hgens, &ngens, &curgcd);3576}3577}35783579d = ugcd(np-1,n);3580if (d != 1) { /* GCD(Np-1,n) != 1 */3581modpr = nf_to_Fq_init(nf, &pr, &T, &p);3582while (!dividesmod(curgcd,h,n)) { /* TODO gener_FpXQ_local */3583if (T==NULL) randg = randomi(p);3584else randg = random_FpX(degpol(T), varn(T),p);35853586if (!gequal0(randg) && !gequal1(randg)) {3587gene = Fq_to_nf(randg, modpr);3588hg = localhasse(rnf, cnd, pl, auts, gene, j);3589nextgen(gene, hg, &gens, &hgens, &ngens, &curgcd);3590}3591}3592}35933594setlg(gens,ngens+1);3595setlg(hgens,ngens+1);35963597sol = ZV_extgcd(hgens);3598D = gel(sol,1);3599U = gmael(sol,2,ngens);36003601b = gen_1;3602d = itou(D);3603d1 = ugcd(d,n);3604d2 = d/d1;3605d = ((h/d1)*Fl_inv(d2,n))%n;3606for (i=1; i<=ngens; i++) {3607k = (itos(gel(U,i))*d)%n;3608if (k<0) k = n-k;3609if (k) b = nfmul(nf, b, nfpow_u(nf, gel(gens,i),k));3610if (i==1) *v = k;3611}3612return b;3613}36143615static int3616testsplits(GEN data, GEN fa)3617{3618GEN rnf = gel(data,1), forbid = gel(data,2), P = gel(fa,1), E = gel(fa,2);3619long i, n, l = lg(P);36203621for (i = 1; i < l; i++)3622{3623GEN pr = gel(P,i);3624if (tablesearch(forbid, pr, &cmp_prime_ideal)) return 0;3625}3626n = rnf_get_degree(rnf);3627for (i = 1; i < l; i++)3628{3629long e = itos(gel(E,i)) % n;3630if (e)3631{3632GEN L = rnfidealprimedec(rnf, gel(P,i));3633long g = lg(L) - 1;3634if ((e * g) % n) return 0;3635}3636}3637return 1;3638}36393640/* remove entries with Hasse invariant 0 */3641static GEN3642hassereduce(GEN hf)3643{3644GEN pr,h, PR = gel(hf,1), H = gel(hf,2);3645long i, j, l = lg(PR);36463647pr= cgetg(l, t_VEC);3648h = cgetg(l, t_VECSMALL);3649for (i = j = 1; i < l; i++)3650if (H[i]) {3651gel(pr,j) = gel(PR,i);3652h[j] = H[i]; j++;3653}3654setlg(pr,j);3655setlg(h,j); return mkvec2(pr,h);3656}36573658/* v vector of prid. Return underlying list of rational primes */3659static GEN3660pr_primes(GEN v)3661{3662long i, l = lg(v);3663GEN w = cgetg(l,t_VEC);3664for (i=1; i<l; i++) gel(w,i) = pr_get_p(gel(v,i));3665return ZV_sort_uniq(w);3666}36673668/* rnf complete */3669static GEN3670alg_complete0(GEN rnf, GEN aut, GEN hf, GEN hi, long maxord)3671{3672pari_sp av = avma;3673GEN nf, pl, pl2, cnd, prcnd, cnds, y, Lpr, auts, b, fa, data, hfe;3674GEN forbid, al, ind;3675long D, n, d, i, j, l;3676nf = rnf_get_nf(rnf);3677n = rnf_get_degree(rnf);3678d = nf_get_degree(nf);3679D = d*n*n;3680checkhasse(nf,hf,hi,n);3681hf = hassereduce(hf);3682Lpr = gel(hf,1);3683hfe = gel(hf,2);36843685auts = allauts(rnf,aut);36863687pl = leafcopy(hi); /* conditions on the final b */3688pl2 = leafcopy(hi); /* conditions for computing local Hasse invariants */3689l = lg(pl); ind = cgetg(l, t_VECSMALL);3690for (i = j = 1; i < l; i++)3691if (hi[i]) { pl[i] = -1; pl2[i] = 1; } else ind[j++] = i;3692setlg(ind, j);3693y = nfpolsturm(nf, rnf_get_pol(rnf), ind);3694for (i = 1; i < j; i++)3695if (!signe(gel(y,i))) { pl[ind[i]] = 1; pl2[ind[i]] = 1; }36963697cnds = computecnd(rnf,Lpr);3698prcnd = gel(cnds,1);3699cnd = gel(cnds,2);3700y = cgetg(lgcols(prcnd),t_VEC);3701forbid = vectrunc_init(lg(Lpr));3702for (i=j=1; i<lg(Lpr); i++)3703{3704GEN pr = gcoeff(prcnd,i,1), yi;3705long v, e = itou( gcoeff(prcnd,i,2) );3706if (!e) {3707long frob = cyclicrelfrob(rnf,auts,pr), f1 = ugcd(frob,n);3708vectrunc_append(forbid, pr);3709yi = gen_0;3710v = ((hfe[i]/f1) * Fl_inv(frob/f1,n)) % n;3711}3712else3713yi = localcomplete(rnf, pl2, cnd, auts, j++, n, hfe[i], &v);3714gel(y,i) = yi;3715gcoeff(prcnd,i,2) = stoi(e + v);3716}3717for (; i<lgcols(prcnd); i++) gel(y,i) = gen_1;3718gen_sort_inplace(forbid, (void*)&cmp_prime_ideal, &cmp_nodata, NULL);3719data = mkvec2(rnf,forbid);3720b = factoredextchinesetest(nf,prcnd,y,pl,&fa,data,testsplits);37213722al = cgetg(12, t_VEC);3723gel(al,10)= gen_0; /* must be set first */3724gel(al,1) = rnf;3725gel(al,2) = auts;3726gel(al,3) = basistoalg(nf,b);3727gel(al,4) = hi;3728/* add primes | disc or b with trivial Hasse invariant to hf */3729Lpr = gel(prcnd,1); y = b;3730(void)nfmakecoprime(nf, &y, Lpr);3731Lpr = shallowconcat(Lpr, gel(idealfactor(nf,y), 1));3732settyp(Lpr,t_VEC);3733hf = mkvec2(Lpr, shallowconcat(hfe, const_vecsmall(lg(Lpr)-lg(hfe), 0)));3734gel(al,5) = hf;3735gel(al,6) = gen_0;3736gel(al,7) = matid(D);3737gel(al,8) = matid(D); /* TODO modify 7, 8 et 9 once LLL added */3738gel(al,9) = algnatmultable(al,D);3739gel(al,11)= algtracebasis(al);3740if (maxord) al = alg_maximal_primes(al, pr_primes(Lpr));3741return gerepilecopy(av, al);3742}37433744GEN3745alg_complete(GEN rnf, GEN aut, GEN hf, GEN hi, long maxord)3746{3747long n = rnf_get_degree(rnf);3748rnfcomplete(rnf);3749return alg_complete0(rnf,aut,hasseconvert(hf,n),hasseconvert(hi,n), maxord);3750}37513752void3753checkhasse(GEN nf, GEN hf, GEN hi, long n)3754{3755GEN Lpr, Lh;3756long i, sum;3757if (typ(hf) != t_VEC || lg(hf) != 3) pari_err_TYPE("checkhasse [hf]", hf);3758Lpr = gel(hf,1);3759Lh = gel(hf,2);3760if (typ(Lpr) != t_VEC) pari_err_TYPE("checkhasse [Lpr]", Lpr);3761if (typ(Lh) != t_VECSMALL) pari_err_TYPE("checkhasse [Lh]", Lh);3762if (typ(hi) != t_VECSMALL) pari_err_TYPE("checkhasse [hi]", hi);3763if ((nf && lg(hi) != nf_get_r1(nf)+1))3764pari_err_DOMAIN("checkhasse [hi should have r1 components]","#hi","!=",stoi(nf_get_r1(nf)),stoi(lg(hi)-1));3765if (lg(Lpr) != lg(Lh))3766pari_err_DIM("checkhasse [Lpr and Lh should have same length]");3767for (i=1; i<lg(Lpr); i++) checkprid(gel(Lpr,i));3768if (lg(gen_sort_uniq(Lpr, (void*)cmp_prime_ideal, cmp_nodata)) < lg(Lpr))3769pari_err(e_MISC, "error in checkhasse [duplicate prime ideal]");3770sum = 0;3771for (i=1; i<lg(Lh); i++) sum = (sum+Lh[i])%n;3772for (i=1; i<lg(hi); i++) {3773if (hi[i] && 2*hi[i] != n) pari_err_DOMAIN("checkhasse", "Hasse invariant at real place [must be 0 or 1/2]", "!=", n%2? gen_0 : stoi(n/2), stoi(hi[i]));3774sum = (sum+hi[i])%n;3775}3776if (sum<0) sum = n+sum;3777if (sum != 0)3778pari_err_DOMAIN("checkhasse","sum(Hasse invariants)","!=",gen_0,Lh);3779}37803781static GEN3782hassecoprime(GEN hf, GEN hi, long n)3783{3784pari_sp av = avma;3785long l, i, j, lk, inv;3786GEN fa, P,E, res, hil, hfl;3787hi = hasseconvert(hi, n);3788hf = hasseconvert(hf, n);3789checkhasse(NULL,hf,hi,n);3790fa = factoru(n);3791P = gel(fa,1); l = lg(P);3792E = gel(fa,2);3793res = cgetg(l,t_VEC);3794for (i=1; i<l; i++) {3795lk = upowuu(P[i],E[i]);3796inv = Fl_invsafe((n/lk)%lk, lk);3797hil = gcopy(hi);3798hfl = gcopy(hf);37993800if (P[i] == 2)3801for (j=1; j<lg(hil); j++) hil[j] = hi[j]==0 ? 0 : lk/2;3802else3803for (j=1; j<lg(hil); j++) hil[j] = 0;3804for (j=1; j<lgcols(hfl); j++) gel(hfl,2)[j] = (gel(hf,2)[j]*inv)%lk;3805hfl = hassereduce(hfl);3806gel(res,i) = mkvec3(hfl,hil,utoi(lk));3807}38083809return gerepilecopy(av, res);3810}38113812/* no garbage collection */3813static GEN3814genefrob(GEN nf, GEN gal, GEN r)3815{3816long i;3817GEN g = identity_perm(nf_get_degree(nf)), fa = Z_factor(r), p, pr, frob;3818for (i=1; i<lgcols(fa); i++) {3819p = gcoeff(fa,i,1);3820pr = idealprimedec(nf, p);3821pr = gel(pr,1);3822frob = idealfrobenius(nf, gal, pr);3823g = perm_mul(g, perm_pow(frob, gcoeff(fa,i,2)));3824}3825return g;3826}38273828static GEN3829rnfcycaut(GEN rnf)3830{3831GEN nf2 = obj_check(rnf, rnf_NFABS);3832GEN L, alpha, pol, salpha, s, sj, polabs, k, X, pol0, nf;3833long i, d, j;3834d = rnf_get_degree(rnf);3835L = galoisconj(nf2,NULL);3836alpha = lift_shallow(rnf_get_alpha(rnf));3837pol = rnf_get_pol(rnf);3838k = rnf_get_k(rnf);3839polabs = rnf_get_polabs(rnf);3840nf = rnf_get_nf(rnf);3841pol0 = nf_get_pol(nf);3842X = RgX_rem(pol_x(varn(pol0)), pol0);38433844/* TODO check mod prime of degree 1 */3845for (i=1; i<lg(L); i++) {3846s = gel(L,i);3847salpha = RgX_RgXQ_eval(alpha,s,polabs);3848if (!gequal(alpha,salpha)) continue;38493850s = lift_shallow(rnfeltabstorel(rnf,s));3851sj = s = gsub(s, gmul(k,X));3852for (j=1; !gequal0(gsub(sj,pol_x(varn(s)))); j++)3853sj = RgX_RgXQ_eval(sj,s,pol);3854if (j<d) continue;3855return s;3856}3857return NULL; /*LCOV_EXCL_LINE*/3858}38593860/* returns Lpr augmented with an extra, distinct prime */3861/* TODO be less lazy and return a small prime */3862static GEN3863extraprime(GEN nf, GEN Lpr)3864{3865GEN Lpr2, p = gen_2, pr;3866long i;3867Lpr2 = cgetg(lg(Lpr)+1,t_VEC);3868for (i=1; i<lg(Lpr); i++)3869{3870gel(Lpr2,i) = gel(Lpr,i);3871p = gmax_shallow(p, pr_get_p(gel(Lpr,i)));3872}3873p = nextprime(addis(p,1));3874pr = gel(idealprimedec_limit_f(nf, p, 0), 1);3875gel(Lpr2,lg(Lpr)) = pr;3876return Lpr2;3877}38783879GEN3880alg_hasse(GEN nf, long n, GEN hf, GEN hi, long var, long maxord)3881{3882pari_sp av = avma;3883GEN primary, al = gen_0, al2, rnf, hil, hfl, Ld, pl, pol, Lpr, aut, Lpr2, Ld2;3884long i, lk, j, maxdeg;3885dbg_printf(1)("alg_hasse\n");3886if (n<=1) pari_err_DOMAIN("alg_hasse", "degree", "<=", gen_1, stoi(n));3887primary = hassecoprime(hf, hi, n);3888for (i=1; i<lg(primary); i++) {3889lk = itos(gmael(primary,i,3));3890hfl = gmael(primary,i,1);3891hil = gmael(primary,i,2);3892checkhasse(nf, hfl, hil, lk);3893dbg_printf(1)("alg_hasse: i=%d hf=%Ps hi=%Ps lk=%d\n", i, hfl, hil, lk);38943895if (lg(gel(hfl,1))>1 || lk%2==0) {3896maxdeg = 1;3897Lpr = gel(hfl,1);3898Ld = gcopy(gel(hfl,2));3899for (j=1; j<lg(Ld); j++)3900{3901Ld[j] = lk/ugcd(lk,Ld[j]);3902maxdeg = maxss(Ld[j],maxdeg);3903}3904pl = gcopy(hil);3905for (j=1; j<lg(pl); j++) if(pl[j])3906{3907pl[j] = -1;3908maxdeg = maxss(maxdeg,2);3909}39103911Lpr2 = Lpr;3912Ld2 = Ld;3913if (maxdeg<lk)3914{3915if (maxdeg==1 && lk==2 && lg(pl)>1) pl[1] = -1;3916else3917{3918Lpr2 = extraprime(nf,Lpr);3919Ld2 = cgetg(lg(Ld)+1, t_VECSMALL);3920for (j=1; j<lg(Ld); j++) Ld2[j] = Ld[j];3921Ld2[lg(Ld)] = lk;3922}3923}39243925dbg_printf(2)("alg_hasse: calling nfgrunwaldwang Lpr=%Ps Pd=%Ps pl=%Ps\n",3926Lpr, Ld, pl);3927pol = nfgrunwaldwang(nf, Lpr2, Ld2, pl, var);3928dbg_printf(2)("alg_hasse: calling rnfinit(%Ps)\n", pol);3929rnf = rnfinit0(nf,pol,1);3930dbg_printf(2)("alg_hasse: computing automorphism\n");3931aut = rnfcycaut(rnf);3932dbg_printf(2)("alg_hasse: calling alg_complete\n");3933al2 = alg_complete0(rnf,aut,hfl,hil,maxord);3934}3935else al2 = alg_matrix(nf, lk, var, cgetg(1,t_VEC), maxord);39363937if (i==1) al = al2;3938else al = algtensor(al,al2,maxord);3939}3940return gerepilecopy(av,al);3941}39423943/** CYCLIC ALGEBRA WITH GIVEN HASSE INVARIANTS **/39443945/* no garbage collection */3946static int3947linindep(GEN pol, GEN L)3948{3949long i;3950GEN fa;3951for (i=1; i<lg(L); i++) {3952fa = nffactor(gel(L,i),pol);3953if (lgcols(fa)>2) return 0;3954}3955return 1;3956}39573958/* no garbage collection */3959static GEN3960subcycloindep(GEN nf, long n, long v, GEN L, GEN *pr)3961{3962pari_sp av;3963forprime_t S;3964ulong p;3965u_forprime_arith_init(&S, 1, ULONG_MAX, 1, n);3966av = avma;3967while ((p = u_forprime_next(&S)))3968{3969ulong r = pgener_Fl(p);3970GEN pol = galoissubcyclo(utoipos(p), utoipos(Fl_powu(r,n,p)), 0, v);3971GEN fa = nffactor(nf, pol);3972if (lgcols(fa) == 2 && linindep(pol,L)) { *pr = utoipos(r); return pol; }3973set_avma(av);3974}3975pari_err_BUG("subcycloindep (no suitable prime = 1(mod n))"); /*LCOV_EXCL_LINE*/3976*pr = NULL; return NULL; /*LCOV_EXCL_LINE*/3977}39783979GEN3980alg_matrix(GEN nf, long n, long v, GEN L, long maxord)3981{3982pari_sp av = avma;3983GEN pol, gal, rnf, cyclo, g, r, aut;3984dbg_printf(1)("alg_matrix\n");3985if (n<=0) pari_err_DOMAIN("alg_matrix", "n", "<=", gen_0, stoi(n));3986pol = subcycloindep(nf, n, v, L, &r);3987rnf = rnfinit(nf, pol);3988cyclo = nfinit(pol, nf_get_prec(nf));3989gal = galoisinit(cyclo, NULL);3990g = genefrob(cyclo,gal,r);3991aut = galoispermtopol(gal,g);3992return gerepileupto(av, alg_cyclic(rnf, aut, gen_1, maxord));3993}39943995GEN3996alg_hilbert(GEN nf, GEN a, GEN b, long v, long maxord)3997{3998pari_sp av = avma;3999GEN C, P, rnf, aut;4000dbg_printf(1)("alg_hilbert\n");4001checknf(nf);4002if (!isint1(Q_denom(a)))4003pari_err_DOMAIN("alg_hilbert", "denominator(a)", "!=", gen_1,a);4004if (!isint1(Q_denom(b)))4005pari_err_DOMAIN("alg_hilbert", "denominator(b)", "!=", gen_1,b);40064007if (v < 0) v = 0;4008C = Rg_col_ei(gneg(a), 3, 3);4009gel(C,1) = gen_1;4010P = gtopoly(C,v);4011rnf = rnfinit(nf, P);4012aut = gneg(pol_x(v));4013return gerepileupto(av, alg_cyclic(rnf, aut, b, maxord));4014}40154016GEN4017alginit(GEN A, GEN B, long v, long maxord)4018{4019long w;4020switch(nftyp(A))4021{4022case typ_NF:4023if (v<0) v=0;4024w = gvar(nf_get_pol(A));4025if (varncmp(v,w)>=0) pari_err_PRIORITY("alginit", pol_x(v), ">=", w);4026switch(typ(B))4027{4028long nB;4029case t_INT: return alg_matrix(A, itos(B), v, cgetg(1,t_VEC), maxord);4030case t_VEC:4031nB = lg(B)-1;4032if (nB && typ(gel(B,1)) == t_MAT) return alg_csa_table(A,B,v,maxord);4033switch(nB)4034{4035case 2: return alg_hilbert(A, gel(B,1), gel(B,2), v, maxord);4036case 3:4037if (typ(gel(B,1))!=t_INT)4038pari_err_TYPE("alginit [degree should be an integer]", gel(B,1));4039return alg_hasse(A, itos(gel(B,1)), gel(B,2), gel(B,3), v,4040maxord);4041}4042}4043pari_err_TYPE("alginit", B); break;40444045case typ_RNF:4046if (typ(B) != t_VEC || lg(B) != 3) pari_err_TYPE("alginit", B);4047return alg_cyclic(A, gel(B,1), gel(B,2), maxord);4048}4049pari_err_TYPE("alginit", A);4050return NULL;/*LCOV_EXCL_LINE*/4051}40524053/* assumes al CSA or CYCLIC */4054static GEN4055algnatmultable(GEN al, long D)4056{4057GEN res, x;4058long i;4059res = cgetg(D+1,t_VEC);4060for (i=1; i<=D; i++) {4061x = algnattoalg(al,col_ei(D,i));4062gel(res,i) = algZmultable(al,x);4063}4064return res;4065}40664067/* no garbage collection */4068static void4069algcomputehasse(GEN al)4070{4071long r1, k, n, m, m1, m2, m3, i, m23, m123;4072GEN rnf, nf, b, fab, disc2, cnd, fad, auts, pr, pl, perm, y, hi, PH, H, L;40734074rnf = alg_get_splittingfield(al);4075n = rnf_get_degree(rnf);4076nf = rnf_get_nf(rnf);4077b = alg_get_b(al);4078r1 = nf_get_r1(nf);4079auts = alg_get_auts(al);4080(void)alg_get_abssplitting(al);40814082y = nfpolsturm(nf, rnf_get_pol(rnf), NULL);4083pl = cgetg(r1+1, t_VECSMALL);4084/* real places where rnf/nf ramifies */4085for (k = 1; k <= r1; k++) pl[k] = !signe(gel(y,k));40864087/* infinite Hasse invariants */4088if (odd(n)) hi = const_vecsmall(r1, 0);4089else4090{4091GEN s = nfsign(nf, b);4092hi = cgetg(r1+1, t_VECSMALL);4093for (k = 1; k<=r1; k++) hi[k] = (s[k] && pl[k]) ? (n/2) : 0;4094}40954096fab = idealfactor(nf, b);4097disc2 = rnf_get_idealdisc(rnf);4098L = nfmakecoprime(nf, &disc2, gel(fab,1));4099m = lg(L)-1;4100/* m1 = #{pr|b: pr \nmid disc}, m3 = #{pr|b: pr | disc} */4101perm = cgetg(m+1, t_VECSMALL);4102for (i=1, m1=m, k=1; k<=m; k++)4103if (signe(gel(L,k))) perm[m1--] = k; else perm[i++] = k;4104m3 = m - m1;41054106/* disc2 : factor of disc coprime to b */4107fad = idealfactor(nf, disc2);4108/* m2 : number of prime factors of disc not dividing b */4109m2 = nbrows(fad);4110m23 = m2+m3;4111m123 = m1+m2+m3;41124113/* initialize the possibly ramified primes (hasse) and the factored conductor of rnf/nf (cnd) */4114cnd = zeromatcopy(m23,2);4115PH = cgetg(m123+1, t_VEC); /* ramified primes */4116H = cgetg(m123+1, t_VECSMALL); /* Hasse invariant */4117/* compute Hasse invariant at primes that are unramified in rnf/nf */4118for (k=1; k<=m1; k++) {/* pr | b, pr \nmid disc */4119long frob, e, j = perm[k];4120pr = gcoeff(fab,j,1);4121e = itos(gcoeff(fab,j,2));4122frob = cyclicrelfrob(rnf, auts, pr);4123gel(PH,k) = pr;4124H[k] = Fl_mul(frob, e, n);4125}4126/* compute Hasse invariant at primes that are ramified in rnf/nf */4127for (k=1; k<=m2; k++) {/* pr \nmid b, pr | disc */4128pr = gcoeff(fad,k,1);4129gel(PH,k+m1) = pr;4130gcoeff(cnd,k,1) = pr;4131gcoeff(cnd,k,2) = gcoeff(fad,k,2);4132}4133for (k=1; k<=m3; k++) { /* pr | (b, disc) */4134long j = perm[k+m1];4135pr = gcoeff(fab,j,1);4136gel(PH,k+m1+m2) = pr;4137gcoeff(cnd,k+m2,1) = pr;4138gcoeff(cnd,k+m2,2) = gel(L,j);4139}4140gel(cnd,2) = gdiventgs(gel(cnd,2), eulerphiu(n));4141for (k=1; k<=m23; k++) H[k+m1] = localhasse(rnf, cnd, pl, auts, b, k);4142gel(al,4) = hi;4143perm = gen_indexsort(PH, (void*)&cmp_prime_ideal, &cmp_nodata);4144gel(al,5) = mkvec2(vecpermute(PH,perm),vecsmallpermute(H,perm));4145checkhasse(nf,alg_get_hasse_f(al),alg_get_hasse_i(al),n);4146}41474148static GEN4149alg_maximal_primes(GEN al, GEN P)4150{4151pari_sp av = avma;4152long l = lg(P), i;4153for (i=1; i<l; i++)4154{4155if (i != 1) al = gerepilecopy(av, al);4156al = alg_pmaximal(al,gel(P,i));4157}4158return al;4159}41604161GEN4162alg_cyclic(GEN rnf, GEN aut, GEN b, long maxord)4163{4164pari_sp av = avma;4165GEN al, nf;4166long D, n, d;4167dbg_printf(1)("alg_cyclic\n");4168checkrnf(rnf);4169if (!isint1(Q_denom(b)))4170pari_err_DOMAIN("alg_cyclic", "denominator(b)", "!=", gen_1,b);41714172nf = rnf_get_nf(rnf);4173n = rnf_get_degree(rnf);4174d = nf_get_degree(nf);4175D = d*n*n;41764177al = cgetg(12,t_VEC);4178gel(al,10)= gen_0; /* must be set first */4179gel(al,1) = rnf;4180gel(al,2) = allauts(rnf, aut);4181gel(al,3) = basistoalg(nf,b);4182rnf_build_nfabs(rnf, nf_get_prec(nf));4183gel(al,6) = gen_0;4184gel(al,7) = matid(D);4185gel(al,8) = matid(D); /* TODO modify 7, 8 et 9 once LLL added */4186gel(al,9) = algnatmultable(al,D);4187gel(al,11)= algtracebasis(al);41884189algcomputehasse(al);41904191if (maxord) {4192GEN hf = alg_get_hasse_f(al), pr = gel(hf,1);4193al = alg_maximal_primes(al, pr_primes(pr));4194}4195return gerepilecopy(av, al);4196}41974198static int4199ismaximalsubfield(GEN al, GEN x, GEN d, long v, GEN *pt_minpol)4200{4201GEN cp = algbasischarpoly(al, x, v), lead;4202if (!ispower(cp, d, pt_minpol)) return 0;4203lead = leading_coeff(*pt_minpol);4204if (isintm1(lead)) *pt_minpol = gneg(*pt_minpol);4205return ZX_is_irred(*pt_minpol);4206}42074208static GEN4209findmaximalsubfield(GEN al, GEN d, long v)4210{4211long count, nb=2, i, N = alg_get_absdim(al), n = nf_get_degree(alg_get_center(al));4212GEN x, minpol, maxc = gen_1;42134214for (i=n+1; i<=N; i+=n) {4215for (count=0; count<2 && i+count<=N; count++) {4216x = col_ei(N,i+count);4217if (ismaximalsubfield(al, x, d, v, &minpol)) return mkvec2(x,minpol);4218}4219}42204221while(1) {4222x = zerocol(N);4223for (count=0; count<nb; count++)4224{4225i = random_Fl(N)+1;4226gel(x,i) = addiu(randomi(maxc),1);4227if (random_bits(1)) gel(x,i) = negi(gel(x,i));4228}4229if (ismaximalsubfield(al, x, d, v, &minpol)) return mkvec2(x,minpol);4230if (!random_bits(3)) maxc = addiu(maxc,1);4231if (nb<N) nb++;4232}42334234return NULL; /* LCOV_EXCL_LINE */4235}42364237static GEN4238frobeniusform(GEN al, GEN x)4239{4240GEN M, FP, P, Pi;42414242/* /!\ has to be the *right* multiplication table */4243M = algbasisrightmultable(al, x);42444245FP = matfrobenius(M,2,0); /* M = P^(-1)*F*P */4246P = gel(FP,2);4247Pi = RgM_inv(P);4248return mkvec2(P, Pi);4249}42504251static void4252computesplitting(GEN al, long d, long v)4253{4254GEN subf, x, pol, polabs, basis, P, Pi, nf = alg_get_center(al), rnf, Lbasis, Lbasisinv, Q, pows;4255long i, n = nf_get_degree(nf), nd = n*d, N = alg_get_absdim(al), j, j2;42564257subf = findmaximalsubfield(al, utoipos(d), v);4258x = gel(subf, 1);4259polabs = gel(subf, 2);42604261/* Frobenius form to obtain L-vector space structure */4262basis = frobeniusform(al, x);4263P = gel(basis, 1);4264Pi = gel(basis, 2);42654266/* construct rnf of splitting field */4267pol = nffactor(nf,polabs);4268pol = gcoeff(pol,1,1);4269gel(al,1) = rnf = rnfinit(nf, pol);4270/* since pol is irreducible over Q, we have k=0 in rnf. */4271if (!gequal0(rnf_get_k(rnf)))4272pari_err_BUG("computesplitting (k!=0)"); /*LCOV_EXCL_LINE*/4273gel(al,6) = gen_0;4274rnf_build_nfabs(rnf, nf_get_prec(nf));42754276/* construct splitting data */4277Lbasis = cgetg(d+1, t_MAT);4278for (j=j2=1; j<=d; j++, j2+=nd)4279gel(Lbasis,j) = gel(Pi,j2);42804281Q = zeromatcopy(d,N);4282pows = pol_x_powers(nd,v);4283for (i=j=1; j<=N; j+=nd, i++)4284for (j2=0; j2<nd; j2++)4285gcoeff(Q,i,j+j2) = mkpolmod(gel(pows,j2+1),polabs);4286Lbasisinv = RgM_mul(Q,P);42874288gel(al,3) = mkvec3(x,Lbasis,Lbasisinv);4289}42904291/* assumes that mt defines a central simple algebra over nf */4292GEN4293alg_csa_table(GEN nf, GEN mt0, long v, long maxord)4294{4295pari_sp av = avma;4296GEN al, mt;4297long n, D, d2 = lg(mt0)-1, d = usqrt(d2);4298dbg_printf(1)("alg_csa_table\n");42994300nf = checknf(nf);4301mt = check_relmt(nf,mt0);4302if (!mt) pari_err_TYPE("alg_csa_table", mt0);4303n = nf_get_degree(nf);4304D = n*d2;4305if (d*d != d2)4306pari_err_DOMAIN("alg_csa_table","(nonsquare) dimension","!=",stoi(d*d),mt);43074308al = cgetg(12, t_VEC);4309gel(al,10) = gen_0; /* must be set first */4310gel(al,1) = zerovec(12); gmael(al,1,10) = nf;4311gmael(al,1,1) = gpowgs(pol_x(0), d); /* placeholder before splitting field */4312gel(al,2) = mt;4313gel(al,3) = gen_0; /* placeholder */4314gel(al,4) = gel(al,5) = gen_0; /* TODO Hasse invariants */4315gel(al,5) = gel(al,6) = gen_0; /* placeholder */4316gel(al,7) = matid(D);4317gel(al,8) = matid(D);4318gel(al,9) = algnatmultable(al,D);4319gel(al,11)= algtracebasis(al);4320if (maxord) al = alg_maximal(al);4321computesplitting(al, d, v);4322return gerepilecopy(av, al);4323}43244325static GEN4326algtableinit_i(GEN mt0, GEN p)4327{4328GEN al, mt;4329long i, n;43304331if (p && !signe(p)) p = NULL;4332mt = check_mt(mt0,p);4333if (!mt) pari_err_TYPE("algtableinit", mt0);4334if (!p && !isint1(Q_denom(mt0)))4335pari_err_DOMAIN("algtableinit", "denominator(mt)", "!=", gen_1, mt0);4336n = lg(mt)-1;4337al = cgetg(12, t_VEC);4338for (i=1; i<=6; i++) gel(al,i) = gen_0;4339gel(al,7) = matid(n);4340gel(al,8) = matid(n);4341gel(al,9) = mt;4342gel(al,10) = p? p: gen_0;4343gel(al,11)= algtracebasis(al);4344return al;4345}4346GEN4347algtableinit(GEN mt0, GEN p)4348{4349pari_sp av = avma;4350if (p)4351{4352if (typ(p) != t_INT) pari_err_TYPE("algtableinit",p);4353if (signe(p) && !BPSW_psp(p)) pari_err_PRIME("algtableinit",p);4354}4355return gerepilecopy(av, algtableinit_i(mt0, p));4356}43574358/** REPRESENTATIONS OF GROUPS **/43594360static GEN4361list_to_regular_rep(GEN elts, long n)4362{4363GEN reg, elts2, g;4364long i,j;4365elts = shallowcopy(elts);4366gen_sort_inplace(elts, (void*)&vecsmall_lexcmp, &cmp_nodata, NULL);4367reg = cgetg(n+1, t_VEC);4368gel(reg,1) = identity_perm(n);4369for (i=2; i<=n; i++) {4370g = perm_inv(gel(elts,i));4371elts2 = cgetg(n+1, t_VEC);4372for (j=1; j<=n; j++) gel(elts2,j) = perm_mul(g,gel(elts,j));4373gen_sort_inplace(elts2, (void*)&vecsmall_lexcmp, &cmp_nodata, &gel(reg,i));4374}4375return reg;4376}43774378static GEN4379matrix_perm(GEN perm, long n)4380{4381GEN m;4382long j;4383m = cgetg(n+1, t_MAT);4384for (j=1; j<=n; j++) {4385gel(m,j) = col_ei(n,perm[j]);4386}4387return m;4388}43894390GEN4391conjclasses_algcenter(GEN cc, GEN p)4392{4393GEN mt, elts = gel(cc,1), conjclass = gel(cc,2), rep = gel(cc,3), card;4394long i, nbcl = lg(rep)-1, n = lg(elts)-1;4395pari_sp av;43964397card = zero_Flv(nbcl);4398for (i=1; i<=n; i++) card[conjclass[i]]++;43994400/* multiplication table of the center of Z[G] (class functions) */4401mt = cgetg(nbcl+1,t_VEC);4402for (i=1;i<=nbcl;i++) gel(mt,i) = zero_Flm_copy(nbcl,nbcl);4403av = avma;4404for (i=1;i<=nbcl;i++)4405{4406GEN xi = gel(elts,rep[i]), mi = gel(mt,i);4407long j,k;4408for (j=1;j<=n;j++)4409{4410GEN xj = gel(elts,j);4411k = vecsearch(elts, perm_mul(xi,xj), NULL);4412ucoeff(mi, conjclass[k], conjclass[j])++;4413}4414for (k=1; k<=nbcl; k++)4415for (j=1; j<=nbcl; j++)4416{4417ucoeff(mi,k,j) *= card[i];4418ucoeff(mi,k,j) /= card[k];4419}4420set_avma(av);4421}4422for (i=1;i<=nbcl;i++) gel(mt,i) = Flm_to_ZM(gel(mt,i));4423return algtableinit_i(mt,p);4424}44254426GEN4427alggroupcenter(GEN G, GEN p, GEN *pcc)4428{4429pari_sp av = avma;4430GEN cc = group_to_cc(G), al = conjclasses_algcenter(cc, p);4431if (!pcc) al = gerepilecopy(av,al);4432else4433{ *pcc = cc; gerepileall(av,2,&al,pcc); }4434return al;4435}44364437static GEN4438groupelts_algebra(GEN elts, GEN p)4439{4440pari_sp av = avma;4441GEN mt;4442long i, n = lg(elts)-1;4443elts = list_to_regular_rep(elts,n);4444mt = cgetg(n+1, t_VEC);4445for (i=1; i<=n; i++) gel(mt,i) = matrix_perm(gel(elts,i),n);4446return gerepilecopy(av, algtableinit_i(mt,p));4447}44484449GEN4450alggroup(GEN gal, GEN p)4451{4452GEN elts = checkgroupelts(gal);4453return groupelts_algebra(elts, p);4454}44554456/** MAXIMAL ORDER **/44574458GEN4459alg_changeorder(GEN al, GEN ord)4460{4461GEN al2, mt, iord, mtx;4462long i, n;4463pari_sp av = avma;44644465if (!gequal0(gel(al,10)))4466pari_err_DOMAIN("alg_changeorder","characteristic","!=",gen_0,gel(al,10));4467n = alg_get_absdim(al);44684469iord = QM_inv(ord);4470al2 = shallowcopy(al);44714472gel(al2,7) = RgM_mul(gel(al2,7), ord);44734474gel(al2,8) = RgM_mul(iord, gel(al,8));44754476mt = cgetg(n+1,t_VEC);4477gel(mt,1) = matid(n);4478for (i=2; i<=n; i++) {4479mtx = algbasismultable(al,gel(ord,i));4480gel(mt,i) = RgM_mul(iord, RgM_mul(mtx, ord));4481}4482gel(al2,9) = mt;44834484gel(al2,11) = algtracebasis(al2);44854486return gerepilecopy(av,al2);4487}44884489static GEN4490mattocol(GEN M, long n)4491{4492GEN C = cgetg(n*n+1, t_COL);4493long i,j,ic;4494ic = 1;4495for (i=1; i<=n; i++)4496for (j=1; j<=n; j++, ic++) gel(C,ic) = gcoeff(M,i,j);4497return C;4498}44994500/* Ip is a lift of a left O/pO-ideal where O is the integral basis of al */4501static GEN4502algleftordermodp(GEN al, GEN Ip, GEN p)4503{4504pari_sp av = avma;4505GEN I, Ii, M, mt, K, imi, p2;4506long n, i;4507n = alg_get_absdim(al);4508mt = alg_get_multable(al);4509p2 = sqri(p);45104511I = ZM_hnfmodid(Ip, p);4512Ii = ZM_inv(I,NULL);45134514M = cgetg(n+1, t_MAT);4515for (i=1; i<=n; i++) {4516imi = FpM_mul(Ii, FpM_mul(gel(mt,i), I, p2), p2);4517imi = ZM_Z_divexact(imi, p);4518gel(M,i) = mattocol(imi, n);4519}4520K = FpM_ker(M, p);4521if (lg(K)==1) { set_avma(av); return matid(n); }4522K = ZM_hnfmodid(K,p);45234524return gerepileupto(av, ZM_Z_div(K,p));4525}45264527static GEN4528alg_ordermodp(GEN al, GEN p)4529{4530GEN alp;4531long i, N = alg_get_absdim(al);4532alp = cgetg(12, t_VEC);4533for (i=1; i<=8; i++) gel(alp,i) = gen_0;4534gel(alp,9) = cgetg(N+1, t_VEC);4535for (i=1; i<=N; i++) gmael(alp,9,i) = FpM_red(gmael(al,9,i), p);4536gel(alp,10) = p;4537gel(alp,11) = cgetg(N+1, t_VEC);4538for (i=1; i<=N; i++) gmael(alp,11,i) = Fp_red(gmael(al,11,i), p);45394540return alp;4541}45424543static GEN4544algpradical_i(GEN al, GEN p, GEN zprad, GEN projs)4545{4546pari_sp av = avma;4547GEN alp = alg_ordermodp(al, p), liftrad, projrad, alq, alrad, res, Lalp, radq;4548long i;4549if (lg(zprad)==1) {4550liftrad = NULL;4551projrad = NULL;4552}4553else {4554alq = alg_quotient(alp, zprad, 1);4555alp = gel(alq,1);4556projrad = gel(alq,2);4557liftrad = gel(alq,3);4558}45594560if (projs) {4561if (projrad) {4562projs = gcopy(projs);4563for (i=1; i<lg(projs); i++)4564gel(projs,i) = FpM_FpC_mul(projrad, gel(projs,i), p);4565}4566Lalp = alg_centralproj(alp, projs, 1);45674568alrad = cgetg(lg(Lalp),t_VEC);4569for (i=1; i<lg(Lalp); i++) {4570alq = gel(Lalp,i);4571radq = algradical(gel(alq,1));4572if (gequal0(radq))4573gel(alrad,i) = cgetg(1,t_MAT);4574else {4575radq = FpM_mul(gel(alq,3),radq,p);4576gel(alrad,i) = radq;4577}4578}4579alrad = shallowmatconcat(alrad);4580alrad = FpM_image(alrad,p);4581}4582else alrad = algradical(alp);45834584if (!gequal0(alrad)) {4585if (liftrad) alrad = FpM_mul(liftrad, alrad, p);4586res = shallowmatconcat(mkvec2(alrad, zprad));4587res = FpM_image(res,p);4588}4589else res = lg(zprad)==1 ? gen_0 : zprad;4590return gerepilecopy(av, res);4591}45924593static GEN4594algpdecompose0(GEN al, GEN prad, GEN p, GEN projs)4595{4596pari_sp av = avma;4597GEN alp, quo, ss, liftm = NULL, projm = NULL, dec, res, I, Lss, deci;4598long i, j;45994600alp = alg_ordermodp(al, p);4601if (!gequal0(prad)) {4602quo = alg_quotient(alp, prad, 1);4603ss = gel(quo,1);4604projm = gel(quo,2);4605liftm = gel(quo,3);4606}4607else ss = alp;46084609if (projs) {4610if (projm) {4611for (i=1; i<lg(projs); i++)4612gel(projs,i) = FpM_FpC_mul(projm, gel(projs,i), p);4613}4614Lss = alg_centralproj(ss, projs, 1);46154616dec = cgetg(lg(Lss),t_VEC);4617for (i=1; i<lg(Lss); i++) {4618gel(dec,i) = algsimpledec_ss(gmael(Lss,i,1), 1);4619deci = gel(dec,i);4620for (j=1; j<lg(deci); j++)4621gmael(deci,j,3) = FpM_mul(gmael(Lss,i,3), gmael(deci,j,3), p);4622}4623dec = shallowconcat1(dec);4624}4625else dec = algsimpledec_ss(ss,1);46264627res = cgetg(lg(dec),t_VEC);4628for (i=1; i<lg(dec); i++) {4629I = gmael(dec,i,3);4630if (liftm) I = FpM_mul(liftm,I,p);4631I = shallowmatconcat(mkvec2(I,prad));4632gel(res,i) = I;4633}46344635return gerepilecopy(av, res);4636}46374638/* finds a nontrivial ideal of O/prad or gen_0 if there is none. */4639static GEN4640algpdecompose_i(GEN al, GEN p, GEN zprad, GEN projs)4641{4642pari_sp av = avma;4643GEN prad = algpradical_i(al,p,zprad,projs);4644return gerepileupto(av, algpdecompose0(al, prad, p, projs));4645}46464647/* ord is assumed to be in hnf wrt the integral basis of al. */4648/* assumes that alg_get_invbasis(al) is integral. */4649static GEN4650alg_change_overorder_shallow(GEN al, GEN ord)4651{4652GEN al2, mt, iord, mtx, den, den2, div;4653long i, n;4654n = alg_get_absdim(al);46554656iord = QM_inv(ord);4657al2 = shallowcopy(al);4658ord = Q_remove_denom(ord,&den);46594660gel(al2,7) = Q_remove_denom(gel(al,7), &den2);4661if (den2) div = mulii(den,den2);4662else div = den;4663gel(al2,7) = ZM_Z_div(ZM_mul(gel(al2,7), ord), div);46644665gel(al2,8) = ZM_mul(iord, gel(al,8));46664667mt = cgetg(n+1,t_VEC);4668gel(mt,1) = matid(n);4669div = sqri(den);4670for (i=2; i<=n; i++) {4671mtx = algbasismultable(al,gel(ord,i));4672gel(mt,i) = ZM_mul(iord, ZM_mul(mtx, ord));4673gel(mt,i) = ZM_Z_divexact(gel(mt,i), div);4674}4675gel(al2,9) = mt;46764677gel(al2,11) = algtracebasis(al2);46784679return al2;4680}46814682static GEN4683algfromcenter(GEN al, GEN x)4684{4685GEN nf = alg_get_center(al);4686long n;4687switch(alg_type(al)) {4688case al_CYCLIC:4689n = alg_get_degree(al);4690break;4691case al_CSA:4692n = alg_get_dim(al);4693break;4694default:4695return NULL; /*LCOV_EXCL_LINE*/4696}4697return algalgtobasis(al, scalarcol(basistoalg(nf, x), n));4698}46994700/* x is an ideal of the center in hnf form */4701static GEN4702algfromcenterhnf(GEN al, GEN x)4703{4704GEN res;4705long i;4706res = cgetg(lg(x), t_MAT);4707for (i=1; i<lg(x); i++) gel(res,i) = algfromcenter(al, gel(x,i));4708return res;4709}47104711/* assumes al is CSA or CYCLIC */4712static GEN4713algcenter_precompute(GEN al, GEN p)4714{4715GEN fa, pdec, nfprad, projs, nf = alg_get_center(al);4716long i, np;47174718pdec = idealprimedec(nf, p);4719settyp(pdec, t_COL);4720np = lg(pdec)-1;4721fa = mkmat2(pdec, const_col(np, gen_1));4722if (dvdii(nf_get_disc(nf), p))4723nfprad = idealprodprime(nf, pdec);4724else4725nfprad = scalarmat_shallow(p, nf_get_degree(nf));4726fa = idealchineseinit(nf, fa);4727projs = cgetg(np+1, t_VEC);4728for (i=1; i<=np; i++) gel(projs, i) = idealchinese(nf, fa, vec_ei(np,i));4729return mkvec2(nfprad, projs);4730}47314732static GEN4733algcenter_prad(GEN al, GEN p, GEN pre)4734{4735GEN nfprad, zprad, mtprad;4736long i;4737nfprad = gel(pre,1);4738zprad = algfromcenterhnf(al, nfprad);4739zprad = FpM_image(zprad, p);4740mtprad = cgetg(lg(zprad), t_VEC);4741for (i=1; i<lg(zprad); i++) gel(mtprad, i) = algbasismultable(al, gel(zprad,i));4742mtprad = shallowmatconcat(mtprad);4743zprad = FpM_image(mtprad, p);4744return zprad;4745}47464747static GEN4748algcenter_p_projs(GEN al, GEN p, GEN pre)4749{4750GEN projs, zprojs;4751long i;4752projs = gel(pre,2);4753zprojs = cgetg(lg(projs), t_VEC);4754for (i=1; i<lg(projs); i++) gel(zprojs,i) = FpC_red(algfromcenter(al, gel(projs,i)),p);4755return zprojs;4756}47574758/* al is assumed to be simple */4759static GEN4760alg_pmaximal(GEN al, GEN p)4761{4762GEN al2, prad, lord = gen_0, I, id, dec, zprad, projs, pre;4763long n, i;4764n = alg_get_absdim(al);4765id = matid(n);4766al2 = al;47674768dbg_printf(0)("Round 2 (noncommutative) at p=%Ps, dim=%d\n", p, n);47694770pre = algcenter_precompute(al,p);47714772while (1) {4773zprad = algcenter_prad(al2, p, pre);4774projs = algcenter_p_projs(al2, p, pre);4775if (lg(projs) == 2) projs = NULL;4776prad = algpradical_i(al2,p,zprad,projs);4777if (typ(prad) == t_INT) break;4778lord = algleftordermodp(al2,prad,p);4779if (!cmp_universal(lord,id)) break;4780al2 = alg_change_overorder_shallow(al2,lord);4781}47824783dec = algpdecompose0(al2,prad,p,projs);4784while (lg(dec)>2) {4785for (i=1; i<lg(dec); i++) {4786I = gel(dec,i);4787lord = algleftordermodp(al2,I,p);4788if (cmp_universal(lord,matid(n))) break;4789}4790if (i==lg(dec)) break;4791al2 = alg_change_overorder_shallow(al2,lord);4792zprad = algcenter_prad(al2, p, pre);4793projs = algcenter_p_projs(al2, p, pre);4794if (lg(projs) == 2) projs = NULL;4795dec = algpdecompose_i(al2,p,zprad,projs);4796}4797return al2;4798}47994800static GEN4801algtracematrix(GEN al)4802{4803GEN M, mt;4804long n, i, j;4805n = alg_get_absdim(al);4806mt = alg_get_multable(al);4807M = cgetg(n+1, t_MAT);4808for (i=1; i<=n; i++)4809{4810gel(M,i) = cgetg(n+1,t_MAT);4811for (j=1; j<=i; j++)4812gcoeff(M,j,i) = gcoeff(M,i,j) = algabstrace(al,gmael(mt,i,j));4813}4814return M;4815}4816static GEN4817algdisc_i(GEN al) { return ZM_det(algtracematrix(al)); }4818GEN4819algdisc(GEN al)4820{4821pari_sp av = avma;4822checkalg(al); return gerepileuptoint(av, algdisc_i(al));4823}4824static GEN4825alg_maximal(GEN al)4826{4827GEN fa = absZ_factor(algdisc_i(al));4828return alg_maximal_primes(al, gel(fa,1));4829}48304831/** LATTICES **/48324833/*4834Convention: lattice = [I,t] representing t*I, where4835- I integral nonsingular upper-triangular matrix representing a lattice over4836the integral basis of the algebra, and4837- t>0 either an integer or a rational number.48384839Recommended and returned by the functions below:4840- I HNF and primitive4841*/48424843/* TODO use hnfmodid whenever possible using a*O <= I <= O4844* for instance a = ZM_det_triangular(I) */48454846static GEN4847primlat(GEN lat)4848{4849GEN m, t, c;4850m = alglat_get_primbasis(lat);4851t = alglat_get_scalar(lat);4852m = Q_primitive_part(m,&c);4853if (c) return mkvec2(m,gmul(t,c));4854return lat;4855}48564857/* assumes the lattice contains d * integral basis, d=0 allowed */4858GEN4859alglathnf(GEN al, GEN m, GEN d)4860{4861pari_sp av = avma;4862long N,i,j;4863GEN m2, c;4864checkalg(al);4865N = alg_get_absdim(al);4866if (!d) d = gen_0;4867if (typ(m) == t_VEC) m = matconcat(m);4868if (typ(m) == t_COL) m = algleftmultable(al,m);4869if (typ(m) != t_MAT) pari_err_TYPE("alglathnf",m);4870if (typ(d) != t_FRAC && typ(d) != t_INT) pari_err_TYPE("alglathnf",d);4871if (lg(m)-1 < N || lg(gel(m,1))-1 != N) pari_err_DIM("alglathnf");4872for (i=1; i<=N; i++)4873for (j=1; j<lg(m); j++)4874if (typ(gcoeff(m,i,j)) != t_FRAC && typ(gcoeff(m,i,j)) != t_INT)4875pari_err_TYPE("alglathnf", gcoeff(m,i,j));4876m2 = Q_primitive_part(m,&c);4877if (!c) c = gen_1;4878if (!signe(d)) d = detint(m2);4879else d = gdiv(d,c); /* should be an integer */4880if (!signe(d)) pari_err_INV("alglathnf [m does not have full rank]", m2);4881m2 = ZM_hnfmodid(m2,d);4882return gerepilecopy(av, mkvec2(m2,c));4883}48844885static GEN4886prepare_multipliers(GEN *a, GEN *b)4887{4888GEN na, nb, da, db, d;4889na = numer_i(*a); da = denom_i(*a);4890nb = numer_i(*b); db = denom_i(*b);4891na = mulii(na,db);4892nb = mulii(nb,da);4893d = gcdii(na,nb);4894*a = diviiexact(na,d);4895*b = diviiexact(nb,d);4896return gdiv(d, mulii(da,db));4897}48984899static GEN4900prepare_lat(GEN m1, GEN t1, GEN m2, GEN t2)4901{4902GEN d = prepare_multipliers(&t1, &t2);4903m1 = ZM_Z_mul(m1,t1);4904m2 = ZM_Z_mul(m2,t2);4905return mkvec3(m1,m2,d);4906}49074908static GEN4909alglataddinter(GEN al, GEN lat1, GEN lat2, GEN *sum, GEN *inter)4910{4911GEN d, m1, m2, t1, t2, M, prep, d1, d2, ds, di, K;4912checkalg(al);4913checklat(al,lat1);4914checklat(al,lat2);49154916m1 = alglat_get_primbasis(lat1);4917t1 = alglat_get_scalar(lat1);4918m2 = alglat_get_primbasis(lat2);4919t2 = alglat_get_scalar(lat2);4920prep = prepare_lat(m1, t1, m2, t2);4921m1 = gel(prep,1);4922m2 = gel(prep,2);4923d = gel(prep,3);4924M = matconcat(mkvec2(m1,m2));4925d1 = ZM_det_triangular(m1);4926d2 = ZM_det_triangular(m2);4927ds = gcdii(d1,d2);4928if (inter)4929{4930di = diviiexact(mulii(d1,d2),ds);4931K = matkermod(M,di,sum);4932K = rowslice(K,1,lg(m1));4933*inter = hnfmodid(FpM_mul(m1,K,di),di);4934if (sum) *sum = hnfmodid(*sum,ds);4935}4936else *sum = hnfmodid(M,ds);4937return d;4938}49394940GEN4941alglatinter(GEN al, GEN lat1, GEN lat2, GEN* ptsum)4942{4943pari_sp av = avma;4944GEN inter, d;4945d = alglataddinter(al, lat1, lat2, ptsum, &inter);4946inter = primlat(mkvec2(inter, d));4947if (ptsum)4948{4949*ptsum = primlat(mkvec2(*ptsum,d));4950gerepileall(av, 2, &inter, ptsum);4951}4952else inter = gerepilecopy(av, inter);4953return inter;4954}49554956GEN4957alglatadd(GEN al, GEN lat1, GEN lat2, GEN* ptinter)4958{4959pari_sp av = avma;4960GEN sum, d;4961d = alglataddinter(al, lat1, lat2, &sum, ptinter);4962sum = primlat(mkvec2(sum, d));4963if (ptinter)4964{4965*ptinter = primlat(mkvec2(*ptinter,d));4966gerepileall(av, 2, &sum, ptinter);4967}4968else sum = gerepilecopy(av, sum);4969return sum;4970}49714972int4973alglatsubset(GEN al, GEN lat1, GEN lat2, GEN* ptindex)4974{4975/* TODO version that returns the quotient as abelian group? */4976/* return matrices to convert coordinates from one to other? */4977pari_sp av = avma;4978int res;4979GEN m1, m2, m2i, m, t;4980checkalg(al);4981checklat(al,lat1);4982checklat(al,lat2);4983m1 = alglat_get_primbasis(lat1);4984m2 = alglat_get_primbasis(lat2);4985m2i = RgM_inv_upper(m2);4986t = gdiv(alglat_get_scalar(lat1), alglat_get_scalar(lat2));4987m = RgM_Rg_mul(RgM_mul(m2i,m1), t);4988res = RgM_is_ZM(m);4989if (res && ptindex)4990{4991*ptindex = mpabs(ZM_det_triangular(m));4992gerepileall(av,1,ptindex);4993}4994else set_avma(av);4995return res;4996}49974998GEN4999alglatindex(GEN al, GEN lat1, GEN lat2)5000{5001pari_sp av = avma;5002long N;5003GEN res;5004checkalg(al);5005checklat(al,lat1);5006checklat(al,lat2);5007N = alg_get_absdim(al);5008res = alglat_get_scalar(lat1);5009res = gdiv(res, alglat_get_scalar(lat2));5010res = gpowgs(res, N);5011res = gmul(res,RgM_det_triangular(alglat_get_primbasis(lat1)));5012res = gdiv(res, RgM_det_triangular(alglat_get_primbasis(lat2)));5013res = gabs(res,0);5014return gerepilecopy(av, res);5015}50165017GEN5018alglatmul(GEN al, GEN lat1, GEN lat2)5019{5020pari_sp av = avma;5021long N,i;5022GEN m1, m2, m, V, lat, t, d, dp;5023checkalg(al);5024if (typ(lat1)==t_COL)5025{5026if (typ(lat2)==t_COL)5027pari_err_TYPE("alglatmul [one of lat1, lat2 has to be a lattice]", lat2);5028checklat(al,lat2);5029lat1 = Q_remove_denom(lat1,&d);5030m = algbasismultable(al,lat1);5031m2 = alglat_get_primbasis(lat2);5032dp = mulii(detint(m),ZM_det_triangular(m2));5033m = ZM_mul(m,m2);5034t = alglat_get_scalar(lat2);5035if (d) t = gdiv(t,d);5036}5037else /* typ(lat1)!=t_COL */5038{5039checklat(al,lat1);5040if (typ(lat2)==t_COL)5041{5042lat2 = Q_remove_denom(lat2,&d);5043m = algbasisrightmultable(al,lat2);5044m1 = alglat_get_primbasis(lat1);5045dp = mulii(detint(m),ZM_det_triangular(m1));5046m = ZM_mul(m,m1);5047t = alglat_get_scalar(lat1);5048if (d) t = gdiv(t,d);5049}5050else /* typ(lat2)!=t_COL */5051{5052checklat(al,lat2);5053N = alg_get_absdim(al);5054m1 = alglat_get_primbasis(lat1);5055m2 = alglat_get_primbasis(lat2);5056dp = mulii(ZM_det_triangular(m1), ZM_det_triangular(m2));5057V = cgetg(N+1,t_VEC);5058for (i=1; i<=N; i++) {5059gel(V,i) = algbasismultable(al,gel(m1,i));5060gel(V,i) = ZM_mul(gel(V,i),m2);5061}5062m = matconcat(V);5063t = gmul(alglat_get_scalar(lat1), alglat_get_scalar(lat2));5064}5065}50665067lat = alglathnf(al,m,dp);5068gel(lat,2) = gmul(alglat_get_scalar(lat), t);5069lat = primlat(lat);5070return gerepilecopy(av, lat);5071}50725073int5074alglatcontains(GEN al, GEN lat, GEN x, GEN *ptc)5075{5076pari_sp av = avma;5077GEN m, t, sol;5078checkalg(al);5079checklat(al,lat);5080m = alglat_get_primbasis(lat);5081t = alglat_get_scalar(lat);5082x = RgC_Rg_div(x,t);5083if (!RgV_is_ZV(x)) return gc_bool(av,0);5084sol = hnf_solve(m,x);5085if (!sol) return gc_bool(av,0);5086if (!ptc) return gc_bool(av,1);5087*ptc = sol; gerepileall(av,1,ptc); return 1;5088}50895090GEN5091alglatelement(GEN al, GEN lat, GEN c)5092{5093pari_sp av = avma;5094GEN res;5095checkalg(al);5096checklat(al,lat);5097if (typ(c)!=t_COL) pari_err_TYPE("alglatelement", c);5098res = ZM_ZC_mul(alglat_get_primbasis(lat),c);5099res = RgC_Rg_mul(res, alglat_get_scalar(lat));5100return gerepilecopy(av,res);5101}51025103/* idem QM_invimZ, knowing result is contained in 1/c*Z^n */5104static GEN5105QM_invimZ_mod(GEN m, GEN c)5106{5107GEN d, m0, K;5108m0 = Q_remove_denom(m, &d);5109if (d) d = mulii(d,c);5110else d = c;5111K = matkermod(m0, d, NULL);5112if (lg(K)==1) K = scalarmat(d, lg(m)-1);5113else K = hnfmodid(K, d);5114return RgM_Rg_div(K,c);5115}51165117/* If m is injective, computes a Z-basis of the submodule of elements whose5118* image under m is integral */5119static GEN5120QM_invimZ(GEN m)5121{5122return RgM_invimage(m, QM_ImQ_hnf(m));5123}51245125/* An isomorphism of R-modules M_{m,n}(R) -> R^{m*n} */5126static GEN5127mat2col(GEN M, long m, long n)5128{5129long i,j,k,p;5130GEN C;5131p = m*n;5132C = cgetg(p+1,t_COL);5133for (i=1,k=1;i<=m;i++)5134for (j=1;j<=n;j++,k++)5135gel(C,k) = gcoeff(M,i,j);5136return C;5137}51385139static GEN5140alglattransporter_i(GEN al, GEN lat1, GEN lat2, long right)5141{5142GEN m1, m2, m2i, M, MT, mt, t1, t2, T, c;5143long N, i;5144N = alg_get_absdim(al);5145m1 = alglat_get_primbasis(lat1);5146m2 = alglat_get_primbasis(lat2);5147m2i = RgM_inv_upper(m2);5148c = detint(m1);5149t1 = alglat_get_scalar(lat1);5150m1 = RgM_Rg_mul(m1,t1);5151t2 = alglat_get_scalar(lat2);5152m2i = RgM_Rg_div(m2i,t2);51535154MT = right? NULL: alg_get_multable(al);5155M = cgetg(N+1, t_MAT);5156for (i=1; i<=N; i++) {5157if (right) mt = algbasisrightmultable(al, vec_ei(N,i));5158else mt = gel(MT,i);5159mt = RgM_mul(m2i,mt);5160mt = RgM_mul(mt,m1);5161gel(M,i) = mat2col(mt, N, N);5162}51635164c = gdiv(t2,gmul(c,t1));5165c = denom_i(c);5166T = QM_invimZ_mod(M,c);5167return primlat(mkvec2(T,gen_1));5168}51695170/*5171{ x in al | x*lat1 subset lat2}5172*/5173GEN5174alglatlefttransporter(GEN al, GEN lat1, GEN lat2)5175{5176pari_sp av = avma;5177checkalg(al);5178checklat(al,lat1);5179checklat(al,lat2);5180return gerepilecopy(av, alglattransporter_i(al,lat1,lat2,0));5181}51825183/*5184{ x in al | lat1*x subset lat2}5185*/5186GEN5187alglatrighttransporter(GEN al, GEN lat1, GEN lat2)5188{5189pari_sp av = avma;5190checkalg(al);5191checklat(al,lat1);5192checklat(al,lat2);5193return gerepilecopy(av, alglattransporter_i(al,lat1,lat2,1));5194}51955196GEN5197algmakeintegral(GEN mt0, long maps)5198{5199pari_sp av = avma;5200long n,i;5201GEN m,P,Pi,mt2,mt;5202n = lg(mt0)-1;5203mt = check_mt(mt0,NULL);5204if (!mt) pari_err_TYPE("algmakeintegral", mt0);5205if (isint1(Q_denom(mt0))) {5206if (maps) mt = mkvec3(mt,matid(n),matid(n));5207return gerepilecopy(av,mt);5208}5209dbg_printf(2)(" algmakeintegral: dim=%d, denom=%Ps\n", n, Q_denom(mt0));5210m = cgetg(n+1,t_MAT);5211for (i=1;i<=n;i++)5212gel(m,i) = mat2col(gel(mt,i),n,n);5213dbg_printf(2)(" computing order, dims m = %d x %d...\n", nbrows(m), lg(m)-1);5214P = QM_invimZ(m);5215dbg_printf(2)(" ...done.\n");5216P = shallowmatconcat(mkvec2(col_ei(n,1),P));5217P = hnf(P);5218Pi = RgM_inv(P);5219mt2 = change_Rgmultable(mt,P,Pi);5220if (maps) mt2 = mkvec3(mt2,Pi,P); /* mt2, mt->mt2, mt2->mt */5221return gerepilecopy(av,mt2);5222}52235224/** ORDERS **/52255226/** IDEALS **/5227522852295230