#include "fmpz.h"
#include "fmpz_mod.h"
#include "fmpz_mod_mat.h"
#include "fmpz_mod_poly.h"
#include "fq.h"
#include "fq_poly.h"
#include "fq_embed.h"
void fq_embed_mono_to_dual_matrix(fmpz_mod_mat_t res, const fq_ctx_t ctx)
{
slong i, j, n = fq_ctx_degree(ctx);
fmpz_mod_poly_t ctx_inv_rev, d_ctx;
const fmpz_mod_poly_struct *modulus = fq_ctx_modulus(ctx);
fmpz_mod_poly_init(ctx_inv_rev, ctx->ctxp);
fmpz_mod_poly_init(d_ctx, ctx->ctxp);
fq_modulus_pow_series_inv(ctx_inv_rev, ctx, 2*n);
fmpz_mod_poly_derivative(d_ctx, modulus, ctx->ctxp);
fmpz_mod_poly_reverse(d_ctx, d_ctx, n, ctx->ctxp);
fmpz_mod_poly_mullow(ctx_inv_rev, ctx_inv_rev, d_ctx, 2*n, ctx->ctxp);
fmpz_mod_mat_zero(res, ctx->ctxp);
for (i = 0; i < n; i++)
for (j = 0; j < n && i+j < ctx_inv_rev->length; j++)
fmpz_mod_mat_set_entry(res, i, j, ctx_inv_rev->coeffs + i + j, ctx->ctxp);
fmpz_mod_poly_clear(ctx_inv_rev, ctx->ctxp);
fmpz_mod_poly_clear(d_ctx, ctx->ctxp);
}
void fq_embed_dual_to_mono_matrix(fmpz_mod_mat_t res, const fq_ctx_t ctx)
{
slong i, j, n = fq_ctx_degree(ctx);
fq_t d_ctx, d_ctx_inv;
const fmpz_mod_poly_struct *modulus = fq_ctx_modulus(ctx);
fmpz_mod_mat_t mul_mat, tmp;
fq_init(d_ctx, ctx);
fq_init(d_ctx_inv, ctx);
fmpz_mod_mat_init(mul_mat, n, n, ctx->ctxp);
fmpz_mod_mat_init(tmp, n, n, ctx->ctxp);
fmpz_mod_mat_zero(tmp, ctx->ctxp);
for (i = 0; i < n; i++)
for (j = 0; j < n - i; j++)
fmpz_mod_mat_set_entry(tmp, i, j, modulus->coeffs + i + j + 1, ctx->ctxp);
fq_modulus_derivative_inv(d_ctx, d_ctx_inv, ctx);
fq_embed_mul_matrix(mul_mat, d_ctx_inv, ctx);
fmpz_mod_mat_mul(res, mul_mat, tmp, ctx->ctxp);
fq_clear(d_ctx, ctx);
fq_clear(d_ctx_inv, ctx);
fmpz_mod_mat_clear(mul_mat, ctx->ctxp);
fmpz_mod_mat_clear(tmp, ctx->ctxp);
}
void fq_embed_trace_matrix(fmpz_mod_mat_t res,
const fmpz_mod_mat_t basis,
const fq_ctx_t sub_ctx,
const fq_ctx_t sup_ctx)
{
slong m = fmpz_mod_mat_ncols(basis, sub_ctx->ctxp);
slong n = fmpz_mod_mat_nrows(basis, sub_ctx->ctxp);
fmpz_mod_mat_t m2d, d2m, tmp;
fmpz_mod_mat_init(m2d, n, n, sub_ctx->ctxp);
fmpz_mod_mat_init(d2m, m, m, sub_ctx->ctxp);
fmpz_mod_mat_init(tmp, m, n, sub_ctx->ctxp);
fq_embed_mono_to_dual_matrix(m2d, sup_ctx);
fmpz_mod_mat_transpose(res, basis, sub_ctx->ctxp);
fq_embed_dual_to_mono_matrix(d2m, sub_ctx);
fmpz_mod_mat_mul(tmp, res, m2d, sub_ctx->ctxp);
fmpz_mod_mat_mul(res, d2m, tmp, sub_ctx->ctxp);
fmpz_mod_mat_clear(m2d, sub_ctx->ctxp);
fmpz_mod_mat_clear(d2m, sub_ctx->ctxp);
fmpz_mod_mat_clear(tmp, sub_ctx->ctxp);
}
void fq_embed_matrices(fmpz_mod_mat_t embed,
fmpz_mod_mat_t project,
const fq_t gen_sub,
const fq_ctx_t sub_ctx,
const fq_t gen_sup,
const fq_ctx_t sup_ctx,
const fmpz_mod_poly_t gen_minpoly)
{
const fmpz_mod_ctx_struct * ctxp = sub_ctx->ctxp;
slong m = fq_ctx_degree(sub_ctx);
slong n = fq_ctx_degree(sup_ctx);
slong d = n / m;
fmpz_t invd;
fq_ctx_t gen_ctx;
fmpz_mod_poly_t gen_minpoly_cp;
fmpz_mod_mat_t gen2sub, sub2gen, gen2sup, sup2gen;
fmpz_mod_poly_init(gen_minpoly_cp, ctxp);
fmpz_mod_poly_set(gen_minpoly_cp, gen_minpoly, ctxp);
fmpz_init(invd);
fq_ctx_init_modulus(gen_ctx, gen_minpoly_cp, ctxp, "x");
fmpz_mod_mat_init(gen2sub, m, m, ctxp);
fmpz_mod_mat_init(sub2gen, m, m, ctxp);
fmpz_mod_mat_init(gen2sup, n, m, ctxp);
fmpz_mod_mat_init(sup2gen, m, n, ctxp);
fq_embed_composition_matrix(gen2sub, gen_sub, sub_ctx);
fq_embed_trace_matrix(sub2gen, gen2sub, gen_ctx, sub_ctx);
fq_embed_composition_matrix_sub(gen2sup, gen_sup, sup_ctx, m);
fq_embed_trace_matrix(sup2gen, gen2sup, gen_ctx, sup_ctx);
if (d == 1) {}
else if (fmpz_set_si(invd, d),
fmpz_invmod(invd, invd, fmpz_mod_ctx_modulus(ctxp)))
{
fmpz_mod_mat_scalar_mul_fmpz(sup2gen, sup2gen, invd, ctxp);
}
else
{
int i;
fq_t mul, trace;
fmpz_mod_mat_t column, tvec, mat_mul, tmp;
fq_init(mul, sup_ctx);
fq_init(trace, sup_ctx);
fmpz_mod_mat_init(tvec, n, 1, ctxp);
fmpz_mod_mat_init(mat_mul, n, n, ctxp);
fmpz_mod_mat_init(tmp, m, n, ctxp);
for (i = 1; i < n; i++)
{
if (!fmpz_is_zero(fmpz_mod_mat_entry(sup2gen, 0, i)))
break;
}
fq_gen(mul, sup_ctx);
fq_pow_ui(mul, mul, i, sup_ctx);
fmpz_mod_mat_window_init(column, sup2gen, 0, i, m, i+1, ctxp);
fmpz_mod_mat_mul(tvec, gen2sup, column, ctxp);
fq_set_fmpz_mod_mat(trace, tvec, sup_ctx);
fq_div(mul, mul, trace, sup_ctx);
fq_embed_mul_matrix(mat_mul, mul, sup_ctx);
fmpz_mod_mat_mul(tmp, sup2gen, mat_mul, ctxp);
fmpz_mod_mat_swap(tmp, sup2gen, ctxp);
fmpz_mod_mat_clear(tmp, ctxp);
fmpz_mod_mat_clear(mat_mul, ctxp);
fmpz_mod_mat_clear(tvec, ctxp);
fmpz_mod_mat_window_clear(column, ctxp);
fq_clear(mul, sup_ctx);
fq_clear(trace, sup_ctx);
}
fmpz_mod_mat_mul(embed, gen2sup, sub2gen, ctxp);
fmpz_mod_mat_mul(project, gen2sub, sup2gen, ctxp);
fmpz_mod_mat_clear(gen2sub, ctxp);
fmpz_mod_mat_clear(sub2gen, ctxp);
fmpz_mod_mat_clear(gen2sup, ctxp);
fmpz_mod_mat_clear(sup2gen, ctxp);
fq_ctx_clear(gen_ctx);
fmpz_clear(invd);
fmpz_mod_poly_clear(gen_minpoly_cp, ctxp);
}