#include "dirichlet.h"
static ulong
primitive_root_p_and_p2(ulong p)
{
if (p == 40487)
return 10;
#if FLINT_BITS == 64
if (p == UWORD(6692367337))
return 7;
if (p > UWORD(10000000000000000))
flint_throw(FLINT_ERROR, "p > 10^16 not implemented in %s\n", __func__);
#endif
return n_primitive_root_prime(p);
}
static void
dirichlet_prime_group_init(dirichlet_prime_group_struct * P, ulong p, int e)
{
P->p = p;
P->e = e;
if (p == 2 || p == 4)
{
P->p = 2;
nmod_init(&P->pe, UWORD(1) << e);
if (p == 2)
{
P->e = 2;
nmod_init(&P->phi, 2);
P->g = (UWORD(1) << e) - 1;
}
else
{
nmod_init(&P->phi, UWORD(1) << (e - 2));
P->g = 5;
}
}
else
{
ulong pe1;
pe1 = n_pow(p, e - 1);
nmod_init(&P->phi, (p-1) * pe1);
nmod_init(&P->pe, p * pe1);
P->g = primitive_root_p_and_p2(p);
}
P->dlog = NULL;
}
static void
dirichlet_group_lift_generators(dirichlet_group_t G)
{
slong k;
dirichlet_prime_group_struct * P = G->P;
G->expo = G->phi_q = 1;
if (G->neven)
{
G->phi_q = G->q_even / 2;
G->expo = P[G->neven - 1].phi.n;
}
for (k = G->neven; k < G->num; k++)
{
G->phi_q *= P[k].phi.n;
G->expo *= P[k].phi.n / n_gcd(G->expo, P[k].p - 1);
}
for (k = 0; k < G->num; k++)
{
nmod_t pe;
ulong qpe, v;
G->PHI[k] = G->expo / G->P[k].phi.n;
pe = G->P[k].pe;
qpe = G->q / pe.n;
if (G->q < G->P[k].pe.n)
{
flint_printf("lift generator %wu from %wu to %wu e=%wu\n",
G->P[k].g, G->P[k].pe.n, G->q, G->P[k].e);
}
v = nmod_inv(qpe % pe.n, pe);
G->generators[k] = (1 + nmod_mul(G->P[k].g-1, v * qpe, G->mod)) % G->q;
}
}
int
dirichlet_group_init(dirichlet_group_t G, ulong q)
{
slong k;
ulong e2;
n_factor_t fac;
G->q = q;
nmod_init(&G->mod, q);
e2 = n_remove(&q, 2);
G->q_even = UWORD(1) << e2;
G->neven = (e2 >= 3) ? 2 : (e2 == 2) ? 1 : 0;
n_factor_init(&fac);
n_factor(&fac, q, 1);
#if FLINT_BITS == 64
for (k = 0; k < fac.num; k++)
if (fac.p[k] > UWORD(10000000000000000))
return 0;
#endif
G->num = fac.num + G->neven;
G->P = flint_malloc(G->num * sizeof(dirichlet_prime_group_struct));
G->generators = flint_malloc(G->num * sizeof(ulong));
G->PHI = flint_malloc(G->num * sizeof(ulong));
if (G->neven >= 1)
dirichlet_prime_group_init(&G->P[0], 2, e2);
if (G->neven == 2)
dirichlet_prime_group_init(&G->P[1], 4, e2);
G->rad_q = 1;
for (k = G->neven; k < G->num; k++)
{
ulong p, e;
p = fac.p[k - G->neven];
e = fac.exp[k - G->neven];
G->rad_q *= p;
dirichlet_prime_group_init(&G->P[k], p, e);
}
dirichlet_group_lift_generators(G);
return 1;
}
void
dirichlet_subgroup_init(dirichlet_group_t H, const dirichlet_group_t G, ulong h)
{
int s[15];
slong k, j, e2;
H->q = h;
nmod_init(&H->mod, h);
e2 = n_remove(&h, 2);
H->q_even = UWORD(1) << e2;
s[0] = s[1] = 0;
j = 0;
if (e2 >= 2)
s[j++] = 2;
if (e2 >= 3)
s[j++] = e2;
H->neven = j;
for (k = G->neven; k < G->num; k++)
{
ulong p = G->P[k].p;
s[k] = n_remove(&h, p);
if (s[k] > 0)
{
j++;
H->rad_q *= p;
}
}
H->num = j;
H->P = flint_malloc(j * sizeof(dirichlet_prime_group_struct));
H->generators = flint_malloc(j * sizeof(ulong));
H->PHI = flint_malloc(j * sizeof(ulong));
j = 0;
for (k = 0; k < H->neven; k++)
{
H->P[j] = G->P[k];
if (H->q_even < G->q_even)
{
nmod_init(&H->P[j].pe, H->q_even);
H->P[j].e = s[k];
if (k == 0)
H->P[j].g = H->q_even - 1;
else
nmod_init(&H->P[j].phi, H->q_even / 4);
}
j++;
}
for (k = G->neven; k < G->num; k++)
{
if (s[k])
{
H->P[j] = G->P[k];
if (s[k] < G->P[k].e)
{
ulong pf, p = H->P[j].p;
H->P[j].e = s[k];
pf = n_pow(p, s[k]);
nmod_init(&H->P[j].pe, pf);
nmod_init(&H->P[j].phi, (p-1) * pf / p);
}
j++;
}
}
dirichlet_group_lift_generators(H);
}