#include "pari.h"
#include "paripriv.h"
int
checkznstar_i(GEN G)
{
return (typ(G) == t_VEC && lg(G) == 6
&& typ(znstar_get_faN(G)) == t_VEC
&& typ(gel(G,1)) == t_VEC && lg(gel(G,1)) == 3);
}
int
char_check(GEN cyc, GEN chi)
{ return typ(chi) == t_VEC && lg(chi) == lg(cyc) && RgV_is_ZV(chi); }
GEN
cyc_normalize(GEN d)
{
long i, l = lg(d);
GEN C, D;
if (l == 1) return mkvec(gen_1);
D = cgetg(l, t_VEC); gel(D,1) = C = gel(d,1);
for (i = 2; i < l; i++) gel(D,i) = diviiexact(C, gel(d,i));
return D;
}
GEN
char_simplify(GEN D, GEN C)
{
GEN d = D;
if (lg(C) == 1) d = gen_1;
else
{
GEN t = gcdii(d, ZV_content(C));
if (!equali1(t))
{
long tc = typ(C);
C = ZC_Z_divexact(C, t); settyp(C, tc);
d = diviiexact(d, t);
}
}
return mkvec2(d,C);
}
GEN
char_normalize(GEN chi, GEN ncyc)
{
long i, l = lg(chi);
GEN c = cgetg(l, t_VEC);
if (l > 1) {
gel(c,1) = gel(chi,1);
for (i = 2; i < l; i++) gel(c,i) = mulii(gel(chi,i), gel(ncyc,i));
}
return char_simplify(gel(ncyc,1), c);
}
static GEN
get_cyc(GEN x, GEN chi, const char *s)
{
if (nftyp(x) == typ_BIDZ)
{
if (!zncharcheck(x, chi)) pari_err_TYPE(s, chi);
return NULL;
}
else
{
if (typ(x) != t_VEC || !RgV_is_ZV(x)) x = member_cyc(x);
if (!char_check(x, chi)) pari_err_TYPE(s, chi);
return x;
}
}
GEN
charconj(GEN cyc, GEN chi)
{
long i, l;
GEN z = cgetg_copy(chi, &l);
for (i = 1; i < l; i++)
{
GEN c = gel(chi,i);
gel(z,i) = signe(c)? subii(gel(cyc,i), c): gen_0;
}
return z;
}
GEN
charconj0(GEN x, GEN chi)
{
GEN cyc = get_cyc(x, chi, "charconj");
return cyc? charconj(cyc, chi): zncharconj(x, chi);
}
GEN
charorder(GEN cyc, GEN x)
{
pari_sp av = avma;
long i, l = lg(cyc);
GEN f = gen_1;
for (i = 1; i < l; i++)
if (signe(gel(x,i)))
{
GEN c, o = gel(cyc,i);
c = gcdii(o, gel(x,i));
if (!is_pm1(c)) o = diviiexact(o,c);
f = lcmii(f, o);
}
return gerepileuptoint(av, f);
}
GEN
charorder0(GEN x, GEN chi)
{
GEN cyc = get_cyc(x, chi, "charorder");
return cyc? charorder(cyc, chi): zncharorder(x, chi);
}
GEN
charker(GEN cyc, GEN chi)
{
long i, l = lg(cyc);
GEN nchi, ncyc, m, U;
if (l == 1) return cgetg(1,t_MAT);
ncyc = cyc_normalize(cyc);
nchi = char_normalize(chi, ncyc);
m = shallowconcat(gel(nchi,2), gel(nchi,1));
U = gel(ZV_extgcd(m), 2); setlg(U,l);
for (i = 1; i < l; i++) setlg(U[i], l);
return hnfmodid(U, gel(ncyc,1));
}
GEN
charker0(GEN x, GEN chi)
{
GEN cyc = get_cyc(x, chi, "charker");
return cyc? charker(cyc, chi): zncharker(x, chi);
}
GEN
charpow(GEN cyc, GEN a, GEN N)
{
long i, l;
GEN v = cgetg_copy(a, &l);
for (i = 1; i < l; i++) gel(v,i) = Fp_mul(gel(a,i), N, gel(cyc,i));
return v;
}
GEN
charmul(GEN cyc, GEN a, GEN b)
{
long i, l;
GEN v = cgetg_copy(a, &l);
for (i = 1; i < l; i++) gel(v,i) = Fp_add(gel(a,i), gel(b,i), gel(cyc,i));
return v;
}
GEN
chardiv(GEN cyc, GEN a, GEN b)
{
long i, l;
GEN v = cgetg_copy(a, &l);
for (i = 1; i < l; i++) gel(v,i) = Fp_sub(gel(a,i), gel(b,i), gel(cyc,i));
return v;
}
GEN
charpow0(GEN x, GEN a, GEN N)
{
GEN cyc = get_cyc(x, a, "charpow");
return cyc? charpow(cyc, a, N): zncharpow(x, a, N);
}
GEN
charmul0(GEN x, GEN a, GEN b)
{
const char *s = "charmul";
GEN cyc = get_cyc(x, a, s);
if (!cyc)
{
if (!zncharcheck(x, b)) pari_err_TYPE(s, b);
return zncharmul(x, a, b);
}
else
{
if (!char_check(cyc, b)) pari_err_TYPE(s, b);
return charmul(cyc, a, b);
}
}
GEN
chardiv0(GEN x, GEN a, GEN b)
{
const char *s = "chardiv";
GEN cyc = get_cyc(x, a, s);
if (!cyc)
{
if (!zncharcheck(x, b)) pari_err_TYPE(s, b);
return znchardiv(x, a, b);
}
else
{
if (!char_check(cyc, b)) pari_err_TYPE(s, b);
return chardiv(cyc, a, b);
}
}
static GEN
chareval_i(GEN nchi, GEN dlog, GEN z)
{
GEN o, q, r, b = gel(nchi,1);
GEN a = FpV_dotproduct(gel(nchi,2), dlog, b);
if (!z) return gdiv(a,b);
if (typ(z) == t_INT)
{
q = dvmdii(z, b, &r);
if (signe(r)) pari_err_TYPE("chareval", z);
return mulii(a, q);
}
if (typ(z) != t_VEC || lg(z) != 3) pari_err_TYPE("chareval", z);
o = gel(z,2); if (typ(o) != t_INT) pari_err_TYPE("chareval", z);
q = dvmdii(o, b, &r); if (signe(r)) pari_err_TYPE("chareval", z);
q = mulii(a, q);
z = gel(z,1);
if (typ(z) == t_VEC)
{
if (itos_or_0(o) != lg(z)-1) pari_err_TYPE("chareval", z);
return gcopy(gel(z, itos(q)+1));
}
else
return gpow(z, q, DEFAULTPREC);
}
static GEN
not_coprime(GEN z)
{ return (!z || typ(z) == t_INT)? gen_m1: gen_0; }
static GEN
get_chi(GEN cyc, GEN chi)
{
if (!char_check(cyc,chi)) pari_err_TYPE("chareval", chi);
return char_normalize(chi, cyc_normalize(cyc));
}
static int
bnr_coprime(GEN G, GEN x)
{
GEN t, N = gel(bnr_get_mod(G), 1);
if (typ(x) == t_INT)
{
t = gcdii(gcoeff(N,1,1), x);
if (equali1(t)) return 1;
t = idealadd(G, N, x);
return equali1(gcoeff(t,1,1));
}
x = idealnumden(G, x);
t = idealadd(G, N, gel(x,1));
if (!equali1(gcoeff(t,1,1))) return 0;
t = idealadd(G, N, gel(x,2));
return equali1(gcoeff(t,1,1));
}
GEN
chareval(GEN G, GEN chi, GEN x, GEN z)
{
pari_sp av = avma;
GEN nchi, L;
switch(nftyp(G))
{
case typ_BNR:
if (!bnr_coprime(G, x)) return not_coprime(z);
L = isprincipalray(G, x);
nchi = get_chi(bnr_get_cyc(G), chi);
break;
case typ_BNF:
L = isprincipal(G, x);
nchi = get_chi(bnf_get_cyc(G), chi);
break;
case typ_BIDZ:
if (checkznstar_i(G)) return gerepileupto(av, znchareval(G, chi, x, z));
default:
pari_err_TYPE("chareval", G);
return NULL;
}
return gerepileupto(av, chareval_i(nchi, L, z));
}
GEN
ncharvecexpo(GEN G, GEN nchi)
{
long N = itou(znstar_get_N(G)), ord = itou(gel(nchi,1)), i, j, l;
GEN cyc, gen, d, t, t1, t2, t3, e, u, u1, u2, u3;
GEN D = gel(nchi,2), v = const_vecsmall(N,-1);
pari_sp av = avma;
if (typ(D) == t_COL) {
cyc = znstar_get_conreycyc(G);
gen = znstar_get_conreygen(G);
} else {
cyc = znstar_get_cyc(G);
gen = znstar_get_gen(G);
}
l = lg(cyc);
e = u = cgetg(N+1,t_VECSMALL);
d = t = cgetg(N+1,t_VECSMALL);
*++d = 1;
*++e = 0; v[*d] = *e;
for (i = 1; i < l; i++)
{
ulong g = itou(gel(gen,i)), c = itou(gel(cyc,i)), x = itou(gel(D,i));
for (t1=t,u1=u,j=c-1; j; j--,t1=t2,u1=u2)
for (t2=d,u2=e, t3=t1,u3=u1; t3<t2; )
{
*++d = Fl_mul(*++t3, g, N);
*++e = Fl_add(*++u3, x, ord); v[*d] = *e;
}
}
avma = av; return v;
}
static ulong
lcmuu(ulong a, ulong b) { return (a/ugcd(a,b)) * b; }
static ulong
zv_charorder(GEN cyc, GEN x)
{
long i, l = lg(cyc);
ulong f = 1;
for (i = 1; i < l; i++)
if (x[i])
{
ulong o = cyc[i];
f = lcmuu(f, o / ugcd(o, x[i]));
}
return f;
}
GEN
coprimes_zv(ulong N)
{
GEN v = const_vecsmall(N,1);
pari_sp av = avma;
GEN P = gel(factoru(N),1);
long i, l = lg(P);
for (i = 1; i < l; i++)
{
ulong p = P[i], j;
for (j = p; j <= N; j += p) v[j] = 0;
}
avma = av; return v;
}
long
zv_cyc_minimize(GEN cyc, GEN g, GEN coprime)
{
pari_sp av = avma;
long d, k, e, i, maxi, k0, bestk, l = lg(g), o = lg(coprime)-1;
GEN best, gk, gd;
ulong t;
if (o == 1) return 1;
for (i = 1; i < l; i++)
if (g[i]) break;
if (g[i] == 1) return 1;
k0 = Fl_invgen(g[i], cyc[i], &t);
d = cyc[i] / (long)t;
if (k0 > 1) g = vecmoduu(Flv_Fl_mul(g, k0, cyc[i]), cyc);
for (i++; i < l; i++)
if (g[i]) break;
if (i == l) return k0;
cyc = vecslice(cyc,i,l-1);
g = vecslice(g, i,l-1);
e = cyc[1];
gd = Flv_Fl_mul(g, d, e);
bestk = 1; best = g; maxi = e/ugcd(d,e);
for (gk = g, k = d+1, i = 1; i < maxi; k += d, i++)
{
long ko = k % o;
gk = Flv_add(gk, gd, e); if (!ko || !coprime[ko]) continue;
gk = vecmoduu(gk, cyc);
if (vecsmall_lexcmp(gk, best) < 0) { best = gk; bestk = k; }
}
avma = av; return bestk == 1? k0: Fl_mul(k0, bestk, o);
}
long
zv_cyc_minimal(GEN cyc, GEN g, GEN coprime)
{
pari_sp av = avma;
long i, maxi, d, k, e, l = lg(g), o = lg(coprime)-1;
GEN gd, gk;
if (o == 1) return 1;
for (k = 1; k < l; k++)
if (g[k]) break;
if (g[k] == 1) return 1;
if (cyc[k] % g[k]) return 0;
d = cyc[k] / g[k];
for (k++; k < l; k++)
if (g[k]) break;
if (k == l) return 1;
cyc = vecslice(cyc,k,l-1);
g = vecslice(g, k,l-1);
e = cyc[1];
gd = Flv_Fl_mul(g, d, e); maxi = e/ugcd(d,e);
for (gk = g, k = d+1, i = 1; i < maxi; i++, k += d)
{
long ko = k % o;
gk = Flv_add(gk, gd, e); if (!coprime[ko]) continue;
gk = vecmoduu(gk, cyc);
if (vecsmall_lexcmp(gk, g) < 0) { avma = av; return 0; }
}
avma = av; return 1;
}
static GEN
coprime_tables(long N)
{
GEN D = divisorsu(N), v = const_vec(N, NULL);
long i, l = lg(D);
for (i = 1; i < l; i++) gel(v, D[i]) = coprimes_zv(D[i]);
return v;
}
static GEN
cyc2elts_normal(GEN cyc, long maxord, GEN ORD)
{
long i, n, o, N, j = 1;
GEN z, vcoprime;
if (typ(cyc) != t_VECSMALL) cyc = gtovecsmall(cyc);
n = lg(cyc)-1;
if (n == 0) return cgetg(1, t_VEC);
N = zv_prod(cyc);
z = cgetg(N+1, t_VEC);
if (1 <= maxord && (!ORD|| zv_search(ORD,1)))
gel(z,j++) = zero_zv(n);
vcoprime = coprime_tables(cyc[1]);
for (i = n; i > 0; i--)
{
GEN cyc0 = vecslice(cyc,i+1,n), pre = zero_zv(i);
GEN D = divisorsu(cyc[i]), C = cyc2elts(cyc0);
long s, t, lD = lg(D), nC = lg(C)-1;
for (s = 1; s < lD-1; s++)
{
long o0 = D[lD-s];
if (o0 > maxord) continue;
pre[i] = D[s];
if (!ORD || zv_search(ORD,o0))
{
GEN c = vecsmall_concat(pre, zero_zv(n-i));
gel(z,j++) = c;
}
for (t = 1; t < nC; t++)
{
GEN chi0 = gel(C,t);
o = lcmuu(o0, zv_charorder(cyc0,chi0));
if (o <= maxord && (!ORD || zv_search(ORD,o)))
{
GEN c = vecsmall_concat(pre, chi0);
if (zv_cyc_minimal(cyc, c, gel(vcoprime,o))) gel(z,j++) = c;
}
}
}
}
setlg(z,j); return z;
}
GEN
chargalois(GEN G, GEN ORD)
{
pari_sp av = avma;
long maxord, i, l;
GEN v, cyc = (typ(G) == t_VEC && RgV_is_ZVpos(G))? G: member_cyc(G);
if (lg(cyc) == 1) retmkvec(cgetg(1,t_VEC));
maxord = itou(gel(cyc,1));
if (ORD && gequal0(ORD)) ORD = NULL;
if (ORD)
switch(typ(ORD))
{
long l;
case t_VEC:
ORD = ZV_to_zv(ORD);
case t_VECSMALL:
ORD = leafcopy(ORD);
vecsmall_sort(ORD);
l = lg(ORD);
if (l == 1) return cgetg(1, t_VECSMALL);
maxord = minss(maxord, ORD[l-1]);
break;
case t_INT:
maxord = minss(maxord, itos(ORD));
ORD = NULL;
break;
default: pari_err_TYPE("chargalois", ORD);
}
v = cyc2elts_normal(cyc, maxord, ORD); l = lg(v);
for(i = 1; i < l; i++) gel(v,i) = zv_to_ZV(gel(v,i));
return gerepileupto(av, v);
}
GEN
znstar0(GEN N, long flag)
{
GEN F = NULL, P, E, cyc, gen, mod, G;
long i, i0, l, nbprimes;
pari_sp av = avma;
if (flag && flag != 1) pari_err_FLAG("znstar");
if ((F = check_arith_all(N,"znstar")))
{
F = clean_Z_factor(F);
N = typ(N) == t_VEC? gel(N,1): factorback(F);
}
if (!signe(N))
{
if (flag) pari_err_IMPL("znstar(0,1)");
avma = av;
retmkvec3(gen_2, mkvec(gen_2), mkvec(gen_m1));
}
N = absi_shallow(N);
if (abscmpiu(N,2) <= 0)
{
G = mkvec3(gen_1, cgetg(1,t_VEC), cgetg(1,t_VEC));
if (flag)
{
GEN v = const_vec(6,cgetg(1,t_VEC));
gel(v,3) = cgetg(1,t_MAT);
F = equali1(N)? mkvec2(cgetg(1,t_COL),cgetg(1,t_VECSMALL))
: mkvec2(mkcol(gen_2), mkvecsmall(1));
G = mkvec5(mkvec2(N,mkvec(gen_0)), G, F, v, cgetg(1,t_MAT));
}
return gerepilecopy(av,G);
}
if (!F) F = Z_factor(N);
P = gel(F,1); nbprimes = lg(P)-1;
E = ZV_to_nv( gel(F,2) );
switch(mod8(N))
{
case 0:
P = shallowconcat(gen_2,P);
E = vecsmall_prepend(E, E[1]);
i = 2;
break;
case 4:
i = 1;
break;
case 2: case 6:
P = vecsplice(P,1);
E = vecsplice(E,1);
i = 0;
break;
default:
i = 0;
break;
}
l = lg(P);
cyc = cgetg(l,t_VEC);
gen = cgetg(l,t_VEC);
mod = cgetg(l,t_VEC);
if (i == 2)
{
long v2 = E[1];
GEN q = int2n(v2);
gel(cyc,1) = gen_2;
gel(gen,1) = subiu(q,1);
gel(mod,1) = q;
gel(cyc,2) = int2n(v2-2);
gel(gen,2) = utoipos(5);
gel(mod,2) = q;
i0 = 3;
}
else if (i == 1)
{
gel(cyc,1) = gen_2;
gel(gen,1) = utoipos(3);
gel(mod,1) = utoipos(4);
i0 = 2;
}
else
i0 = 1;
for (i = i0; i < l; i++)
{
long e = E[i];
GEN p = gel(P,i), q = powiu(p, e-1), Q = mulii(p, q);
gel(cyc,i) = subii(Q, q);
gel(gen,i) = pgener_Zp(p);
gel(mod,i) = Q;
}
if (nbprimes > 1)
for (i=1; i<l; i++)
{
GEN Q = gel(mod,i), g = gel(gen,i), qinv = Fp_inv(Q, diviiexact(N,Q));
g = addii(g, mulii(mulii(subsi(1,g),qinv),Q));
gel(gen,i) = modii(g, N);
}
if (!flag)
{
G = gen;
for (i=l-1; i>=2; i--)
{
GEN ci = gel(cyc,i), gi = gel(G,i);
long j;
for (j=i-1; j>=1; j--)
{
GEN cj = gel(cyc,j), gj, qj, v, d;
d = bezout(ci,cj,NULL,&v);
if (absequalii(ci, d)) continue;
if (absequalii(cj, d)) {
swap(gel(G,j),gel(G,i));
gi = gel(G,i);
swap(gel(cyc,j),gel(cyc,i));
ci = gel(cyc,i); continue;
}
qj = diviiexact(cj,d);
gel(cyc,j) = mulii(ci,qj);
gel(cyc,i) = d;
gj = gel(G,j);
gj = Fp_mul(gj, gi, N);
togglesign_safe(&v);
if (signe(v) < 0) v = modii(v,ci);
gel(G,i) = gi = Fp_mul(gi, Fp_pow(gj, mulii(qj, v), N), N);
gel(G,j) = gj;
ci = d; if (absequaliu(ci, 2)) break;
}
}
G = mkvec3(ZV_prod(cyc), cyc, FpV_to_mod(G,N));
}
else
{
GEN D, U, Ui, fao = cgetg(l, t_VEC), lo = cgetg(l, t_VEC);
F = mkvec2(P, E);
D = ZV_snf_group(cyc,&U,&Ui);
for (i = 1; i < l; i++)
{
GEN t = gen_0, p = gel(P,i), p_1 = subiu(p,1);
long e = E[i];
gel(fao,i) = get_arith_ZZM(p_1);
if (e >= 2 && !absequaliu(p,2))
{
GEN q = gel(mod,i), g = Fp_pow(gel(gen,i),p_1,q);
if (e == 2)
t = Fp_inv(diviiexact(subiu(g,1), p), p);
else
t = ginv(Qp_log(cvtop(g,p,e)));
}
gel(lo,i) = t;
}
G = cgetg(l, t_VEC);
for (i = 1; i < l; i++) gel(G,i) = FpV_factorback(gen, gel(Ui,i), N);
G = mkvec3(ZV_prod(D), D, G);
G = mkvec5(mkvec2(N,mkvec(gen_0)), G, F,
mkvecn(6,mod,fao,Ui,gen,cyc,lo), U);
}
return gerepilecopy(av, G);
}
GEN
znstar(GEN N) { return znstar0(N, 0); }
static GEN
Zideallog_2k(GEN h, GEN g, long e, GEN pe)
{
GEN a = Fp_log(h, g, int2n(e-2), pe);
if (typ(a) != t_INT) return NULL;
return a;
}
static GEN
Zideallog_pk(GEN h, GEN g, GEN p, long e, GEN pe, GEN ord, GEN lo)
{
GEN gp = (e == 1)? g: modii(g, p);
GEN hp = (e == 1)? h: modii(h, p);
GEN a = Fp_log(hp, gp, ord, p);
if (typ(a) != t_INT) return NULL;
if (e > 1)
{
GEN b, p_1 = gel(ord,1);
h = Fp_mul(h, Fp_pow(g, negi(a), pe), pe);
if (e == 2)
b = Fp_mul(diviiexact(subiu(h,1), p), lo, p);
else
b = padic_to_Q(gmul(Qp_log(cvtop(h, p, e)), lo));
a = addii(a, mulii(p_1, b));
}
return a;
}
int
znconrey_check(GEN cyc, GEN chi)
{ return typ(chi) == t_COL && lg(chi) == lg(cyc) && RgV_is_ZV(chi); }
int
zncharcheck(GEN G, GEN chi)
{
switch(typ(chi))
{
case t_INT: return 1;
case t_COL: return znconrey_check(znstar_get_conreycyc(G), chi);
case t_VEC: return char_check(znstar_get_cyc(G), chi);
}
return 0;
}
GEN
znconreyfromchar_normalized(GEN bid, GEN chi)
{
GEN nchi, U = znstar_get_U(bid);
long l = lg(chi);
if (l == 1) retmkvec2(gen_1,cgetg(1,t_VEC));
if (!RgV_is_ZV(chi) || lgcols(U) != l) pari_err_TYPE("lfunchiZ", chi);
nchi = char_normalize(chi, cyc_normalize(znstar_get_cyc(bid)));
gel(nchi,2) = ZV_ZM_mul(gel(nchi,2),U); return nchi;
}
GEN
znconreyfromchar(GEN bid, GEN chi)
{
GEN nchi = znconreyfromchar_normalized(bid, chi);
GEN v = char_denormalize(znstar_get_conreycyc(bid), gel(nchi,1), gel(nchi,2));
settyp(v, t_COL); return v;
}
GEN
znconreylog(GEN bid, GEN x)
{
pari_sp av = avma;
GEN N, L, F, P,E, y, pe, fao, gen, lo, cycg;
long i, l;
if (!checkznstar_i(bid)) pari_err_TYPE("znconreylog", bid);
N = znstar_get_N(bid);
if (typ(N) != t_INT) pari_err_TYPE("znconreylog", N);
if (abscmpiu(N, 2) <= 0) return cgetg(1, t_COL);
cycg = znstar_get_conreycyc(bid);
switch(typ(x))
{
GEN Ui;
case t_INT:
if (!signe(x)) pari_err_COPRIME("znconreylog", x, N);
break;
case t_COL:
Ui = znstar_get_Ui(bid);
if (!RgV_is_ZV(x) || lg(x) != lg(Ui)) pari_err_TYPE("znconreylog", x);
return gerepileupto(av, vecmodii(ZM_ZC_mul(Ui,x), cycg));
case t_VEC:
return gerepilecopy(av, znconreyfromchar(bid, x));
default: pari_err_TYPE("znconreylog", x);
}
F = znstar_get_faN(bid);
P = gel(F, 1);
E = gel(F, 2);
L = gel(bid,4);
pe = znstar_get_pe(bid);
fao = gel(L,2);
gen = znstar_get_conreygen(bid);
lo = gel(L,6);
l = lg(gen); i = 1;
y = cgetg(l, t_COL);
if (!mod2(N) && !mod2(x)) pari_err_COPRIME("znconreylog", x, N);
if (absequaliu(gel(P,1), 2) && E[1] >= 2)
{
if (E[1] == 2)
gel(y,i++) = mod4(x) == 1? gen_0: gen_1;
else
{
GEN a, x2, q2 = gel(pe,1);
x2 = modii(x, q2);
if (mod4(x) == 1)
gel(y,i++) = gen_0;
else
{ gel(y,i++) = gen_1; x2 = subii(q2, x2); }
a = Zideallog_2k(x2, gel(gen,i), E[1], q2);
if (!a) pari_err_COPRIME("znconreylog", x, N);
gel(y, i++) = a;
}
}
while (i < l)
{
GEN p = gel(P,i), q = gel(pe,i), xpe = modii(x, q);
GEN a = Zideallog_pk(xpe, gel(gen,i), p, E[i], q, gel(fao,i), gel(lo,i));
if (!a) pari_err_COPRIME("znconreylog", x, N);
gel(y, i++) = a;
}
return gerepilecopy(av, y);
}
GEN
Zideallog(GEN bid, GEN x)
{
pari_sp av = avma;
GEN y = znconreylog(bid, x), U = znstar_get_U(bid);
return gerepileupto(av, ZM_ZC_mul(U, y));
}
GEN
znlog0(GEN h, GEN g, GEN o)
{
if (typ(g) == t_VEC)
{
GEN N;
if (o) pari_err_TYPE("znlog [with znstar]", o);
if (!checkznstar_i(g)) pari_err_TYPE("znlog", h);
N = znstar_get_N(g);
h = Rg_to_Fp(h,N);
return Zideallog(g, h);
}
return znlog(h, g, o);
}
GEN
znconreyexp(GEN bid, GEN x)
{
pari_sp av = avma;
long i, l;
GEN N, pe, gen, cycg, v, vmod;
int e2;
if (!checkznstar_i(bid)) pari_err_TYPE("znconreyexp", bid);
cycg = znstar_get_conreycyc(bid);
switch(typ(x))
{
case t_VEC:
x = znconreylog(bid, x);
break;
case t_COL:
if (RgV_is_ZV(x) && lg(x) == lg(cycg)) break;
default: pari_err_TYPE("znconreyexp",x);
}
pe = znstar_get_pe(bid);
gen = znstar_get_conreygen(bid);
cycg = znstar_get_conreycyc(bid);
l = lg(x); v = cgetg(l, t_VEC);
N = znstar_get_N(bid);
e2 = !mod8(N);
for (i = 1; i < l; i++)
{
GEN q, g, m;
if (i == 1 && e2) { gel(v,1) = NULL; continue; }
q = gel(pe,i);
g = gel(gen,i);
m = modii(gel(x,i), gel(cycg,i));
m = Fp_pow(g, m, q);
if (i == 2 && e2 && signe(gel(x,1))) m = Fp_neg(m, q);
gel(v,i) = mkintmod(m, q);
}
if (e2) v = vecsplice(v, 1);
v = chinese1_coprime_Z(v);
vmod = gel(v,1);
v = gel(v,2);
if (mpodd(v) || mpodd(N)) return gerepilecopy(av, v);
return gerepileuptoint(av, addii(v, vmod));
}
GEN
znconreychar(GEN bid, GEN m)
{
pari_sp av = avma;
GEN c, d, nchi;
if (!checkznstar_i(bid)) pari_err_TYPE("znconreychar", bid);
switch(typ(m))
{
case t_COL:
case t_INT:
nchi = znconrey_normalized(bid,m);
break;
default:
pari_err_TYPE("znconreychar",m);
return NULL;
}
d = gel(nchi,1);
c = ZV_ZM_mul(gel(nchi,2), znstar_get_Ui(bid));
return gerepilecopy(av, char_denormalize(znstar_get_cyc(bid),d,c));
}
GEN
znconreyconductor(GEN bid, GEN chi, GEN *pm)
{
pari_sp av = avma;
GEN q, m, F, P, E;
long i, j, l;
int e2, primitive = 1;
if (!checkznstar_i(bid)) pari_err_TYPE("znconreyconductor", bid);
if (typ(chi) == t_COL)
{
if (!znconrey_check(znstar_get_conreycyc(bid), chi))
pari_err_TYPE("znconreyconductor",chi);
}
else
chi = znconreylog(bid, chi);
l = lg(chi);
F = znstar_get_faN(bid);
P = gel(F,1);
E = gel(F,2);
if (l == 1)
{
avma = av;
if (pm) *pm = cgetg(1,t_COL);
if (lg(P) == 1) return gen_1;
retmkvec2(gen_1, trivial_fact());
}
P = leafcopy(P);
E = leafcopy(E);
m = cgetg(l, t_COL);
e2 = (E[1] >= 3 && absequaliu(gel(P,1),2));
i = j = 1;
if (e2)
{
GEN a1 = gel(chi,1), a = gel(chi,2);
i = 3;
if (!signe(a))
{
e2 = primitive = 0;
if (signe(a1))
{
E[1] = 2;
gel(m,1) = a1;
j = 2;
}
}
else
{
long v = Z_pvalrem(a, gen_2, &a);
if (v) { E[1] -= v; E[2] = E[1]; primitive = 0; }
gel(m,1) = a1;
gel(m,2) = a;
j = 3;
}
}
l = lg(P);
for (; i < l; i++)
{
GEN p = gel(P,i), a = gel(chi,i);
if (!signe(a)) primitive = 0;
else
{
long v = Z_pvalrem(a, p, &a);
E[j] = E[i]; if (v) { E[j] -= v; primitive = 0; }
gel(P,j) = gel(P,i);
gel(m,j) = a; j++;
}
}
setlg(m,j);
setlg(P,j);
setlg(E,j);
if (pm) *pm = m;
if (primitive)
{
q = znstar_get_N(bid);
if (mod4(q) == 2) primitive = 0;
}
if (!primitive)
{
if (e2)
{
P = vecsplice(P,1);
E = vecsplice(E,1);
}
E = zc_to_ZC(E);
q = mkvec2(factorback2(P,E), mkmat2(P,E));
}
gerepileall(av, pm? 2: 1, &q, pm);
return q;
}
GEN
zncharinduce(GEN G, GEN chi, GEN N)
{
pari_sp av = avma;
GEN q, faq, P, E, Pq, Eq, CHI;
long i, j, l;
int e2;
if (!checkznstar_i(G)) pari_err_TYPE("zncharinduce", G);
if (!zncharcheck(G, chi)) pari_err_TYPE("zncharinduce", chi);
q = znstar_get_N(G);
if (typ(chi) != t_COL) chi = znconreylog(G, chi);
if (checkznstar_i(N))
{
GEN faN = znstar_get_faN(N);
P = gel(faN,1); l = lg(P);
E = gel(faN,2);
N = znstar_get_N(N);
if (l > 2 && equalii(gel(P,1),gel(P,2)))
{
l--;
P = vecsplice(P,1);
E = vecsplice(E,1);
}
}
else
{
GEN faN = check_arith_pos(N, "zncharinduce");
if (!faN) faN = Z_factor(N);
else
N = (typ(N) == t_VEC)? gel(N,1): factorback(faN);
P = gel(faN,1);
E = gel(faN,2);
}
if (!dvdii(N,q)) pari_err_DOMAIN("zncharinduce", "N % q", "!=", gen_0, N);
if (mod4(N) == 2)
{
if (lg(P) > 1 && absequaliu(gel(P,1), 2))
{
P = vecsplice(P,1);
E = vecsplice(E,1);
}
N = shifti(N,-1);
}
l = lg(P);
if (cmpii(N,q) <= 0) return gerepilecopy(av, chi);
if (typ(E) != t_VECSMALL) E = ZV_to_zv(E);
e2 = (E[1] >= 3 && absequaliu(gel(P,1),2));
if (ZV_equal0(chi))
{
avma = av;
return equali1(N)? cgetg(1, t_COL): zerocol(l+e2 - 1);
}
faq = znstar_get_faN(G);
Pq = gel(faq,1);
Eq = gel(faq,2);
CHI = cgetg(l+e2, t_COL);
i = j = 1;
if (e2)
{
i = 2; j = 3;
if (absequaliu(gel(Pq,1), 2))
{
if (Eq[1] >= 3)
{
gel(CHI,1) = gel(chi,1);
gel(CHI,2) = shifti(gel(chi,2), E[1]-Eq[1]);
}
else if (Eq[1] == 2)
{
gel(CHI,1) = gel(chi,1);
gel(CHI,2) = gen_0;
}
else
gel(CHI,1) = gel(CHI,2) = gen_0;
}
else
gel(CHI,1) = gel(CHI,2) = gen_0;
}
for (; i < l; i++,j++)
{
GEN p = gel(P,i);
long k = ZV_search(Pq, p);
gel(CHI,j) = k? mulii(gel(chi,k), powiu(p, E[i]-Eq[k])): gen_0;
}
return gerepilecopy(av, CHI);
}
GEN
znconreylog_normalize(GEN G, GEN m)
{
GEN cycg = znstar_get_conreycyc(G);
long i, l;
GEN d, M = cgetg_copy(m, &l);
if (typ(cycg) != t_VEC || lg(cycg) != l)
pari_err_TYPE("znconreylog_normalize",mkvec2(m,cycg));
for (i = 1; i < l; i++) gel(M,i) = gdiv(gel(m,i), gel(cycg,i));
M = Q_remove_denom(M, &d);
return mkvec2(d? d: gen_1, M);
}
GEN
znconrey_normalized(GEN G, GEN chi)
{
switch(typ(chi))
{
case t_INT:
return znconreylog_normalize(G, znconreylog(G, chi));
case t_COL:
if (!RgV_is_ZV(chi)) break;
return znconreylog_normalize(G, chi);
case t_VEC:
if (!RgV_is_ZV(chi)) break;
return znconreyfromchar_normalized(G, chi);
}
pari_err_TYPE("znchareval",chi);
return NULL;
}
long
zncharisodd(GEN G, GEN chi)
{
long i, l, s;
GEN N;
if (!checkznstar_i(G)) pari_err_TYPE("zncharisodd", G);
if (!zncharcheck(G, chi)) pari_err_TYPE("zncharisodd", chi);
if (typ(chi) != t_COL) chi = znconreylog(G, chi);
N = znstar_get_N(G);
l = lg(chi);
s = 0;
if (!mod8(N))
{
s = mpodd(gel(chi,1));
i = 3;
}
else
i = 1;
for (; i < l; i++) s += mpodd(gel(chi,i));
return odd(s);
}
GEN
znchartokronecker(GEN G, GEN chi, long flag)
{
pari_sp av = avma;
long s;
GEN F, o;
if (flag && flag != 1) pari_err_FLAG("znchartokronecker");
s = zncharisodd(G, chi)? -1: 1;
if (typ(chi) != t_COL) chi = znconreylog(G, chi);
o = zncharorder(G, chi);
if (abscmpiu(o,2) > 0) { avma = av; return gen_0; }
F = znconreyconductor(G, chi, NULL);
if (typ(F) == t_INT)
{
if (s < 0) F = negi(F);
return gerepileuptoint(av, F);
}
F = gel(F,1);
F = (s < 0)? negi(F): icopy(F);
if (!flag)
{
GEN MF = znstar_get_faN(G), P = gel(MF,1);
long i, l = lg(P);
for (i = 1; i < l; i++)
{
GEN p = gel(P,i);
if (!dvdii(F,p)) F = mulii(F,sqri(p));
}
}
return gerepileuptoint(av, F);
}
GEN
znchar_quad(GEN G, GEN D)
{
GEN cyc = znstar_get_conreycyc(G);
GEN gen = znstar_get_conreygen(G);
long i, l = lg(cyc);
GEN chi = cgetg(l, t_COL);
for (i = 1; i < l; i++)
{
long k = kronecker(D, gel(gen,i));
gel(chi,i) = (k==1)? gen_0: shifti(gel(cyc,i), -1);
}
return chi;
}
GEN
znchar(GEN D)
{
pari_sp av = avma;
GEN G, chi;
switch(typ(D))
{
case t_INT:
if (!signe(D) || Mod4(D) > 1) pari_err_TYPE("znchar", D);
G = znstar0(D,1);
chi = mkvec2(G, znchar_quad(G,D));
break;
case t_INTMOD:
G = znstar0(gel(D,1), 1);
chi = mkvec2(G, znconreylog(G, gel(D,2)));
break;
case t_VEC:
if (checkMF_i(D)) { chi = vecslice(MF_get_CHI(D),1,2); break; }
else if (checkmf_i(D)) { chi = vecslice(mf_get_CHI(D),1,2); break; }
if (lg(D) != 3) pari_err_TYPE("znchar", D);
G = gel(D,1);
if (!checkznstar_i(G)) pari_err_TYPE("znchar", D);
chi = gel(D,2);
if (typ(chi) == t_VEC && lg(chi) == 3 && is_vec_t(typ(gel(chi,2))))
{
GEN n = gel(chi,1), chic = gel(chi,2);
GEN cyc = typ(chic)==t_VEC? znstar_get_cyc(G): znstar_get_conreycyc(G);
if (!char_check(cyc, chic)) pari_err_TYPE("znchar",D);
chi = char_denormalize(cyc, n, chic);
}
if (!zncharcheck(G, chi)) pari_err_TYPE("znchar", D);
chi = mkvec2(G,chi); break;
default:
pari_err_TYPE("znchar", D);
return NULL;
}
return gerepilecopy(av, chi);
}
GEN
znchareval(GEN G, GEN chi, GEN n, GEN z)
{
GEN nchi, N = znstar_get_N(G);
if (typ(n) == t_FRAC && !equali1(gcdii(gel(n,2), N))) return not_coprime(z);
n = Rg_to_Fp(n, N);
if (!equali1(gcdii(n, N))) return not_coprime(z);
nchi = znconrey_normalized(G, chi);
return chareval_i(nchi, znconreylog(G,n), z);
}
GEN
zncharconj(GEN G, GEN chi)
{
switch(typ(chi))
{
case t_INT: chi = znconreylog(G, chi);
case t_COL: return charconj(znstar_get_conreycyc(G), chi);
case t_VEC: return charconj(znstar_get_cyc(G), chi);
}
pari_err_TYPE("zncharconj",chi);
return NULL;
}
GEN
zncharorder(GEN G, GEN chi)
{
switch(typ(chi))
{
case t_INT: chi = znconreylog(G, chi);
case t_COL: return charorder(znstar_get_conreycyc(G), chi);
case t_VEC: return charorder(znstar_get_cyc(G), chi);
default: pari_err_TYPE("zncharorder",chi);
return NULL;
}
}
GEN
zncharker(GEN G, GEN chi)
{
if (typ(chi) != t_VEC) chi = znconreychar(G, chi);
return charker(znstar_get_cyc(G), chi);
}
GEN
zncharpow(GEN G, GEN a, GEN n)
{
switch(typ(a))
{
case t_INT: return Fp_pow(a, n, znstar_get_N(G));
case t_VEC: return charpow(znstar_get_cyc(G), a, n);
case t_COL: return charpow(znstar_get_conreycyc(G), a, n);
default: pari_err_TYPE("znchapow",a);
return NULL;
}
}
GEN
zncharmul(GEN G, GEN a, GEN b)
{
long ta = typ(a), tb = typ(b);
if (ta == tb) switch(ta)
{
case t_INT: return Fp_mul(a, b, znstar_get_N(G));
case t_VEC: return charmul(znstar_get_cyc(G), a, b);
case t_COL: return charmul(znstar_get_conreycyc(G), a, b);
default: pari_err_TYPE("zncharmul",a);
return NULL;
}
if (ta != t_COL) a = znconreylog(G, a);
if (tb != t_COL) b = znconreylog(G, b);
return charmul(znstar_get_conreycyc(G), a, b);
}
GEN
znchardiv(GEN G, GEN a, GEN b)
{
long ta = typ(a), tb = typ(b);
if (ta == tb) switch(ta)
{
case t_INT: return Fp_div(a, b, znstar_get_N(G));
case t_VEC: return chardiv(znstar_get_cyc(G), a, b);
case t_COL: return chardiv(znstar_get_conreycyc(G), a, b);
default: pari_err_TYPE("znchardiv",a);
return NULL;
}
if (ta != t_COL) a = znconreylog(G, a);
if (tb != t_COL) b = znconreylog(G, b);
return chardiv(znstar_get_conreycyc(G), a, b);
}
GEN
znchardecompose(GEN G, GEN chi, GEN Q)
{
GEN c, P, E, F;
long l, lP, i;
if (!checkznstar_i(G)) pari_err_TYPE("znchardecompose", G);
if (typ(Q) != t_INT) pari_err_TYPE("znchardecompose", Q);
if (typ(chi) == t_COL)
{ if (!zncharcheck(G, chi)) pari_err_TYPE("znchardecompose", chi); }
else
chi = znconreylog(G, chi);
l = lg(chi);
F = znstar_get_faN(G);
c = zerocol(l-1);
P = gel(F,1);
lP = lg(P);
E = gel(F,2);
for (i = 1; i < lP; i++)
{
GEN p = gel(P,i);
if (i == 1 && equaliu(p,2) && E[1] >= 3)
{
if (!mpodd(Q))
{
gel(c,1) = icopy(gel(chi,1));
gel(c,2) = icopy(gel(chi,2));
}
i = 2;
}
else
if (dvdii(Q, p)) gel(c,i) = icopy(gel(chi,i));
}
return c;
}
GEN
zncharconductor(GEN G, GEN chi)
{
pari_sp av = avma;
GEN F = znconreyconductor(G, chi, NULL);
if (typ(F) == t_INT) return F;
return gerepilecopy(av, gel(F,1));
}
GEN
znchartoprimitive(GEN G, GEN chi)
{
pari_sp av = avma;
GEN chi0, F = znconreyconductor(G, chi, &chi0);
if (typ(F) == t_INT)
chi = mkvec2(G,chi);
else
chi = mkvec2(znstar0(F,1), chi0);
return gerepilecopy(av, chi);
}